实现本地三节点 K3s 同构环境、脱敏生产快照、Gemini 图片和多参考图视频协议模拟、统一验收报告及故障注入。\n\n新增 Worker 容量控制器、资源与连接预算、任务恢复保护,并将生产验收拆分为 validation 执行和人工 CAS 放量。\n\n验证包括 Go 全量测试、PostgreSQL HTTP 集成测试、go vet、OpenAPI、ShellCheck、前端检查、迁移及发布脚本测试。
317 lines
13 KiB
JavaScript
Executable File
317 lines
13 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
import { createHash } from 'node:crypto';
|
|
import { lstat, mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
|
|
import { dirname, resolve } from 'node:path';
|
|
|
|
const shaPattern = /^[0-9a-f]{40}$/;
|
|
const digestPattern = /^sha256:[0-9a-f]{64}$/;
|
|
const hashPattern = /^[0-9a-f]{64}$/;
|
|
const unsafeKeyPattern = /(password|secret|token|credential|authorization|api.?key|access.?key|private.?key|connection.?string|database.?url|proxy)/i;
|
|
|
|
function parseArgs(argv) {
|
|
const command = argv.shift();
|
|
const values = {};
|
|
while (argv.length > 0) {
|
|
const key = argv.shift();
|
|
if (!key?.startsWith('--') || argv.length === 0) throw new Error('invalid arguments');
|
|
values[key.slice(2)] = argv.shift();
|
|
}
|
|
return { command, values };
|
|
}
|
|
|
|
async function regularJSON(path) {
|
|
const absolute = resolve(path);
|
|
const info = await lstat(absolute);
|
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error(`${path} must be a regular non-symlink file`);
|
|
return JSON.parse(await readFile(absolute, 'utf8'));
|
|
}
|
|
|
|
function assertSecretSafe(value, path = '$') {
|
|
if (Array.isArray(value)) {
|
|
value.forEach((item, index) => assertSecretSafe(item, `${path}[${index}]`));
|
|
return;
|
|
}
|
|
if (value && typeof value === 'object') {
|
|
for (const [key, nested] of Object.entries(value)) {
|
|
if (key !== 'secretSafe' && unsafeKeyPattern.test(key)) {
|
|
throw new Error(`unsafe report key at ${path}.${key}`);
|
|
}
|
|
assertSecretSafe(nested, `${path}.${key}`);
|
|
}
|
|
return;
|
|
}
|
|
if (typeof value === 'string' && /(?:postgres(?:ql)?|https?):\/\/[^\s]+/i.test(value)) {
|
|
throw new Error(`URL-like value is forbidden at ${path}`);
|
|
}
|
|
}
|
|
|
|
function validate(report) {
|
|
if (report.schemaVersion !== 'acceptance-report/v1' || report.secretSafe !== true) {
|
|
throw new Error('unsupported or non-secret-safe acceptance report');
|
|
}
|
|
if (!report.runId || !shaPattern.test(report.release?.sourceSha ?? '') ||
|
|
!digestPattern.test(report.release?.apiImageDigest ?? '') ||
|
|
!digestPattern.test(report.release?.workerImageDigest ?? '') ||
|
|
!shaPattern.test(report.snapshot?.sourceReleaseSha ?? '') ||
|
|
!hashPattern.test(report.snapshot?.configHash ?? '') ||
|
|
!hashPattern.test(report.snapshot?.sha256 ?? '')) {
|
|
throw new Error('acceptance report identity fields are invalid');
|
|
}
|
|
if (!report.stages || !Array.isArray(report.gates) ||
|
|
report.promotion?.requiresManualConfirmation !== true ||
|
|
!['validation', 'live', 'not-applicable'].includes(report.promotion?.trafficMode)) {
|
|
throw new Error('acceptance report stages, gates, or promotion are invalid');
|
|
}
|
|
for (const [name, stage] of Object.entries(report.stages)) {
|
|
if (!['passed', 'failed', 'not-run'].includes(stage?.status)) {
|
|
throw new Error(`invalid stage status for ${name}`);
|
|
}
|
|
}
|
|
assertSecretSafe(report);
|
|
}
|
|
|
|
async function loadReports(directory) {
|
|
const entries = (await readdir(resolve(directory), { withFileTypes: true }))
|
|
.filter((entry) => entry.isFile() && entry.name.endsWith('.json'))
|
|
.map((entry) => entry.name)
|
|
.sort();
|
|
const reports = [];
|
|
for (const name of entries) {
|
|
const report = await regularJSON(resolve(directory, name));
|
|
if (report.schemaVersion === 'acceptance-load-report/v1') {
|
|
if (report.secretSafe !== true) throw new Error(`load report ${name} is not secret-safe`);
|
|
reports.push({
|
|
file: name,
|
|
profile: report.profile,
|
|
passed: report.passed,
|
|
phases: report.phases,
|
|
startedAt: report.startedAt,
|
|
finishedAt: report.finishedAt
|
|
});
|
|
}
|
|
}
|
|
return reports;
|
|
}
|
|
|
|
async function buildLocal(values) {
|
|
for (const key of ['runtime', 'snapshot', 'reports', 'artifact', 'output']) {
|
|
if (!values[key]) throw new Error(`--${key} is required`);
|
|
}
|
|
const runtime = await regularJSON(values.runtime);
|
|
const snapshot = await regularJSON(values.snapshot);
|
|
const artifact = await regularJSON(values.artifact);
|
|
const loads = await loadReports(values.reports);
|
|
if (runtime.schemaVersion !== 'acceptance-runtime/v1' ||
|
|
snapshot.schemaVersion !== 'acceptance-snapshot/v1' ||
|
|
artifact.schemaVersion !== 'acceptance-artifact-smoke/v1') {
|
|
throw new Error('local report inputs have incompatible schema versions');
|
|
}
|
|
if (runtime.releaseSha !== artifact.releaseSha ||
|
|
artifact.apiImageDigest !== values['api-digest'] ||
|
|
runtime.snapshotConfigHash !== snapshot.source.configHash ||
|
|
runtime.snapshotSha256 !== snapshot.snapshotSha256) {
|
|
throw new Error('local report CAS identity mismatch');
|
|
}
|
|
const localPassed = loads.length > 0 && loads.every((item) => item.passed === true);
|
|
const gates = [
|
|
{ id: 'local_load_reports', passed: localPassed, detail: `${loads.length} load reports` },
|
|
{ id: 'amd64_artifact_smoke', passed: artifact.passed === true }
|
|
];
|
|
const report = {
|
|
schemaVersion: 'acceptance-report/v1',
|
|
runId: runtime.runId,
|
|
release: {
|
|
sourceSha: artifact.releaseSha,
|
|
apiImageDigest: artifact.apiImageDigest,
|
|
workerImageDigest: artifact.apiImageDigest
|
|
},
|
|
snapshot: {
|
|
sourceReleaseSha: snapshot.source.releaseSha,
|
|
configHash: snapshot.source.configHash,
|
|
sha256: snapshot.snapshotSha256
|
|
},
|
|
stages: {
|
|
localNative: { status: localPassed ? 'passed' : 'failed', loadReports: loads },
|
|
amd64Artifact: { status: artifact.passed ? 'passed' : 'failed', ...artifact },
|
|
onlineSimulation: { status: 'not-run' },
|
|
realCanary: { status: 'not-run' }
|
|
},
|
|
certifiedProfile: values['certified-profile'] ? {
|
|
name: values['certified-profile'],
|
|
source: 'local-candidate-only'
|
|
} : null,
|
|
gates,
|
|
promotion: {
|
|
ready: false,
|
|
requiresManualConfirmation: true,
|
|
trafficMode: 'not-applicable'
|
|
},
|
|
createdAt: new Date().toISOString(),
|
|
secretSafe: true
|
|
};
|
|
validate(report);
|
|
await mkdir(dirname(resolve(values.output)), { recursive: true, mode: 0o700 });
|
|
await writeFile(resolve(values.output), `${JSON.stringify(report, null, 2)}\n`, { mode: 0o600 });
|
|
process.stdout.write(`acceptance_report_local=PASS sha256=${createHash('sha256').update(JSON.stringify(report)).digest('hex')}\n`);
|
|
}
|
|
|
|
async function buildLocalPartial(values) {
|
|
for (const key of ['runtime', 'snapshot', 'reports', 'output', 'failure-phase']) {
|
|
if (!values[key]) throw new Error(`--${key} is required`);
|
|
}
|
|
const runtime = await regularJSON(values.runtime);
|
|
const snapshot = await regularJSON(values.snapshot);
|
|
const loads = await loadReports(values.reports);
|
|
if (runtime.schemaVersion !== 'acceptance-runtime/v1' ||
|
|
snapshot.schemaVersion !== 'acceptance-snapshot/v1' ||
|
|
!shaPattern.test(runtime.releaseSha ?? '') ||
|
|
!digestPattern.test(runtime.apiImageDigest ?? '') ||
|
|
!digestPattern.test(runtime.workerImageDigest ?? '') ||
|
|
runtime.snapshotConfigHash !== snapshot.source.configHash ||
|
|
runtime.snapshotSha256 !== snapshot.snapshotSha256) {
|
|
throw new Error('partial local report inputs have incompatible CAS identity');
|
|
}
|
|
const completedLoadsPassed = loads.every((item) => item.passed === true);
|
|
const report = {
|
|
schemaVersion: 'acceptance-report/v1',
|
|
runId: runtime.runId,
|
|
release: {
|
|
sourceSha: runtime.releaseSha,
|
|
apiImageDigest: runtime.apiImageDigest,
|
|
workerImageDigest: runtime.workerImageDigest
|
|
},
|
|
snapshot: {
|
|
sourceReleaseSha: snapshot.source.releaseSha,
|
|
configHash: snapshot.source.configHash,
|
|
sha256: snapshot.snapshotSha256
|
|
},
|
|
stages: {
|
|
localNative: {
|
|
status: 'failed',
|
|
failurePhase: values['failure-phase'],
|
|
completedLoadReportsPassed: completedLoadsPassed,
|
|
loadReports: loads
|
|
},
|
|
amd64Artifact: { status: 'not-run' },
|
|
onlineSimulation: { status: 'not-run' },
|
|
realCanary: { status: 'not-run' }
|
|
},
|
|
certifiedProfile: values['certified-profile'] ? {
|
|
name: values['certified-profile'],
|
|
source: 'last-local-stable-candidate'
|
|
} : null,
|
|
gates: [
|
|
{
|
|
id: 'local_execution_complete',
|
|
passed: false,
|
|
detail: `stopped during ${values['failure-phase']}`
|
|
},
|
|
{
|
|
id: 'completed_load_reports',
|
|
passed: completedLoadsPassed,
|
|
detail: `${loads.length} load reports`
|
|
}
|
|
],
|
|
promotion: {
|
|
ready: false,
|
|
requiresManualConfirmation: true,
|
|
trafficMode: 'not-applicable'
|
|
},
|
|
createdAt: new Date().toISOString(),
|
|
secretSafe: true
|
|
};
|
|
validate(report);
|
|
await mkdir(dirname(resolve(values.output)), { recursive: true, mode: 0o700 });
|
|
await writeFile(resolve(values.output), `${JSON.stringify(report, null, 2)}\n`, { mode: 0o600 });
|
|
process.stdout.write('acceptance_report_local_partial=PASS\n');
|
|
}
|
|
|
|
async function mergeProduction(values) {
|
|
for (const key of ['local-report', 'production-summary', 'output']) {
|
|
if (!values[key]) throw new Error(`--${key} is required`);
|
|
}
|
|
const local = await regularJSON(values['local-report']);
|
|
const production = await regularJSON(values['production-summary']);
|
|
validate(local);
|
|
if (local.release.sourceSha !== production.releaseSha ||
|
|
local.release.apiImageDigest !== production.apiImageDigest ||
|
|
local.snapshot.configHash !== production.snapshotConfigHash ||
|
|
local.snapshot.sha256 !== production.snapshotSha256) {
|
|
throw new Error('production summary does not match local acceptance CAS fields');
|
|
}
|
|
local.stages.onlineSimulation = {
|
|
status: production.passed === true ? 'passed' : 'failed',
|
|
stableCapacityProfile: production.stableCapacityProfile,
|
|
tasks: production.tasks,
|
|
geminiTasks: production.geminiTasks,
|
|
videoTasks: production.videoTasks
|
|
};
|
|
local.stages.realCanary = {
|
|
status: production.realCanaryPassed === true ? 'passed' : 'failed'
|
|
};
|
|
local.certifiedProfile = production.certifiedProfile ?? local.certifiedProfile;
|
|
local.gates.push(...(production.gates ?? []));
|
|
local.promotion = {
|
|
ready: production.passed === true && production.realCanaryPassed === true &&
|
|
local.gates.every((gate) => gate.passed === true),
|
|
requiresManualConfirmation: true,
|
|
trafficMode: 'validation'
|
|
};
|
|
local.createdAt = new Date().toISOString();
|
|
validate(local);
|
|
await mkdir(dirname(resolve(values.output)), { recursive: true, mode: 0o700 });
|
|
await writeFile(resolve(values.output), `${JSON.stringify(local, null, 2)}\n`, { mode: 0o600 });
|
|
process.stdout.write(`acceptance_report_production=PASS promotion_ready=${local.promotion.ready}\n`);
|
|
}
|
|
|
|
const { command, values } = parseArgs(process.argv.slice(2));
|
|
if (command === 'validate') {
|
|
const report = await regularJSON(values.input ?? '');
|
|
validate(report);
|
|
process.stdout.write('acceptance_report_validate=PASS\n');
|
|
} else if (command === 'build-local') {
|
|
await buildLocal(values);
|
|
} else if (command === 'build-local-partial') {
|
|
await buildLocalPartial(values);
|
|
} else if (command === 'merge-production') {
|
|
await mergeProduction(values);
|
|
} else if (command === 'mark-promoted') {
|
|
const input = values.input ?? '';
|
|
const output = values.output ?? input;
|
|
const report = await regularJSON(input);
|
|
validate(report);
|
|
if (report.promotion.ready !== true || report.promotion.trafficMode !== 'validation') {
|
|
throw new Error('only a ready validation report can be marked promoted');
|
|
}
|
|
report.promotion.trafficMode = 'live';
|
|
report.promotion.promotedAt = new Date().toISOString();
|
|
await writeFile(resolve(output), `${JSON.stringify(report, null, 2)}\n`, { mode: 0o600 });
|
|
process.stdout.write('acceptance_report_promoted=PASS\n');
|
|
} else if (command === 'attach-monitor') {
|
|
const input = values.input ?? '';
|
|
const output = values.output ?? input;
|
|
const report = await regularJSON(input);
|
|
const monitor = await regularJSON(values.monitor ?? '');
|
|
validate(report);
|
|
if (monitor.schemaVersion !== 'acceptance-monitor-report/v1' ||
|
|
monitor.runId !== report.runId || monitor.releaseSha !== report.release.sourceSha ||
|
|
monitor.secretSafe !== true) {
|
|
throw new Error('monitor report does not match overall acceptance report');
|
|
}
|
|
report.stages.postPromotionMonitor = {
|
|
status: monitor.passed === true ? 'passed' : 'failed',
|
|
startedAt: monitor.startedAt,
|
|
finishedAt: monitor.finishedAt,
|
|
samples: monitor.samples,
|
|
failureGateId: monitor.failureGateId
|
|
};
|
|
if (monitor.passed !== true) {
|
|
report.promotion.ready = false;
|
|
report.promotion.trafficMode = 'validation';
|
|
}
|
|
await writeFile(resolve(output), `${JSON.stringify(report, null, 2)}\n`, { mode: 0o600 });
|
|
process.stdout.write(`acceptance_report_monitor=PASS monitor_passed=${monitor.passed === true}\n`);
|
|
} else {
|
|
throw new Error('usage: report.mjs {validate|build-local|build-local-partial|merge-production|mark-promoted|attach-monitor} [options]');
|
|
}
|