From 360bed3084f1a4483341c5215d7fd03000f3af6a Mon Sep 17 00:00:00 2001 From: Robert B Gordon Date: Mon, 29 Jun 2026 18:59:43 -0500 Subject: [PATCH] Add stage-only merge guard and contributing guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a CI workflow (enforce-stage-only.yml) that fails any PR into `main` whose source branch isn't `stage`, intended as a required status check via branch protection. Add CONTRIBUTING.md documenting the feature → stage → main release workflow, branch roles, and the enforcement rules protecting the public `main` branch. --- .github/workflows/enforce-stage-only.yml | 18 ++++++++++++++ CONTRIBUTING.md | 31 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 .github/workflows/enforce-stage-only.yml create mode 100644 CONTRIBUTING.md diff --git a/.github/workflows/enforce-stage-only.yml b/.github/workflows/enforce-stage-only.yml new file mode 100644 index 0000000..cbd381f --- /dev/null +++ b/.github/workflows/enforce-stage-only.yml @@ -0,0 +1,18 @@ +name: Enforce stage-only merges to main +# Guards the publish branch: the ONLY branch allowed to open a PR into `main` +# is `stage`. Make `source-branch-guard` a required status check on `main` +# (already configured via branch protection) so this cannot be bypassed. +on: + pull_request: + branches: [main] +jobs: + source-branch-guard: + runs-on: ubuntu-latest + steps: + - name: Require PR source to be stage + run: | + if [ "${{ github.head_ref }}" != "stage" ]; then + echo "::error::PRs into 'main' must come from 'stage' (got '${{ github.head_ref }}'). Land your change on 'stage' first, then merge stage -> main." + exit 1 + fi + echo "OK: source branch is 'stage'." diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f4255d4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing + +## Branching & release workflow + +This repo publishes **publicly**. The `main` branch is the public release branch — do **not** commit to it directly. + +``` +feature branch ──► stage ──► (PR) ──► main + (your work) (test) (public release) +``` + +| Branch | Role | What publishing does | +|--------|------|----------------------| +| `stage` | Integration & testing | `release.yml` publishes charts as `-stage.N` | +| `main` | Public release | `release.yml` publishes the real chart versions | + +### How to land a change + +1. Branch off `stage`, do your work, open a PR **into `stage`**. +2. Merge to `stage`. The release workflow publishes a `-stage.N` chart you can test against. +3. When it's good, open a PR **from `stage` into `main`** and merge it. That is the *only* way to release publicly. + +### Enforcement (so we can't forget) + +`main` is protected: + +- **Direct pushes are blocked** — everything goes through a PR. +- **Only `stage` may be merged into `main`.** A required status check (`source-branch-guard`, see `.github/workflows/enforce-stage-only.yml`) fails any PR into `main` whose source branch isn't `stage`. +- Force-pushes and branch deletion are blocked, and the rules apply to admins too. + +If you find yourself wanting to push to `main` directly: don't. Land it on `stage`, then merge `stage` → `main`.