Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/enforce-stage-only.yml
Original file line number Diff line number Diff line change
@@ -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'."
33 changes: 33 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Contributing

## Branching & release workflow

This repo publishes the **public install scripts** (served at `install.controltheory.com` via GitHub Pages). The `main` branch is what the public gets — do **not** commit to it directly.

```
feature branch ──► stage ──► (PR) ──► main
(your work) (test) (public release)
```

| Branch | Role | Where it publishes (`deploy.yml`) |
|--------|------|-----------------------------------|
| `stage` | Integration & testing | served under `/stage/` on the Pages site |
| `main` | Public release | served at the site **root** (what users `curl`) |

> Heads-up: `deploy.yml` builds the site from **both** branches. Pushing to `main` changes the public root immediately; changes you only put on `stage` show up under `/stage/`, **not** at the root. (This is the gotcha that bit us — test on `stage`, release via `main`.)

### How to land a change

1. Branch off `stage`, do your work, open a PR **into `stage`**.
2. Merge to `stage` and test against the `/stage/` scripts.
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`.
Loading
Loading