From 65a34a627f81d0d10e69ca9a21ced5491500edf5 Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Wed, 15 Apr 2026 13:00:34 +0800 Subject: [PATCH] refactor: build frontend externally, copy dist into nginx image - frontend.Dockerfile: runtime-only, COPY apps/frontend/dist instead of building in Docker - build.js: add frontend build step (pnpm build) before docker build - drone.yml: remove obsolete frontend-deps/frontend-build steps --- .drone.yml | 12 +-------- docker/frontend.Dockerfile | 27 +++---------------- scripts/build.js | 53 +++++++++++++++++++++++++------------- 3 files changed, 40 insertions(+), 52 deletions(-) diff --git a/.drone.yml b/.drone.yml index 5675043..f4cce33 100644 --- a/.drone.yml +++ b/.drone.yml @@ -28,16 +28,6 @@ steps: git checkout ${DRONE_TAG} fi - - name: frontend-deps - image: node:22-alpine - commands: - - cd apps/frontend && corepack enable && corepack prepare pnpm@10 --activate && pnpm install --frozen-lockfile - - - name: frontend-build - image: node:22-alpine - commands: - - cd apps/frontend && pnpm build - - name: cargo-build image: rust:1.94-bookworm commands: @@ -80,7 +70,7 @@ steps: /kaniko/executor --context . --dockerfile docker/frontend.Dockerfile \ --destination ${REGISTRY}/frontend:${TAG} --destination ${REGISTRY}/frontend:latest echo "==> All images pushed" - depends_on: [ cargo-build, frontend-build ] + depends_on: [ cargo-build ] - name: prepare-kubeconfig image: alpine:latest diff --git a/docker/frontend.Dockerfile b/docker/frontend.Dockerfile index 4b4b6ca..c663141 100644 --- a/docker/frontend.Dockerfile +++ b/docker/frontend.Dockerfile @@ -1,26 +1,8 @@ -# ---- Stage 1: Build ---- -FROM node:22-alpine AS builder +# Runtime only — frontend built externally via pnpm +FROM nginx:alpine -WORKDIR /app - -# Copy package files -COPY package.json pnpm-lock.yaml ./ -RUN corepack enable && corepack prepare pnpm@10 --activate - -# Install dependencies -RUN pnpm install --frozen-lockfile - -# Copy source -COPY . . - -# Build -RUN pnpm build - -# ---- Stage 2: Serve with nginx ---- -FROM nginx:alpine AS runtime - -# Copy built assets -COPY --from=builder /app/dist /usr/share/nginx/html +# Copy pre-built SPA assets +COPY apps/frontend/dist /usr/share/nginx/html # nginx configuration for SPA RUN echo 'server { \ @@ -46,5 +28,4 @@ RUN echo 'server { \ }' > /etc/nginx/conf.d/default.conf EXPOSE 80 - ENTRYPOINT ["nginx", "-g", "daemon off;"] diff --git a/scripts/build.js b/scripts/build.js index 969d458..5d8bd50 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -4,7 +4,8 @@ * * Workflow: * 1. cargo build --release --target x86_64-unknown-linux-gnu (compile Rust binaries) - * 2. docker build (copy pre-built binaries into minimal runtime images) + * 2. pnpm build (build frontend SPA) + * 3. docker build (copy pre-built artifacts into minimal runtime images) * * Usage: * node scripts/build.js # Build all images @@ -18,19 +19,21 @@ const { execSync } = require('child_process'); const path = require('path'); +const fs = require('fs'); const REGISTRY = process.env.REGISTRY || 'harbor.gitdata.me/gta_team'; const TAG = process.env.TAG || new Date().toISOString().slice(0, 13).replace('T', '-'); const BUILD_TARGET = process.env.TARGET || 'x86_64-unknown-linux-gnu'; const RUST_SERVICES = ['app', 'gitserver', 'email-worker', 'git-hook', 'operator', 'static']; -const ALL_SERVICES = [...RUST_SERVICES, 'frontend']; +const FRONTEND_SERVICE = 'frontend'; +const ALL_SERVICES = [...RUST_SERVICES, FRONTEND_SERVICE]; const args = process.argv.slice(2); const targets = args.length > 0 ? args : ALL_SERVICES; -// Determine which rust services are in targets const rustTargets = targets.filter(s => RUST_SERVICES.includes(s)); +const needsFrontend = targets.includes(FRONTEND_SERVICE); console.log(`\n=== Build Configuration ===`); console.log(`Registry: ${REGISTRY}`); @@ -42,21 +45,15 @@ console.log(`Services: ${targets.join(', ')}\n`); if (rustTargets.length > 0) { console.log(`\n==> Step 1: Building Rust binaries`); const cpus = require('os').cpus().length; - const packages = rustTargets.map(s => { - const pkgMap = { - 'app': 'app', - 'gitserver': 'gitserver', - 'email-worker': 'email-server', - 'git-hook': 'git-hook', - 'operator': 'operator', - 'static': 'static-server', - }; - return `--package ${pkgMap[s]}`; - }).join(' '); - try { execSync( - `cargo build --release --target ${BUILD_TARGET} ${packages} -j ${cpus}`, + `cargo build --release --target ${BUILD_TARGET} ${rustTargets.map(s => { + const pkgMap = { + 'app': 'app', 'gitserver': 'gitserver', 'email-worker': 'email-server', + 'git-hook': 'git-hook', 'operator': 'operator', 'static': 'static-server', + }; + return `--package ${pkgMap[s]}`; + }).join(' ')} -j ${cpus}`, { stdio: 'inherit', cwd: path.join(__dirname, '..') } ); console.log(` [OK] Rust binaries built`); @@ -66,8 +63,28 @@ if (rustTargets.length > 0) { } } -// Step 2: Build Docker images -console.log(`\n==> Step 2: Building Docker images`); +// Step 2: Build frontend (requires apps/frontend to exist locally) +if (needsFrontend) { + const frontendDir = path.join(__dirname, '..', 'apps', 'frontend'); + if (!fs.existsSync(frontendDir)) { + console.error(`\n [FAIL] apps/frontend not found. Please ensure frontend source exists.`); + process.exit(1); + } + console.log(`\n==> Step 2: Building frontend`); + try { + execSync(`corepack enable && corepack prepare pnpm@10 --activate && pnpm install --frozen-lockfile && pnpm build`, { + stdio: 'inherit', + cwd: frontendDir, + }); + console.log(` [OK] Frontend built`); + } catch (error) { + console.error(` [FAIL] Frontend build failed`); + process.exit(1); + } +} + +// Step 3: Build Docker images +console.log(`\n==> Step 3: Building Docker images`); for (const service of targets) { if (!ALL_SERVICES.includes(service)) {