Files
Genarrative/scripts/check-pingora-production-release-build.mjs
T
kdletters a7711d2dc3 引入Pingora独立网关正式化链路
新增 pingora-gateway 独立二进制 crate,覆盖路由、静态资源、压缩、接流保护、TLS 直连和访问日志能力。

新增 Nginx canary、realpath canary、direct preflight、direct live、direct enable 和 rollback 脚本。

新增 Pingora 切流证据包、命令证据、manifest 验真、根目录总审计和 release readiness 聚合门禁。

完善 API release、Jenkins、systemd、health patrol、生产部署和发布包自包含校验。

更新 Pingora 试点文档、Nginx README 与 Hermes 共享记忆。
2026-06-17 20:16:51 +08:00

174 lines
4.9 KiB
JavaScript

#!/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}`,
);
}
}