#!/usr/bin/env node import { spawnSync } from 'node:child_process'; import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync, } from 'node:fs'; import { tmpdir } from 'node:os'; import path from 'node:path'; const BUILD_SCRIPT = 'scripts/build-production-release.sh'; const failures = []; const tmpRoot = mkdtempSync( path.join(tmpdir(), 'genarrative-pingora-production-release-build-'), ); try { main(); } finally { rmSync(tmpRoot, { recursive: true, force: true }); } if (failures.length > 0) { console.error('[check:pingora-production-release-build] FAILED'); for (const failure of failures) { console.error(`- ${failure}`); } process.exit(1); } console.log('[check:pingora-production-release-build] OK'); function main() { const fixture = prepareFixture(); const result = runBuild(fixture); assertStatus(result, 0, 'Pingora production release 真实构建烟测应成功。'); if (result.status !== 0) { return; } const releaseDir = path.join(process.cwd(), 'build', fixture.version); try { assertFileExists( path.join(releaseDir, 'api-server'), '发布包必须包含 api-server 占位 release binary。', ); assertFileExists( path.join(releaseDir, 'api-server.sha256'), '发布包必须包含 api-server checksum。', ); assertFileExists( path.join(releaseDir, 'pingora-gateway'), '真实 include Pingora 时发布包必须包含 pingora-gateway。', ); assertExecutable( path.join(releaseDir, 'pingora-gateway'), 'pingora-gateway 必须保留可执行权限。', ); assertFileExists( path.join(releaseDir, 'pingora-gateway.sha256'), '真实 include Pingora 时发布包必须包含 pingora-gateway checksum。', ); assertFileExists( path.join(releaseDir, 'scripts/check-pingora-direct-live.mjs'), '真实 include Pingora 的发布包仍必须携带 direct live smoke 脚本。', ); assertFileExists( path.join(releaseDir, 'scripts/ops/pingora-current-release-audit.mjs'), '真实 include Pingora 的发布包仍必须携带 current release 自审脚本。', ); assertFileExists( path.join(releaseDir, 'deploy/pingora/nginx-route-parity.matrix.json'), '真实 include Pingora 的发布包仍必须携带 Nginx/Pingora 路由矩阵。', ); const manifest = readJson(path.join(releaseDir, 'release-manifest.json')); if (manifest.component_type !== 'api-server') { failures.push( `release manifest component_type 应为 api-server,实际 ${manifest.component_type}`, ); } if (!manifest.artifacts?.some((item) => item.path === 'api-server')) { failures.push('release manifest 必须登记 api-server artifact。'); } if (!manifest.artifacts?.some((item) => item.path === 'pingora-gateway')) { failures.push( '真实 include Pingora 时 release manifest 必须登记 pingora-gateway artifact。', ); } } finally { rmSync(releaseDir, { recursive: true, force: true }); } } function prepareFixture() { const cargoTargetDir = path.join(tmpRoot, 'cargo-target'); const binaryDir = path.join( cargoTargetDir, 'x86_64-unknown-linux-gnu/release', ); const version = `check-pingora-production-release-build-${process.pid}-${Date.now()}`; mkdirSync(binaryDir, { recursive: true }); const apiBinary = path.join(binaryDir, 'api-server'); writeFileSync(apiBinary, '#!/usr/bin/env bash\nexit 0\n', 'utf8'); spawnSync('chmod', ['0755', apiBinary], { encoding: 'utf8' }); return { cargoTargetDir, version }; } function runBuild(fixture) { return spawnSync( 'bash', [ BUILD_SCRIPT, '--component', 'api-server', '--name', fixture.version, '--skip-api-build', '--include-pingora-gateway', ], { cwd: process.cwd(), encoding: 'utf8', env: { ...process.env, CARGO_TARGET_DIR: fixture.cargoTargetDir, PATH: `${path.join(process.env.HOME || '', '.local', 'bin')}:${process.env.PATH || ''}`, SOURCE_BRANCH: 'test-branch', SOURCE_COMMIT: 'test-commit', }, }, ); } function readJson(filePath) { try { return JSON.parse(readFileSync(filePath, 'utf8')); } catch (error) { failures.push(`${filePath} 不是合法 JSON: ${error.message}`); return {}; } } function assertFileExists(filePath, reason) { if (!existsSync(filePath)) { failures.push(`${reason} 缺少: ${filePath}`); } } function assertExecutable(filePath, reason) { if (!existsSync(filePath)) { return; } if ((statSync(filePath).mode & 0o111) === 0) { failures.push(`${reason} 文件不可执行: ${filePath}`); } } function assertStatus(result, expected, reason) { const actual = result.status ?? 0; if (actual !== expected) { failures.push( `${reason} 预期退出码 ${expected},实际 ${actual}。\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`, ); } }