From d825e3d80c4a5e510d458a96a1e376daace6a5bf Mon Sep 17 00:00:00 2001 From: jjbrunton Date: Mon, 31 Aug 2026 18:03:34 +0100 Subject: [PATCH] ci(branch-policy): allow hotfix and release branches into main The policy accepted only `staging` as a head for main, so any change production needed before the next promotion had nowhere to go: the PR was unmergeable by CI and branch protection blocks direct pushes. Accept `hotfix/*` and `release/*` heads as well. The naming requirement keeps a direct-to-main change deliberate and visible, and the master freeze plus the "features target staging" rule are unchanged. Document the escape hatch and the need to land the same commit on staging. Co-Authored-By: Claude Opus 5 --- .github/workflows/verify.yml | 11 ++++++++--- docs/operations/backend-deployments.md | 12 ++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index f870058..3fa551a 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -24,9 +24,14 @@ jobs: echo "master is frozen; target staging or main" >&2 exit 1 fi - if [ "$BASE_BRANCH" = "main" ] && [ "$HEAD_BRANCH" != "staging" ]; then - echo "Only staging may be promoted to main" >&2 - exit 1 + if [ "$BASE_BRANCH" = "main" ]; then + case "$HEAD_BRANCH" in + staging|hotfix/*|release/*) ;; + *) + echo "main takes staging promotions, or a hotfix/* or release/* branch for direct work" >&2 + exit 1 + ;; + esac fi if [ "$BASE_BRANCH" != "staging" ] && [ "$BASE_BRANCH" != "main" ]; then echo "Feature pull requests target staging; promotions target main" >&2 diff --git a/docs/operations/backend-deployments.md b/docs/operations/backend-deployments.md index 2ccb482..dab7dbb 100644 --- a/docs/operations/backend-deployments.md +++ b/docs/operations/backend-deployments.md @@ -14,10 +14,14 @@ not deploy the retired Supabase data plane. | `master` | frozen legacy default | none | none | Normal feature pull requests target `staging`. After CI and authenticated staging -acceptance pass, open a promotion pull request from `staging` to `main`. Do not -add production-only commits to `main`: production should run the exact commit -already exercised in staging. Direct pushes to either deployment branch are for -explicit incident recovery only. +acceptance pass, open a promotion pull request from `staging` to `main`. Prefer +that route: production then runs the exact commit already exercised in staging. + +When production needs a change that cannot wait for the next promotion, open a +pull request into `main` from a `hotfix/*` or `release/*` branch. CI enforces +that naming so a direct-to-main change is always deliberate and visible. Land +the same commit on `staging` too, or the next promotion will revert it. Direct +pushes to either deployment branch remain blocked by branch protection. Each Dokploy Compose application is connected to `jjbrunton/Sauci`, watches its single branch with push-triggered automatic deployment, and uses