|
| 1 | +# Branch Protection & Branching Strategy |
| 2 | + |
| 3 | +This repository follows a strict **three-tier branching strategy**. Working |
| 4 | +branches always flow into `dev`, and `dev` is the **only** branch allowed to |
| 5 | +merge into `main`. The GitHub Actions workflows in this repo enforce parts of |
| 6 | +this automatically; the remaining enforcement is done via GitHub branch |
| 7 | +protection rules (configured once in the repository settings). |
| 8 | + |
| 9 | +``` |
| 10 | +working branch ── PR ──► dev ── PR ──► main ── release.yml ──► GitHub Release |
| 11 | + (feature-*, │ |
| 12 | + fix-*, docs-*, │ |
| 13 | + refactor-*, chore-*) └── wiki-sync.yml ──► Wiki |
| 14 | +``` |
| 15 | + |
| 16 | +## Working branches |
| 17 | + |
| 18 | +Naming convention (enforced by `auto-pr-dev.yml`): |
| 19 | + |
| 20 | +| Prefix | Used for | Auto labels | |
| 21 | +|--------------|--------------------------------|-------------------------| |
| 22 | +| `feature-*` | New features / enhancements | `feature`, `enhancement`| |
| 23 | +| `fix-*` | Bug fixes | `bug`, `fix` | |
| 24 | +| `docs-*` | Documentation only | `documentation` | |
| 25 | +| `refactor-*` | Refactors (no behavior change) | `refactoring` | |
| 26 | +| `chore-*` | Maintenance, CI, tooling | `chore` | |
| 27 | + |
| 28 | +When linked to an issue, use `{prefix}-{issue-number}-{slug}` |
| 29 | +(e.g. `feature-42-trending-keywords`). The `branch-lifecycle.yml` workflow |
| 30 | +creates these branches automatically when an issue is labelled `ready`. |
| 31 | + |
| 32 | +## Workflow map |
| 33 | + |
| 34 | +| Workflow | Trigger | Purpose | |
| 35 | +|---------------------------|--------------------------------------------------|----------------------------------------| |
| 36 | +| `auto-pr-dev.yml` | Push to `feature-*`/`fix-*`/`docs-*`/`refactor-*`/`chore-*` | Auto-create PR into `dev` | |
| 37 | +| `auto-pr-main.yml` | Manual (`workflow_dispatch`) | Create release PR from `dev` to `main` | |
| 38 | +| `branch-lifecycle.yml` | Issue labelled `ready`, PR sync/close | Create branch / rebase / delete | |
| 39 | +| `python-quality.yml` | PR + push to `dev`/`main` (python paths) | Ruff lint/format + syntax matrix | |
| 40 | +| `security.yml` | PR + push to `dev`/`main` + weekly cron | CodeQL, TruffleHog, dependency audit | |
| 41 | +| `claude-code-review.yml` | PR to `dev`/`main` | Size labels + AI review on dev PRs | |
| 42 | +| `claude-main-check.yml` | PR to `main` | Enforce `dev`-only source + final AI gate | |
| 43 | +| `claude.yml` | `@claude` mention | On-demand assistant | |
| 44 | +| `wiki-sync.yml` | Push to `main` (docs paths) | Mirror docs to GitHub Wiki | |
| 45 | +| `release.yml` | Push to `main` | Build skill ZIP + tag + GitHub Release | |
| 46 | +| `labels.yml` | Push to `main`/`dev` touching label manifest | Sync `.github/labels.yml` | |
| 47 | + |
| 48 | +## Required branch protection rules |
| 49 | + |
| 50 | +Configure these once in **Settings → Branches** (or via `gh api`). The CI status |
| 51 | +check names below match the workflow job names exactly. |
| 52 | + |
| 53 | +## One-time repository settings |
| 54 | + |
| 55 | +Before the automation can work end-to-end, two repository-level toggles must |
| 56 | +be flipped (these cannot be set from a workflow): |
| 57 | + |
| 58 | +1. **Settings → Actions → General → Workflow permissions** |
| 59 | + - Set to **"Read and write permissions"** |
| 60 | + - Tick **"Allow GitHub Actions to create and approve pull requests"** |
| 61 | + |
| 62 | + Without this, `auto-pr-dev.yml` and `auto-pr-main.yml` will fail with |
| 63 | + `GitHub Actions is not permitted to create or approve pull requests`, |
| 64 | + even though the workflows themselves declare `pull-requests: write`. |
| 65 | + |
| 66 | +2. **Settings → Actions → General → Fork pull request workflows from outside |
| 67 | + collaborators** — set to **"Require approval for first-time contributors"** |
| 68 | + (default is fine). Workflows that need a write token (auto-rebase, |
| 69 | + auto-PR) intentionally skip fork branches in `branch-lifecycle.yml`, so |
| 70 | + this is mainly belt-and-braces. |
| 71 | + |
| 72 | +### `main` (protected, release branch) |
| 73 | + |
| 74 | +- Require a pull request before merging |
| 75 | + - Required approvals: **1** |
| 76 | + - Dismiss stale approvals when new commits are pushed |
| 77 | + - Require review from Code Owners |
| 78 | +- Require status checks to pass before merging |
| 79 | + - Require branches to be up to date before merging: **enabled** |
| 80 | + - Required status checks: |
| 81 | + - `Quality Summary` (from `python-quality.yml`) |
| 82 | + - `Security Summary` (from `security.yml`) |
| 83 | + - `Enforce dev to main rule` (from `claude-main-check.yml`) |
| 84 | + - `Claude Release Validation` (from `claude-main-check.yml`) |
| 85 | +- Require conversation resolution before merging |
| 86 | +- Require linear history |
| 87 | +- Restrict who can push to matching branches (admins only) |
| 88 | +- Do not allow bypassing the above settings |
| 89 | +- Allow force pushes: **off** |
| 90 | +- Allow deletions: **off** |
| 91 | +- Restrict pushes that create files larger than 100 MB |
| 92 | + |
| 93 | +### `dev` (protected, integration branch) |
| 94 | + |
| 95 | +- Require a pull request before merging |
| 96 | + - Required approvals: **1** |
| 97 | + - Dismiss stale approvals when new commits are pushed |
| 98 | + - Require review from Code Owners |
| 99 | +- Require status checks to pass before merging |
| 100 | + - Require branches to be up to date before merging: **enabled** |
| 101 | + - Required status checks: |
| 102 | + - `Quality Summary` (from `python-quality.yml`) |
| 103 | + - `Security Summary` (from `security.yml`) |
| 104 | + - `Claude Code Review` (from `claude-code-review.yml`) |
| 105 | +- Require conversation resolution before merging |
| 106 | +- Require linear history |
| 107 | +- Allow force pushes: **off** |
| 108 | +- Allow deletions: **off** |
| 109 | + |
| 110 | +### Working branches (`feature-*`, `fix-*`, `docs-*`, `refactor-*`, `chore-*`) |
| 111 | + |
| 112 | +- Allow force pushes (with lease) so the auto-rebase step can keep the branch |
| 113 | + current with `dev`. |
| 114 | +- No required reviews (review happens on the resulting PR to `dev`). |
| 115 | +- Will be auto-deleted by `branch-lifecycle.yml` after the PR merges. |
| 116 | + |
| 117 | +## One-shot setup with `gh` |
| 118 | + |
| 119 | +The block below applies the recommended protection to `main` and `dev`. Adjust |
| 120 | +the required check names if you rename a workflow. |
| 121 | + |
| 122 | +```bash |
| 123 | +# main |
| 124 | +gh api -X PUT "repos/${OWNER}/${REPO}/branches/main/protection" \ |
| 125 | + -H "Accept: application/vnd.github+json" \ |
| 126 | + -f required_status_checks.strict=true \ |
| 127 | + -F required_status_checks.contexts[]='Quality Summary' \ |
| 128 | + -F required_status_checks.contexts[]='Security Summary' \ |
| 129 | + -F required_status_checks.contexts[]='Enforce dev to main rule' \ |
| 130 | + -F required_status_checks.contexts[]='Claude Release Validation' \ |
| 131 | + -f enforce_admins=true \ |
| 132 | + -F required_pull_request_reviews.required_approving_review_count=1 \ |
| 133 | + -F required_pull_request_reviews.dismiss_stale_reviews=true \ |
| 134 | + -F required_pull_request_reviews.require_code_owner_reviews=true \ |
| 135 | + -f required_linear_history=true \ |
| 136 | + -f allow_force_pushes=false \ |
| 137 | + -f allow_deletions=false \ |
| 138 | + -f restrictions=null |
| 139 | + |
| 140 | +# dev |
| 141 | +gh api -X PUT "repos/${OWNER}/${REPO}/branches/dev/protection" \ |
| 142 | + -H "Accept: application/vnd.github+json" \ |
| 143 | + -f required_status_checks.strict=true \ |
| 144 | + -F required_status_checks.contexts[]='Quality Summary' \ |
| 145 | + -F required_status_checks.contexts[]='Security Summary' \ |
| 146 | + -F required_status_checks.contexts[]='Claude Code Review' \ |
| 147 | + -f enforce_admins=false \ |
| 148 | + -F required_pull_request_reviews.required_approving_review_count=1 \ |
| 149 | + -F required_pull_request_reviews.dismiss_stale_reviews=true \ |
| 150 | + -F required_pull_request_reviews.require_code_owner_reviews=true \ |
| 151 | + -f required_linear_history=true \ |
| 152 | + -f allow_force_pushes=false \ |
| 153 | + -f allow_deletions=false \ |
| 154 | + -f restrictions=null |
| 155 | +``` |
| 156 | + |
| 157 | +## Release flow |
| 158 | + |
| 159 | +1. Open a PR from a working branch to `dev` (auto-created on first push). |
| 160 | +2. Quality + security + AI review run. Address feedback, get 1 approval, merge. |
| 161 | +3. When ready to release, run **Actions → Auto-Create Release PR (dev to main)** |
| 162 | + and pick `patch`/`minor`/`major`. |
| 163 | +4. The release PR is reviewed; `claude-main-check.yml` enforces that the PR |
| 164 | + came from `dev` and runs a minimal final gate. |
| 165 | +5. On merge, `release.yml` reads the version from `pyproject.toml`, builds |
| 166 | + `app-store-optimization-<version>.zip`, tags `v<version>`, and creates the |
| 167 | + GitHub Release with notes from `CHANGELOG.md`. |
| 168 | +6. `wiki-sync.yml` updates the wiki from the new `main`. |
| 169 | + |
| 170 | +## Secrets and variables required |
| 171 | + |
| 172 | +| Name | Type | Used by | |
| 173 | +|----------------------------|---------|---------------------------------------------------------| |
| 174 | +| `CLAUDE_CODE_OAUTH_TOKEN` | Secret | `claude.yml`, `claude-code-review.yml`, `claude-main-check.yml` | |
| 175 | +| `GITHUB_TOKEN` (built-in) | Secret | all workflows | |
| 176 | +| `PROJECT_VERSION` | Var (optional) | `wiki-sync.yml` (defaults to `1.0.0`) | |
0 commit comments