ci(policy): enforce one migration per PR
This commit is contained in:
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@@ -62,6 +62,13 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- name: Enforce one migration per PR
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
env:
|
||||||
|
GITHUB_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||||
|
run: |
|
||||||
|
./scripts/ci/one_migration_per_pr.sh
|
||||||
|
|
||||||
- name: Run migration integrity gate
|
- name: Run migration integrity gate
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
23
docs/policy/one-migration-per-pr.md
Normal file
23
docs/policy/one-migration-per-pr.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Policy: one DB migration per PR
|
||||||
|
|
||||||
|
## Rule
|
||||||
|
If a pull request adds migration files under:
|
||||||
|
|
||||||
|
- `backend/migrations/versions/*.py`
|
||||||
|
|
||||||
|
…then it must add **no more than one** migration file.
|
||||||
|
|
||||||
|
## Why
|
||||||
|
- Makes review and rollback simpler.
|
||||||
|
- Reduces surprise Alembic multiple-head situations.
|
||||||
|
- Keeps CI/installer failures easier to debug.
|
||||||
|
|
||||||
|
## Common exceptions / guidance
|
||||||
|
- If you have multiple Alembic heads, prefer creating **one** merge migration.
|
||||||
|
- If changes are unrelated, split into multiple PRs.
|
||||||
|
|
||||||
|
## CI enforcement
|
||||||
|
CI runs `scripts/ci/one_migration_per_pr.sh` on PRs and fails if >1 migration file is added.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
This policy does not replace the existing migration integrity gate (`make backend-migration-check`). It is a lightweight guardrail to prevent multi-migration PRs.
|
||||||
49
scripts/ci/one_migration_per_pr.sh
Executable file
49
scripts/ci/one_migration_per_pr.sh
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Enforce: if a PR adds migration files under backend/migrations/versions/, it must add <= 1.
|
||||||
|
# Rationale: keeps review/rollback straightforward and avoids surprise multiple-head merges.
|
||||||
|
|
||||||
|
if [ "${GITHUB_EVENT_NAME:-}" != "pull_request" ]; then
|
||||||
|
echo "Not a pull_request event; skipping one-migration-per-PR gate."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASE_SHA="${GITHUB_BASE_SHA:-${GITHUB_EVENT_PULL_REQUEST_BASE_SHA:-}}"
|
||||||
|
HEAD_SHA="${GITHUB_SHA:-}"
|
||||||
|
|
||||||
|
if [ -z "$BASE_SHA" ] || [ -z "$HEAD_SHA" ]; then
|
||||||
|
echo "Missing BASE_SHA/HEAD_SHA (BASE_SHA='$BASE_SHA', HEAD_SHA='$HEAD_SHA')"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure base is present in shallow clones.
|
||||||
|
git fetch --no-tags --depth=1 origin "$BASE_SHA" || true
|
||||||
|
|
||||||
|
CHANGED=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" || true)
|
||||||
|
if [ -z "$CHANGED" ]; then
|
||||||
|
echo "No changed files detected."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
MIGRATIONS=$(echo "$CHANGED" | grep -E '^backend/migrations/versions/.*\.py$' || true)
|
||||||
|
COUNT=$(echo "$MIGRATIONS" | sed '/^$/d' | wc -l | tr -d ' ')
|
||||||
|
|
||||||
|
if [ "$COUNT" -le 1 ]; then
|
||||||
|
echo "Migration gate OK (migrations_added=$COUNT)."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Migration gate FAILED: this PR adds $COUNT migration files; policy allows at most 1."
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "Migrations detected:"
|
||||||
|
echo "$MIGRATIONS"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "How to fix:"
|
||||||
|
echo "- Consolidate schema changes into a single migration file (squash)."
|
||||||
|
echo "- If you have multiple Alembic heads, create ONE merge migration instead of several."
|
||||||
|
echo "- If you truly need multiple migrations, split into multiple PRs."
|
||||||
|
|
||||||
|
exit 1
|
||||||
Reference in New Issue
Block a user