#!/usr/bin/env node /** * Build Docker images for C-----code * * Workflow: * 1. cargo build --release --target x86_64-unknown-linux-gnu (compile Rust binaries) * 2. pnpm build (build frontend SPA, outputs to dist/) * 3. docker build (copy pre-built artifacts into minimal runtime images) * * Usage: * node scripts/build.js # Build all images * node scripts/build.js app # Build specific service * * Environment: * REGISTRY - Docker registry (default: harbor.gitdata.me/gta_team) * TAG - Image tag (default: latest) * TARGET - Rust build target (default: x86_64-unknown-linux-gnu) */ 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 FRONTEND_SERVICE = 'frontend'; const ALL_SERVICES = [...RUST_SERVICES, FRONTEND_SERVICE]; const args = process.argv.slice(2); const targets = args.length > 0 ? args : ALL_SERVICES; const rustTargets = targets.filter(s => RUST_SERVICES.includes(s)); const needsFrontend = targets.includes(FRONTEND_SERVICE); const rootDir = path.join(__dirname, '..'); console.log(`\n=== Build Configuration ===`); console.log(`Registry: ${REGISTRY}`); console.log(`Tag: ${TAG}`); console.log(`Target: ${BUILD_TARGET}`); console.log(`Services: ${targets.join(', ')}\n`); // Step 1: Build Rust binaries if (rustTargets.length > 0) { console.log(`\n==> Step 1: Building Rust binaries`); const cpus = require('os').cpus().length; try { execSync( `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: rootDir } ); console.log(` [OK] Rust binaries built`); } catch (error) { console.error(` [FAIL] Rust build failed`); process.exit(1); } } // Step 2: Build frontend (frontend source is at repo root) if (needsFrontend) { console.log(`\n==> Step 2: Building frontend`); if (!fs.existsSync(path.join(rootDir, 'vite.config.ts'))) { console.error(`\n [FAIL] vite.config.ts not found`); process.exit(1); } try { execSync( `corepack enable && corepack prepare pnpm@10 --activate && pnpm install --frozen-lockfile && pnpm build`, { stdio: 'inherit', cwd: rootDir } ); 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)) { console.error(`Unknown service: ${service}`); console.error(`Available: ${ALL_SERVICES.join(', ')}`); process.exit(1); } const dockerfile = path.join(__dirname, '..', 'docker', `${service}.Dockerfile`); const image = `${REGISTRY}/${service}:${TAG}`; console.log(` Building ${image}`); try { execSync( `docker build -f "${dockerfile}" -t "${image}" .`, { stdio: 'inherit', cwd: rootDir } ); console.log(` [OK] ${image}`); } catch (error) { console.error(` [FAIL] ${service}`); process.exit(1); } } console.log(`\n=== Build Complete ===`); for (const service of targets) { console.log(` ${REGISTRY}/${service}:${TAG}`); } console.log('');