From e261d4130dcd882a443767483d2df5be15adbb0f Mon Sep 17 00:00:00 2001 From: whyour Date: Sun, 23 Aug 2026 20:46:05 +0800 Subject: [PATCH] fix(ci): bind plugin recovery completion evidence --- ...gin-package-recovery-e2e-live-contract.cjs | 35 +++++++++++++------ ...PluginPackageRecoveryE2ELiveAudit.test.cjs | 28 +++++++++++++++ 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/scripts/ql3-plugin-package-recovery-e2e-live-contract.cjs b/scripts/ql3-plugin-package-recovery-e2e-live-contract.cjs index 54eef227..887d2ecb 100644 --- a/scripts/ql3-plugin-package-recovery-e2e-live-contract.cjs +++ b/scripts/ql3-plugin-package-recovery-e2e-live-contract.cjs @@ -1647,6 +1647,24 @@ function jobImageId(name) { return pods.items[0].status.containerStatuses[0].imageID; } +function buildOrderingEvidence( + migrated, + initialRecovered, + rejectedUpgrade, + runtime, +) { + return Object.freeze({ + migrationJobUid: migrated.metadata.uid, + migrationCompletedAt: migrated.status.completionTime, + initialRecoveryJobUid: initialRecovered.metadata.uid, + initialRecoveryCompletedAt: initialRecovered.status.completionTime, + upgradeRecoveryJobUid: rejectedUpgrade.metadata.uid, + upgradeRecoveryCompletedAt: runtime.recoveryCompletedAt, + runtimeCreatedAt: runtime.creationTimestamp, + runtimeBoundRecoveryJobUid: runtime.recoveryJobUid, + }); +} + function diagnostics() { if (!kubeconfig || !fs.existsSync(kubeconfig)) return; const snapshot = kubectl( @@ -1939,16 +1957,12 @@ async function main(argv = process.argv.slice(2)) { upgradeRecoveryImageId, postgresImageId: postgresPod.status.containerStatuses[0].imageID, }), - ordering: Object.freeze({ - migrationJobUid: migrated.metadata.uid, - migrationCompletedAt: migrated.status.completionTime, - initialRecoveryJobUid: initialRecovered.metadata.uid, - initialRecoveryCompletedAt: initialRecovered.status.completionTime, - upgradeRecoveryJobUid: rejectedUpgrade.metadata.uid, - upgradeRecoveryCompletedAt: rejectedUpgrade.status.completionTime, - runtimeCreatedAt: runtime.creationTimestamp, - runtimeBoundRecoveryJobUid: runtime.recoveryJobUid, - }), + ordering: buildOrderingEvidence( + migrated, + initialRecovered, + rejectedUpgrade, + runtime, + ), failedUpgrade: Object.freeze({ recoveryJobUid: rejectedUpgrade.metadata.uid, rejectionReason: database.upgradeFailureReason, @@ -2024,6 +2038,7 @@ if (require.main === module) { module.exports = { REPORT_SCHEMA, + buildOrderingEvidence, privateReportPath, writePrivateReport, }; diff --git a/test/back/ql3PluginPackageRecoveryE2ELiveAudit.test.cjs b/test/back/ql3PluginPackageRecoveryE2ELiveAudit.test.cjs index 3c100a41..f20d1711 100644 --- a/test/back/ql3PluginPackageRecoveryE2ELiveAudit.test.cjs +++ b/test/back/ql3PluginPackageRecoveryE2ELiveAudit.test.cjs @@ -20,6 +20,7 @@ const { } = require('../../scripts/ql3-plugin-package-recovery-e2e-live-audit.cjs'); const { writePrivateReport, + buildOrderingEvidence, } = require('../../scripts/ql3-plugin-package-recovery-e2e-live-contract.cjs'); function validReport() { @@ -130,6 +131,33 @@ test('offline audit accepts one exact low-sensitive recovery report', () => { assert.deepEqual(result.findings, []); }); +test('producer binds ordering to the recovery completion timestamp carried into runtime', () => { + const ordering = buildOrderingEvidence( + { + metadata: { uid: 'migration-job' }, + status: { completionTime: '2026-08-14T08:00:01.000Z' }, + }, + { + metadata: { uid: 'initial-recovery-job' }, + status: { completionTime: '2026-08-14T08:00:02.000Z' }, + }, + { + metadata: { uid: 'upgrade-recovery-job' }, + status: { completionTime: '2026-08-14T08:00:03.000Z' }, + }, + { + creationTimestamp: '2026-08-14T08:00:05.000Z', + recoveryJobUid: 'upgrade-recovery-job', + recoveryCompletedAt: '2026-08-14T08:00:04.000Z', + }, + ); + assert.equal( + ordering.upgradeRecoveryCompletedAt, + '2026-08-14T08:00:04.000Z', + ); + assert.equal(ordering.runtimeBoundRecoveryJobUid, 'upgrade-recovery-job'); +}); + test('offline audit rejects broken upgrade, ordering and image relationships', () => { const report = validReport(); report.database.upgradeRevisionCount = 1;