ci(gitflow): enforce branch and PR governance checks
This commit is contained in:
17
.gitea/PULL_REQUEST_TEMPLATE.md
Normal file
17
.gitea/PULL_REQUEST_TEMPLATE.md
Normal file
@@ -0,0 +1,17 @@
|
||||
## Summary
|
||||
|
||||
- TODO item reference (exact text): `...`
|
||||
- Scope (single primary TODO item): `...`
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Linked TODO item is in `TODO.md`
|
||||
- [ ] Branch name follows `todo/*`, `refactor/*`, or `code/*`
|
||||
- [ ] `bun run check`
|
||||
- [ ] `bun run typecheck`
|
||||
- [ ] `bun run test`
|
||||
- [ ] E2E validation plan included (`bun run test:e2e` or reason if deferred)
|
||||
|
||||
## Notes
|
||||
|
||||
- Risks / migrations / rollout notes:
|
||||
25
.gitea/scripts/check-branch-name.sh
Executable file
25
.gitea/scripts/check-branch-name.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
branch="${1:-}"
|
||||
|
||||
if [ -z "$branch" ]; then
|
||||
echo "Missing branch name."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$branch" in
|
||||
dev|staging|main)
|
||||
echo "Long-lived branch detected: $branch"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if printf "%s" "$branch" | grep -Eq '^(todo|refactor|code)\/[a-z0-9]+([._-][a-z0-9]+)*$'; then
|
||||
echo "Branch naming valid: $branch"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Invalid branch name: $branch"
|
||||
echo "Expected: todo/<slug> | refactor/<slug> | code/<slug>"
|
||||
exit 1
|
||||
17
.gitea/scripts/check-pr-todo-reference.sh
Executable file
17
.gitea/scripts/check-pr-todo-reference.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
body="${1:-}"
|
||||
|
||||
if [ -z "$body" ]; then
|
||||
echo "PR body is empty."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if printf "%s" "$body" | grep -Eq 'TODO|todo|\[P[1-3]\]'; then
|
||||
echo "PR body includes TODO reference."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "PR body must reference the related TODO item."
|
||||
exit 1
|
||||
34
.gitea/scripts/configure-branch-protection.sh
Executable file
34
.gitea/scripts/configure-branch-protection.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
if [ "${#}" -ne 4 ]; then
|
||||
echo "Usage: $0 <base-url> <owner> <repo> <token>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
base_url="$1"
|
||||
owner="$2"
|
||||
repo="$3"
|
||||
token="$4"
|
||||
|
||||
protect_branch() {
|
||||
branch="$1"
|
||||
|
||||
curl -sS -X POST \
|
||||
"${base_url}/api/v1/repos/${owner}/${repo}/branch_protections" \
|
||||
-H "Authorization: token ${token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"branch_name\": \"${branch}\",
|
||||
\"enable_push\": false,
|
||||
\"enable_push_whitelist\": false,
|
||||
\"enable_merge_whitelist\": false,
|
||||
\"enable_status_check\": true,
|
||||
\"status_check_contexts\": [\"Governance Checks\", \"Lint Typecheck Unit E2E\"]
|
||||
}" >/dev/null
|
||||
}
|
||||
|
||||
protect_branch "main"
|
||||
protect_branch "staging"
|
||||
|
||||
echo "Branch protection applied for main and staging."
|
||||
@@ -25,8 +25,38 @@ env:
|
||||
CMS_SUPPORT_LOGIN_KEY: "support-access"
|
||||
|
||||
jobs:
|
||||
governance:
|
||||
name: Governance Checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate branch naming
|
||||
run: |
|
||||
branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}"
|
||||
sh .gitea/scripts/check-branch-name.sh "$branch"
|
||||
|
||||
- name: Validate PR TODO reference
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
body='${{ github.event.pull_request.body }}'
|
||||
sh .gitea/scripts/check-pr-todo-reference.sh "$body"
|
||||
|
||||
- name: Commit schema check (latest commit)
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: ${{ env.BUN_VERSION }}
|
||||
|
||||
- name: Install dependencies for commitlint
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Commitlint
|
||||
run: bun run commitlint
|
||||
|
||||
quality:
|
||||
name: Lint Typecheck Unit E2E
|
||||
needs: governance
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
|
||||
@@ -96,6 +96,13 @@ Apply in repository settings:
|
||||
Optional:
|
||||
|
||||
- Protect `dev` from direct push if team size/process requires stricter control.
|
||||
- Automate protection via `.gitea/scripts/configure-branch-protection.sh`.
|
||||
|
||||
## Governance Automation
|
||||
|
||||
- Branch naming check: `.gitea/scripts/check-branch-name.sh`
|
||||
- PR TODO reference check: `.gitea/scripts/check-pr-todo-reference.sh`
|
||||
- PR template: `.gitea/PULL_REQUEST_TEMPLATE.md`
|
||||
|
||||
## Commit Signing Notes
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
Follow `BRANCHING.md` for long-lived and task branch rules.
|
||||
|
||||
Pull requests should use `.gitea/PULL_REQUEST_TEMPLATE.md` and link the exact TODO item.
|
||||
|
||||
## Commit Message Schema
|
||||
|
||||
This repository uses Conventional Commits.
|
||||
|
||||
66
docs/product-engineering/git-flow-governance.md
Normal file
66
docs/product-engineering/git-flow-governance.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Git Flow Governance
|
||||
|
||||
## Scope
|
||||
|
||||
Governance rules for branch protections, PR gates, branch naming, and merge discipline.
|
||||
|
||||
## Branch Protection
|
||||
|
||||
Protected branches:
|
||||
|
||||
- `main`
|
||||
- `staging`
|
||||
|
||||
Apply protections using:
|
||||
|
||||
- Gitea UI settings
|
||||
- or automation script: `.gitea/scripts/configure-branch-protection.sh`
|
||||
|
||||
Minimum policy:
|
||||
|
||||
- no direct pushes
|
||||
- PR merge required
|
||||
- required status checks
|
||||
- at least one reviewer approval
|
||||
|
||||
## PR Gates
|
||||
|
||||
Required checks are implemented in `.gitea/workflows/ci.yml`:
|
||||
|
||||
- Governance Checks
|
||||
- Lint Typecheck Unit E2E
|
||||
|
||||
## Branch Naming and TODO Scope
|
||||
|
||||
Allowed branch prefixes:
|
||||
|
||||
- `todo/`
|
||||
- `refactor/`
|
||||
- `code/`
|
||||
|
||||
Validation script:
|
||||
|
||||
- `.gitea/scripts/check-branch-name.sh`
|
||||
|
||||
Rule:
|
||||
|
||||
- one primary TODO item per delivery branch
|
||||
|
||||
PR TODO reference enforcement:
|
||||
|
||||
- template: `.gitea/PULL_REQUEST_TEMPLATE.md`
|
||||
- CI check: `.gitea/scripts/check-pr-todo-reference.sh`
|
||||
|
||||
## Branch Lifecycle
|
||||
|
||||
1. Create short-lived branch from latest integration tip.
|
||||
2. Implement one primary scope.
|
||||
3. Open PR and pass required checks.
|
||||
4. Merge into `dev`.
|
||||
5. Promote `dev -> staging -> main`.
|
||||
|
||||
## Commit and Tag Policy
|
||||
|
||||
- Conventional commits required (`CONTRIBUTING.md`)
|
||||
- release tags: `vX.Y.Z`
|
||||
- changelog generated from commit history
|
||||
@@ -28,3 +28,9 @@ Follow `BRANCHING.md`:
|
||||
```bash
|
||||
bun run changelog:release
|
||||
```
|
||||
|
||||
## Governance
|
||||
|
||||
- Branch and PR governance checks run in `.gitea/workflows/ci.yml`.
|
||||
- PR template: `.gitea/PULL_REQUEST_TEMPLATE.md`
|
||||
- Versioning policy: `VERSIONING.md`
|
||||
|
||||
Reference in New Issue
Block a user