fix(acceptance): 支持显式跳过本地验收
在用户明确授权时,将本地原生与 amd64 制品阶段记录为 skipped/waived,并直接使用当前生产脱敏快照进入线上模拟。修复线上报告合并未写入生产 Run ID 导致后续 promote CAS 必然失败的问题。已通过报告回归测试、bash -n、ShellCheck 和 diff 检查。
This commit is contained in:
@@ -86,13 +86,66 @@ function validate(report) {
|
||||
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)) {
|
||||
if (!['passed', 'failed', 'not-run', 'skipped'].includes(stage?.status)) {
|
||||
throw new Error(`invalid stage status for ${name}`);
|
||||
}
|
||||
}
|
||||
assertSecretSafe(report);
|
||||
}
|
||||
|
||||
async function buildOnlineWaiver(values) {
|
||||
for (const key of ['release-sha', 'api-digest', 'snapshot', 'waiver-reason', 'output']) {
|
||||
if (!values[key]) throw new Error(`--${key} is required`);
|
||||
}
|
||||
const snapshot = await regularJSON(values.snapshot);
|
||||
if (!shaPattern.test(values['release-sha']) || !digestPattern.test(values['api-digest']) ||
|
||||
snapshot.schemaVersion !== 'acceptance-snapshot/v1' ||
|
||||
!shaPattern.test(snapshot.source?.releaseSha ?? '') ||
|
||||
!hashPattern.test(snapshot.source?.configHash ?? '') ||
|
||||
!hashPattern.test(snapshot.snapshotSha256 ?? '') ||
|
||||
!/^[a-z0-9_]{3,80}$/.test(values['waiver-reason'])) {
|
||||
throw new Error('online waiver identity fields or reason are invalid');
|
||||
}
|
||||
const report = {
|
||||
schemaVersion: 'acceptance-report/v1',
|
||||
runId: `online-waiver-${values['release-sha'].slice(0, 12)}`,
|
||||
release: {
|
||||
sourceSha: values['release-sha'],
|
||||
apiImageDigest: values['api-digest'],
|
||||
workerImageDigest: values['api-digest']
|
||||
},
|
||||
snapshot: {
|
||||
sourceReleaseSha: snapshot.source.releaseSha,
|
||||
configHash: snapshot.source.configHash,
|
||||
sha256: snapshot.snapshotSha256
|
||||
},
|
||||
stages: {
|
||||
localNative: { status: 'skipped', waiverReason: values['waiver-reason'] },
|
||||
amd64Artifact: { status: 'skipped', waiverReason: values['waiver-reason'] },
|
||||
onlineSimulation: { status: 'not-run' },
|
||||
realCanary: { status: 'not-run' }
|
||||
},
|
||||
certifiedProfile: null,
|
||||
gates: [
|
||||
{
|
||||
id: 'local_acceptance_explicitly_waived',
|
||||
passed: true,
|
||||
detail: values['waiver-reason']
|
||||
}
|
||||
],
|
||||
promotion: {
|
||||
ready: false,
|
||||
requiresManualConfirmation: true,
|
||||
trafficMode: 'not-applicable'
|
||||
},
|
||||
createdAt: new Date().toISOString(),
|
||||
secretSafe: true
|
||||
};
|
||||
validate(report);
|
||||
await writeNewJSON(values.output, report);
|
||||
process.stdout.write('acceptance_report_online_waiver=PASS\n');
|
||||
}
|
||||
|
||||
async function loadReports(directory) {
|
||||
const entries = (await readdir(resolve(directory), { withFileTypes: true }))
|
||||
.filter((entry) => entry.isFile() && entry.name.endsWith('.json'))
|
||||
@@ -260,12 +313,13 @@ async function mergeProduction(values) {
|
||||
const local = await regularJSON(values['local-report']);
|
||||
const production = await regularJSON(values['production-summary']);
|
||||
validate(local);
|
||||
if (local.release.sourceSha !== production.releaseSha ||
|
||||
if (!production.runId || 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.runId = production.runId;
|
||||
local.stages.onlineSimulation = {
|
||||
status: production.passed === true ? 'passed' : 'failed',
|
||||
stableCapacityProfile: production.stableCapacityProfile,
|
||||
@@ -299,6 +353,8 @@ if (command === 'validate') {
|
||||
await buildLocal(values);
|
||||
} else if (command === 'build-local-partial') {
|
||||
await buildLocalPartial(values);
|
||||
} else if (command === 'build-online-waiver') {
|
||||
await buildOnlineWaiver(values);
|
||||
} else if (command === 'merge-production') {
|
||||
await mergeProduction(values);
|
||||
} else if (command === 'mark-promoted') {
|
||||
@@ -338,5 +394,5 @@ if (command === 'validate') {
|
||||
await replaceJSONAtomically(input, output, report);
|
||||
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]');
|
||||
throw new Error('usage: report.mjs {validate|build-local|build-local-partial|build-online-waiver|merge-production|mark-promoted|attach-monitor} [options]');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user