fix: merge forked alembic migration chain #169
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/basketball-api!169
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "166-fix-alembic-forked-migration-chain"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
down_revision = "018", creating a forked Alembic migration chain with two heads on mainChanges
alembic/versions/021_merge_heads.py: new merge migration withdown_revision = ("019", "020")and empty upgrade/downgrade functionsTest Plan
alembic headsreturns exactly one head (021) after this changealembic historyshows clean merge:019, 020 -> 021 (head) (mergepoint)alembic upgrade headwill stamp 021 as current with no schema changesReview Checklist
Related
Review: LGTM
Diff reviewed: 1 file, +26 lines, 0 deletions
Findings: None. This is a textbook Alembic merge migration:
down_revision = ("019", "020")correctly uses a tuple to merge the two headsalembic headsconfirmed single head (021) after the changeNo issues found.
PR #169 Review
DOMAIN REVIEW
Tech stack: Python / FastAPI / SQLAlchemy / Alembic
This PR adds a single file:
alembic/versions/021_merge_heads.py-- a merge migration that reunifies two Alembic heads.Root cause verified. Migrations 019 (
019_player_teams_junction.py) and 020 (020_add_custom_notes_to_player.py) both declaredown_revision = "018", creating a forked Alembic chain with two heads. This is a real bug --alembic upgrade headwould fail with "Multiple head revisions" on a fresh database.Merge migration correctness:
revision = "021"-- follows the project's numeric naming convention (001-020 all use numeric IDs, with the exception of the legacye09c9e678004at position 006)down_revision = ("019", "020")-- correct Alembic tuple syntax for merge migrationsbranch_labels = Noneanddepends_on = None-- correct, no branch labeling neededupgrade()anddowngrade()are bothpass-- correct, this is a merge-only migration with no schema changesfrom alembic import opandimport sqlalchemy as sawith# noqa: F401-- these imports are unused but follow the project's migration template convention. Acceptable.No existing migrations modified -- the original 019 and 020 files remain untouched. Since both are already applied in production, modifying them would be dangerous. The merge-forward approach is the correct pattern.
env.py note (pre-existing, out of scope):
alembic/env.pyimportsCoach, Outbox, Parent, Player, Registration, Tenantbut does not importTeam. This meansalembic revision --autogeneratemay not detect changes to the Team model. This predates this PR and is not a blocker here.BLOCKERS
None.
This PR introduces no new functionality -- it is a structural fix to Alembic's migration chain. The BLOCKER criteria do not apply:
alembic headsreturning one head, which was verified manually per the test plan)NITS
Create Date: 2026-03-25while earlier migrations use the full datetime format (2026-03-10 18:27:23.147242). Cosmetic inconsistency only -- the project actually uses both formats across the migration history, so this is fine as-is.SOP COMPLIANCE
166-fix-alembic-forked-migration-chainreferences issue #166Missing from SOP: The PR body does not include a plan slug reference in the Related section. However, this is a bugfix (not plan-driven work), so a plan slug is not required.
PROCESS OBSERVATIONS
Deployment frequency impact: Positive. This unblocks
alembic upgrade headon fresh databases. Without this fix, any new environment provisioning or CI migration test would fail.Change failure risk: Very low. The migration performs no schema changes. Applying it in production will simply stamp revision 021 as current.
Pre-existing test failures (discovered scope): The PR reports 8 of 529 tests failing on main. Based on the test plan and codebase analysis, these appear to be:
tests/test_keycloak_integration.pythat depend on Keycloak connectivity (test env useshttp://localhost:8080/realms/test-- failures indicate mock gaps or DNS resolution issues in CI)test_temp_team_dedupintests/test_admin_teams.py(line 290): Tests the many-to-many team assignment dedup logic introduced in PR #164 / migration 019. Likely a fixture or model relationship issue from the junction table migrationtests/test_jersey.py:test_sends_announcement_to_all_parents(line 953) andtest_announcement_email_has_no_password_field(line 1148) -- likely related to email service mock configuration or the password-removal cleanup (#168)These 8 failures predate this PR and should be tracked as separate issues. Recommend creating a Forgejo issue for the pre-existing test failures to prevent technical debt accumulation.
Also noted (out of scope):
Teammodel is not imported inalembic/env.py, which means autogenerate would miss Team table changes. Worth a separate fix.VERDICT: APPROVED
d3deabdfd4b983cf5784PR #169 Review (Re-review after rebase)
DOMAIN REVIEW
Tech stack: Python / FastAPI / SQLAlchemy / Alembic
Migration chain verification -- I traced every
down_revisioninalembic/versions/to verify the DAG:018:down_revision = "017"-- linear, correct019:down_revision = "018"-- fork arm 1, correct020:down_revision = "018"-- fork arm 2, correct021:down_revision = "020"-- extends arm 2 (added by PR #163), correct022:down_revision = ("019", "021")-- merges the two leaf heads, correctThe merge migration correctly identifies 019 and 021 as the two heads after the rebase incorporated migration 021 from PR #163. The tuple
("019", "021")will causealembic headsto return exactly one head:022.Merge migration content:
upgrade()anddowngrade()are bothpass, which is correct -- merge migrations carry no schema changes. The# noqa: F401comments on the unused imports are appropriate sincealembic/versionsis excluded from ruff inpyproject.toml(line 37), but the noqa annotations are still good practice.Docstring accuracy: The docstring in
022_merge_heads.pycorrectly describes the fork topology: "019 descends from 018 directly, 021 descends from 018 via 020."BLOCKERS
None. This is a zero-schema-change merge migration. No new functionality requiring tests. No user input. No secrets. No auth logic.
NITS
Stale PR body: The PR description still references the pre-rebase numbering throughout:
alembic/versions/021_merge_heads.py-- should be022_merge_heads.pydown_revision = ("019", "020")-- should be("019", "021")alembic headsreturns021-- should be022019, 020 -> 021 (head)-- should be019, 021 -> 022 (head)The code is correct; only the PR body text is stale from before the rebase. Recommend updating the PR body to match the actual file for future reference.
SOP COMPLIANCE
166-fix-alembic-forked-migration-chainreferences issue #166PROCESS OBSERVATIONS
Clean, minimal fix. One file, no schema changes, no risk of change failure. The fork was caused by two PRs (#164 and #165) landing against the same parent migration 018 without coordinating revision numbers, then PR #163 extending one arm. This merge migration is the standard Alembic pattern to resolve multi-head situations. Safe for
alembic upgrade headin prod -- it will simply stamp 022 as current.VERDICT: APPROVED