Skip to content

Commit b40939d

Browse files
northdpoleclaude
andcommitted
test(migrations): fail when the alembic chain has more than one head
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent 8a2681b commit b40939d

1 file changed

Lines changed: 37 additions & 0 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""The migration chain must end in exactly one head.
2+
3+
Two heads break `flask db upgrade` ("Multiple head revisions"). When two branches each add
4+
a migration, add a merge migration whose down_revision lists both parents.
5+
"""
6+
7+
import re
8+
import unittest
9+
from pathlib import Path
10+
11+
VERSIONS = Path(__file__).resolve().parents[2] / "migrations" / "versions"
12+
13+
14+
def _heads() -> list:
15+
revisions, parents = {}, set()
16+
for path in VERSIONS.glob("*.py"):
17+
text = path.read_text()
18+
rev = re.search(r"^revision\s*=\s*['\"]([^'\"]+)", text, re.M)
19+
down = re.search(r"^down_revision\s*=\s*(.+)$", text, re.M)
20+
if rev:
21+
revisions[rev.group(1)] = path.name
22+
if down:
23+
parents.update(re.findall(r"['\"]([0-9a-zA-Z_]+)['\"]", down.group(1)))
24+
return sorted(r for r in revisions if r not in parents)
25+
26+
27+
class AlembicSingleHeadTest(unittest.TestCase):
28+
def test_exactly_one_head(self) -> None:
29+
self.assertEqual(
30+
len(_heads()),
31+
1,
32+
f"migration chain has multiple heads: {_heads()}; add a merge migration",
33+
)
34+
35+
36+
if __name__ == "__main__":
37+
unittest.main()

0 commit comments

Comments
 (0)