feat(ci): reverse back-merge promotion (prod→preprod→master)#3382
Merged
Conversation
📊 Code Quality Score: 20/100
Was this score accurate? 👍 Yes · 👎 No Scored by GitVelocity · How are scores calculated? |
2772342 to
81938f5
Compare
After a hotfix lands directly on prod, the fix must propagate back down the chain so environments don't diverge. This adds the reverse paths: - promotion-gate: allow prod → preprod (reverse back-merge). preprod → master stays ungated (master is not in the gate's branch list; gating it would block every feature → master PR). - manual-promotion: add prod source / master target, switch validation to a 4-pair allowlist (master→preprod, preprod→prod, prod→preprod, preprod→master), and add a chain-backmerge job that auto-opens the preprod → master PR when a prod → preprod PR merges. The chained PR is created with BYPASS_BRANCH_PROTECTION_TOKEN so its required checks run (a GITHUB_TOKEN-created PR would not trigger them). - generate-promotion-pr.sh: map master → staging env for the rollback-ref lookup so reverse PRs don't warn 'unknown'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add 'head.repo.full_name == github.repository' so a fork PR from a branch coincidentally named 'prod' can't trigger the PAT-backed back-merge leg. - Document the required fine-grained PAT scope for BYPASS_BRANCH_PROTECTION_TOKEN. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v1.27.0 runs on the deprecated Node 20 runtime. v3.0.3 runs on Node 24. The v2+ webhook API replaced the SLACK_WEBHOOK_URL env var with the 'webhook' + 'webhook-type: incoming-webhook' inputs; the payload blocks are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
f46f6c7 to
fa513f9
Compare
…n direct -> prod Hotfixes should ship via hotfix/* -> preprod -> prod (the same gate as everything else). hotfix/* -> prod stays allowed as a break-glass path for true emergencies, but the gate now prints a warning and a reminder to run the reverse back-merge afterwards. Documents the policy in the workflow header. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📊 Code Quality Score: 22/100
Was this score accurate? 👍 Yes · 👎 No Scored by GitVelocity · How are scores calculated? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
After a hotfix lands directly on
prod(viahotfix/* → prod, outside the normal twice-weeklymaster → preprod → prodcadence), that fix must propagate back down the chain so the environments don't diverge. There was no first-class way to doprod → preprodthenpreprod → master, and the promotion gate falsely failed theprod → preprodPRs that are already being merged by hand.What
promotion-gate.yaml—preprodnow acceptsprod(reverse back-merge), alongsidemaster/hotfix/*.preprod → masterstays ungated (master isn't in the gate's branch list; gating it would block everyfeature → masterPR).manual-promotion.yaml— addsprodsource +mastertarget; validation is now a 4-pair allowlist (master→preprod,preprod→prod,prod→preprod,preprod→master); newchain-backmerge-to-masterjob auto-opens thepreprod → masterPR when aprod → preprodPR merges. The chained PR is created withBYPASS_BRANCH_PROTECTION_TOKENso its required checks (PyTest / Playwright / Sandbox Deploy) actually run — aGITHUB_TOKEN-created PR would not trigger them.generate-promotion-pr.sh— mapsmaster → stagingenv for the rollback-ref lookup so reverse PRs don't warn "unknown".Rollback-reference fix (
app=unknown, chart=unknown)This branch also fixes the long-standing bug where the auto-generated Rollback Reference table rendered
app=unknown, chart=unknown(e.g. promotion PR #3447). Supersedes #3446, which targeted the same bug.Root cause: the version extraction used
python3 -c "..."double-quoted inside$(...), so bash collapsed the[\"\'']quote class to["\'']within the program text. Python then raisedSyntaxError: closing parenthesis ']' does not match— which2>&1swallowed and|| ="unknown"turned intounknown. Both fields failed identically, hence both showedunknown. (Reproduced against the exact prod helmrelease that was live when #3447 was generated.)Fix: the Python is now written via a single-quoted heredoc (
'PYEOF') so bash performs no substitution on the regex, and the YAML is passed through an env var rather than the command line. Verified: it now extracts the real values (app=v6.101.14-prod.3, chart=0.85.12-prod.1for the #3447 snapshot).Additional hardening folded in:
refs/heads/<b>:refs/remotes/origin/<b>) soorigin/<target>is reliably the true remote tip for both the changelog and the rollback lookup.GITHUB_OUTPUTheredoc delimiter forai_summaryso a model-generated summary containing a bareEOFline can't inject extra output keys (Actions output injection).SOURCE→TARGETpair viagh pr list --base --head --jq '.[0].url // empty'instead of a baregh pr viewthat returned the runner's current-branch PR.chain-backmerge-to-master(head.repo.full_name == github.repository) so a fork PR from a branch coincidentally namedprodcan't trigger the PAT-backed leg.BYPASS_BRANCH_PROTECTION_TOKEN(Contents: Read + Pull requests: Write, this repo only).Flow
Trigger
prod → preprodfrom the Actions tab → review/merge → thepreprod → masterPR opens automatically with checks running → review/merge → environments back in sync.Reviewer notes / caveats
BYPASS_BRANCH_PROTECTION_TOKENto be able to create PRs (fine-grained PAT withpull-requests: write,contents: read). It's currently proven only forgit pushindeploy-static.yaml. Please confirm scope.pull_requesttriggers from the base branch's copy of the workflow, so the chain job is dormant until this is promoted topreprodnormally. Recommend one deliberate dry-run after it reaches preprod.prod → preprodPR (action-created or hand-made) — the correct invariant: prod commits preprod lacked must reach master.🤖 Generated with Claude Code