diff --git a/.github/workflows/deploy-strategy-switch-console.yml b/.github/workflows/deploy-strategy-switch-console.yml index a146e601..de09b758 100644 --- a/.github/workflows/deploy-strategy-switch-console.yml +++ b/.github/workflows/deploy-strategy-switch-console.yml @@ -1042,6 +1042,23 @@ jobs: fi # Current reviewed merged main release: keep all receiver checks and live configuration. # The earlier reviewed main remains an exact historical source in the next block. + if [ "$RELEASE_SOURCE_SHA" = "dd63b3c8b73b588ce411316d904efd26f2c4f382" ]; then + [ "$GITHUB_REF" = "refs/heads/main" ] && [ "$frontend_mode" = true ] \ + && [ "$SYNC_SECRETS" = false ] && [ "$SYNC_STRATEGY_PROFILES" = false ] \ + && [ "$SKIP_BINANCE_RECEIVER_CHECKS" = false ] || exit 2 + control_sha="$(git rev-parse HEAD)" + [ "$control_sha" = "$GITHUB_SHA" ] || exit 2 + git cat-file -e "${RELEASE_SOURCE_SHA}^{commit}" + git merge-base --is-ancestor "$RELEASE_SOURCE_SHA" "$control_sha" + expected_tree="c08d5542b032abaa008e882dd97ed2c38b3ad236" + [ "$(git rev-parse "${RELEASE_SOURCE_SHA}^{tree}")" = "$expected_tree" ] || exit 2 + [ "$(git rev-parse "${RELEASE_SOURCE_SHA}:web/strategy-switch-console/v2_asset_map.js")" = "a7237657bb6e48c57e947875f629d28bafb5ea27" ] || exit 2 + git -c advice.detachedHead=false checkout --detach "$RELEASE_SOURCE_SHA" + [ "$(git rev-parse HEAD)" = "$RELEASE_SOURCE_SHA" ] \ + && [ "$(git rev-parse 'HEAD^{tree}')" = "$expected_tree" ] || exit 2 + exit 0 + fi + # Historical reviewed merged main release: same receiver and configuration gates. if [ "$RELEASE_SOURCE_SHA" = "084ba4281cfff3304cf226ce511a2fc0a6b67b08" ]; then [ "$GITHUB_REF" = "refs/heads/main" ] && [ "$frontend_mode" = true ] \ && [ "$SYNC_SECRETS" = false ] && [ "$SYNC_STRATEGY_PROFILES" = false ] \ @@ -1162,12 +1179,16 @@ jobs: if: inputs.release_frontend_assets && inputs.source_sha != '20dad166be317dd0d57e492ed06e56dfb82b7aef' && !inputs.render_and_compare_only run: | set -euo pipefail - reviewed_main="084ba4281cfff3304cf226ce511a2fc0a6b67b08" - historical_reviewed_main="c1b737d6e721eb9fe71a12c613fbb49d3fa51563" + reviewed_main="dd63b3c8b73b588ce411316d904efd26f2c4f382" + historical_reviewed_main="084ba4281cfff3304cf226ce511a2fc0a6b67b08" + older_reviewed_main="c1b737d6e721eb9fe71a12c613fbb49d3fa51563" head_sha="$(git rev-parse HEAD)" - if [ "$head_sha" = "$reviewed_main" ] || [ "$head_sha" = "$historical_reviewed_main" ]; then + if [ "$head_sha" = "$reviewed_main" ] || [ "$head_sha" = "$historical_reviewed_main" ] || [ "$head_sha" = "$older_reviewed_main" ]; then [ "$GITHUB_REF" = "refs/heads/main" ] || exit 2 if [ "$head_sha" = "$reviewed_main" ]; then + [ "$(git rev-parse 'HEAD^{tree}')" = "c08d5542b032abaa008e882dd97ed2c38b3ad236" ] || exit 2 + [ "$(git hash-object web/strategy-switch-console/v2_asset_map.js)" = "a7237657bb6e48c57e947875f629d28bafb5ea27" ] || exit 2 + elif [ "$head_sha" = "$historical_reviewed_main" ]; then [ "$(git rev-parse 'HEAD^{tree}')" = "23f94567c2e8b631e8ef035bfea497acef795123" ] || exit 2 [ "$(git hash-object web/strategy-switch-console/v2_asset_map.js)" = "5287f259185ed44f3784aa6374cb00412b3c6238" ] || exit 2 else @@ -1654,8 +1675,9 @@ jobs: const statusCandidate = process.env.RELEASE_SOURCE_SHA === '68e3d4b69e706e2100f860c473ec657e9baf05ba'; const cpuCandidate = process.env.RELEASE_SOURCE_SHA === '974354b98ebe0cd55931baa03034d887d4fac560'; const integrated = controlRuntimeCandidate || controlAdminCandidate || historyCandidate || declarationCandidate || returnsCandidate || settingsCandidate || dailyCandidate || statusCandidate || cpuCandidate || process.env.RELEASE_SOURCE_SHA === '0dd2c0b3e73e890bc2c39ab0bae0eeb48c58470a'; - const currentReviewedMain = process.env.RELEASE_SOURCE_SHA === '084ba4281cfff3304cf226ce511a2fc0a6b67b08'; + const currentReviewedMain = process.env.RELEASE_SOURCE_SHA === 'dd63b3c8b73b588ce411316d904efd26f2c4f382'; const reviewedMain = currentReviewedMain + || process.env.RELEASE_SOURCE_SHA === '084ba4281cfff3304cf226ce511a2fc0a6b67b08' || process.env.RELEASE_SOURCE_SHA === 'c1b737d6e721eb9fe71a12c613fbb49d3fa51563'; const betaRelease = ibkrDaily || integrated || reviewedMain; const flowMode = process.env.EXTERNAL_FLOW_ENABLE === 'true'; @@ -1874,7 +1896,12 @@ jobs: form.set('worker.js', new File([bundle], 'worker.js', { type: 'application/javascript+module' })); const betaBody = betaRelease ? JSON.stringify({ ...metadata, modules: [{name:'worker.js', content_type:'application/javascript+module', content_base64:bundle.toString('base64')}] }) : null; let previousLatest = prior; - const separateWitness = currentReviewedMain && resume === 'off' && !flowMode; + // The reviewed main releases record the newest upload as a concurrency + // witness; keep that behaviour for the previous reviewed main too, so a + // pinned release does not silently change how it inherits configuration. + const witnessReviewedMain = currentReviewedMain + || process.env.RELEASE_SOURCE_SHA === '084ba4281cfff3304cf226ce511a2fc0a6b67b08'; + const separateWitness = witnessReviewedMain && resume === 'off' && !flowMode; if (pr764 || betaRelease) { // The legacy path inherits latest. IBKR and other beta releases // inherit the active version and require that version to be the diff --git a/tests/console_deploy_safety_validation.mjs b/tests/console_deploy_safety_validation.mjs index 2b772abf..6f343e54 100644 --- a/tests/console_deploy_safety_validation.mjs +++ b/tests/console_deploy_safety_validation.mjs @@ -296,7 +296,8 @@ for (const delta of [{FAKE_CONTROL_HEAD:"b".repeat(40)}, {FAKE_OBJECT_EXIT:"1"}, } assert.equal(sourceCase({...mergedEnv,FAKE_SELECTED_HEAD:"b".repeat(40)}, false).selected, true); -// The current reviewed main is the merged decision-inbox source. The previous pin stays valid. +// The current reviewed main is the merged desk-pages source. The previous pins stay valid. +const deskSource = "dd63b3c8b73b588ce411316d904efd26f2c4f382"; const inboxSource = "084ba4281cfff3304cf226ce511a2fc0a6b67b08"; const inboxParent = "395ddd561a16d38ae0d145e5a7e9bb7b60c44ae6"; const inboxTree = "23f94567c2e8b631e8ef035bfea497acef795123"; @@ -758,7 +759,7 @@ async function releaseCase({pr764=false,ibkrDaily=false,activeOverride=null,inhe async function integratedReleaseCase({ reviewedMain=false, reviewedMainSource="c1b737d6e721eb9fe71a12c613fbb49d3fa51563", lineage=null, settings=false, daily=false, statusCandidate=false, cpu=false, active=null, latest=null, moduleContent=syntheticBundle.toString("base64"), uploadedTag=undefined, postTag=runtime.migration_tag, flow=false, oldFlag=null, drift=false, baselineItems=undefined, preUploadVersion=null, preDeployWitness=null, uploadId=null, ambiguousActive=false, expectFailure=null, testHandlers=null, deployed=false } = {}, expected=true) { const calls=[], stdout=[], stderr=[], prior=settings?"7ffe9227-686a-48ad-baa9-72665969d918":daily?"14f0c9c2-09f9-40e9-bfbe-17165f499495":statusCandidate?"175ba768-484a-4fe6-9cea-e5a101bc1618":cpu?"90a0c8e9-b5ad-447a-998f-5d585ec28063":"600ec84d-a804-4ede-8d1d-0a34819ce46a"; active ??= prior; latest ??= active; - const separateWitness = reviewedMain && reviewedMainSource === inboxSource && !flow; + const separateWitness = reviewedMain && (reviewedMainSource === deskSource || reviewedMainSource === inboxSource) && !flow; const responses=responseSequence(false,active); if (ambiguousActive) responses[0].result.deployments[0].versions.push({version_id:"00000000-0000-4000-8000-000000000024",percentage:0}); responses.splice(3,0,wrap({items:baselineItems===undefined?[{id:latest}]:baselineItems})); @@ -833,6 +834,7 @@ const orphanWitness="00000000-0000-4000-8000-000000000021"; const driftedWitness="00000000-0000-4000-8000-000000000022"; const driftedActive="00000000-0000-4000-8000-000000000023"; const stopped={retry:false,deploy_attempted:false}; +await integratedReleaseCase({reviewedMain:true,reviewedMainSource:deskSource}); await integratedReleaseCase({reviewedMain:true,reviewedMainSource:inboxSource}); await integratedReleaseCase({reviewedMain:true,reviewedMainSource:inboxSource,latest:orphanWitness}); const baselineStop={...stopped,stage:"inherit_baseline",checkpoint:"inherit_baseline_mismatch",upload_attempted:false,outcome:"NOT_ATTEMPTED"};