This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
.git
|
||||
.gitea
|
||||
.idea
|
||||
.next
|
||||
.agents
|
||||
.claude
|
||||
.hermes
|
||||
node_modules
|
||||
.env*
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
AGENTS.md
|
||||
CLAUDE.md
|
||||
README.md
|
||||
@@ -0,0 +1,117 @@
|
||||
name: central-platform-frontend
|
||||
run-name: "${{ gitea.event.head_commit.message }}"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: deploy-central-platform-frontend
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
IMAGE_NAME: central-platform-frontend
|
||||
HOST_PORT: 22786
|
||||
# 浏览器访问的后端公网地址,会内联进前端代码;在仓库 Variables 中配置 API_BASE_URL
|
||||
NEXT_PUBLIC_API_BASE_URL: ${{ vars.API_BASE_URL }}
|
||||
# Next.js 服务端访问同机后端,容器使用宿主机网络,127.0.0.1 即宿主机
|
||||
INTERNAL_API_BASE_URL: http://127.0.0.1:22788
|
||||
DOCKER_BUILDKIT: "0"
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on:
|
||||
- vps_jp
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Prepare environment
|
||||
run: |
|
||||
STEP_START=$(date +%s)
|
||||
|
||||
if ! command -v docker > /dev/null 2>&1; then
|
||||
apk add docker-cli
|
||||
fi
|
||||
if ! command -v git > /dev/null 2>&1; then
|
||||
apk add git
|
||||
fi
|
||||
if ! command -v node > /dev/null 2>&1; then
|
||||
apk add nodejs
|
||||
fi
|
||||
|
||||
docker --version
|
||||
echo "Environment preparation took $(( $(date +%s) - STEP_START ))s"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check build config
|
||||
run: |
|
||||
if [ -z "$NEXT_PUBLIC_API_BASE_URL" ]; then
|
||||
echo "Repository variable API_BASE_URL is empty, set the public backend URL"
|
||||
exit 1
|
||||
fi
|
||||
echo "API base URL: $NEXT_PUBLIC_API_BASE_URL"
|
||||
|
||||
- name: Build application image
|
||||
run: |
|
||||
STEP_START=$(date +%s)
|
||||
SHORT_SHA=$(echo "${{ gitea.sha }}" | cut -c1-8)
|
||||
docker build \
|
||||
--build-arg NEXT_PUBLIC_API_BASE_URL="$NEXT_PUBLIC_API_BASE_URL" \
|
||||
--label "git.sha=${{ gitea.sha }}" \
|
||||
-t $IMAGE_NAME:latest \
|
||||
-t $IMAGE_NAME:$SHORT_SHA .
|
||||
docker images $IMAGE_NAME:latest --format "Image size: {{.Size}}"
|
||||
echo "Application image step took $(( $(date +%s) - STEP_START ))s"
|
||||
|
||||
- name: Deploy container
|
||||
run: |
|
||||
STEP_START=$(date +%s)
|
||||
|
||||
if docker ps -a --filter "name=^${IMAGE_NAME}$" -q | grep -q .; then
|
||||
docker stop --timeout 15 $IMAGE_NAME || true
|
||||
docker rm $IMAGE_NAME || true
|
||||
fi
|
||||
|
||||
docker run -d \
|
||||
--name $IMAGE_NAME \
|
||||
--network host \
|
||||
-e PORT=$HOST_PORT \
|
||||
-e HOSTNAME=127.0.0.1 \
|
||||
-e INTERNAL_API_BASE_URL=$INTERNAL_API_BASE_URL \
|
||||
-e TZ=Asia/Shanghai \
|
||||
--restart unless-stopped \
|
||||
$IMAGE_NAME:latest
|
||||
|
||||
MAX_WAIT=60
|
||||
WAITED=0
|
||||
HEALTH="unknown"
|
||||
while [ $WAITED -lt $MAX_WAIT ]; do
|
||||
RUNNING_ID=$(docker ps --filter "name=^${IMAGE_NAME}$" --filter "status=running" -q)
|
||||
HEALTH=$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' $IMAGE_NAME 2>/dev/null || echo "unknown")
|
||||
if [ -n "$RUNNING_ID" ] && [ "$HEALTH" = "healthy" ]; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
WAITED=$((WAITED + 2))
|
||||
done
|
||||
|
||||
if [ "$HEALTH" = "healthy" ]; then
|
||||
docker ps --filter "name=^${IMAGE_NAME}$" --format "Status: {{.Status}}"
|
||||
echo "Startup wait: ${WAITED}s"
|
||||
else
|
||||
echo "Container not healthy after ${MAX_WAIT}s (health=$HEALTH)"
|
||||
docker inspect $IMAGE_NAME --format 'ExitCode={{.State.ExitCode}} Error={{.State.Error}} StartedAt={{.State.StartedAt}} FinishedAt={{.State.FinishedAt}}' 2>/dev/null || true
|
||||
docker logs --tail 30 $IMAGE_NAME || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Deployment step took $(( $(date +%s) - STEP_START ))s"
|
||||
|
||||
- name: Clean up old images
|
||||
run: docker image prune -f
|
||||
|
||||
- name: Show startup logs
|
||||
if: always()
|
||||
run: docker logs --tail 15 $IMAGE_NAME
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
FROM node:22-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN npm install -g pnpm@10
|
||||
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# NEXT_PUBLIC_* 会在构建时内联进浏览器代码,必须通过构建参数传入
|
||||
ARG NEXT_PUBLIC_API_BASE_URL
|
||||
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL \
|
||||
NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
COPY . .
|
||||
RUN pnpm build
|
||||
|
||||
FROM node:22-alpine
|
||||
|
||||
ENV NODE_ENV=production \
|
||||
NEXT_TELEMETRY_DISABLED=1 \
|
||||
HOSTNAME=127.0.0.1 \
|
||||
PORT=22786
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder --chown=node:node /app/.next/standalone ./
|
||||
COPY --from=builder --chown=node:node /app/.next/static ./.next/static
|
||||
COPY --from=builder --chown=node:node /app/public ./public
|
||||
|
||||
USER node
|
||||
|
||||
EXPOSE 22786
|
||||
|
||||
HEALTHCHECK --interval=10s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD wget -q --spider http://127.0.0.1:22786/ || exit 1
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
// 生成可独立运行的 server.js,供 Docker 镜像使用
|
||||
output: "standalone"
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
Reference in New Issue
Block a user