Files
xiaoxia-saas/scripts/ci/check_migration_naming.py
cibot 35e7670376
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 27s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m38s
AI Code Review / AI Code Review (pull_request) Successful in 8m52s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 14m33s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 17s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 33s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m46s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m58s
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m17s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 4m1s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 4m8s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 5m47s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 4m57s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 17s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m40s
style: black格式化check_migration_naming.py
2026-07-25 00:41:23 +08:00

138 lines
4.7 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
检查 Alembic migration 文件命名规范。
规则:
1. 文件名必须以数字前缀开头(3位补零),如 001_xxx.py、052_add_table.py
2. 数字前缀必须连续递增(与 check_migration_chain.py 一致,但只看文件名)
3. 数字前缀后必须跟有描述性后缀(不能只有数字)
4. 文件名使用小写+下划线(snake_case
5. revision 变量值必须与文件名数字前缀一致(可选带描述后缀)
用法:
python3 scripts/ci/check_migration_naming.py [alembic_versions_dir]
默认目录: alembic/versions/
退出码:
0 - 全部通过
1 - 有命名违规
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
# 文件名格式: 3位数字_描述.py
FILE_NAME_PATTERN = re.compile(r"^(\d{3})_[a-z][a-z0-9_]*\.py$")
# 纯数字文件名(不允许)
PURE_NUM_PATTERN = re.compile(r"^\d{3}\.py$")
# revision 值的数字前缀
REV_NUM_PATTERN = re.compile(r"^(\d{3})")
# revision 变量行
REV_LINE_PATTERN = re.compile(
r'^\s*revision\s*(?::\s*str\s*)?=\s*["\']([^"\']+)["\']',
re.MULTILINE,
)
def check_naming(versions_dir: Path) -> list[str]:
"""检查 migration 文件命名,返回错误列表。"""
errors: list[str] = []
if not versions_dir.is_dir():
return [f"目录不存在: {versions_dir}"]
py_files = sorted(f for f in versions_dir.iterdir() if f.suffix == ".py")
if not py_files:
return [f"目录下没有 migration 文件: {versions_dir}"]
print(f"检查 migration 文件命名: {versions_dir}")
print(f"共 {len(py_files)} 个文件")
print()
# 1. 文件名格式检查
print("1. 文件名格式检查...")
file_nums: list[int] = []
for f in py_files:
name = f.name
if PURE_NUM_PATTERN.match(name):
errors.append(f" ❌ {name}: 只有数字编号,缺少描述性后缀")
continue
m = FILE_NAME_PATTERN.match(name)
if not m:
errors.append(f" ❌ {name}: 命名格式不规范,应为 NNN_description.py " f"(3位数字前缀+下划线+小写描述)")
continue
file_nums.append(int(m.group(1)))
if not any("命名格式不规范" in e or "缺少描述性后缀" in e for e in errors):
print(f" ✅ 全部 {len(py_files)} 个文件名格式正确")
else:
for e in errors:
if "命名格式不规范" in e or "缺少描述性后缀" in e:
print(e)
# 2. 编号连续性检查(基于文件名数字前缀)
print()
print("2. 编号连续性检查...")
if file_nums:
expected = set(range(min(file_nums), max(file_nums) + 1))
actual = set(file_nums)
missing = sorted(expected - actual)
if missing:
errors.append(f" ❌ 编号不连续,缺少: {', '.join(f'{n:03d}' for n in missing)}")
print(f" ❌ 编号不连续,缺少 {len(missing)} 个: " f"{', '.join(f'{n:03d}' for n in missing)}")
else:
print(f" ✅ 编号连续({min(file_nums):03d} ~ {max(file_nums):03d}")
# 3. revision 变量与文件名前缀一致性检查
print()
print("3. revision变量与文件名一致性检查...")
rev_mismatch = 0
for f in py_files:
m = FILE_NAME_PATTERN.match(f.name)
if not m:
continue # 格式不对的已经报过了
file_num = m.group(1)
content = f.read_text(encoding="utf-8")
rev_match = REV_LINE_PATTERN.search(content)
if not rev_match:
errors.append(f" ❌ {f.name}: 未找到 revision 变量定义")
rev_mismatch += 1
continue
rev_value = rev_match.group(1)
rev_num_match = REV_NUM_PATTERN.match(rev_value)
if not rev_num_match or rev_num_match.group(1) != file_num:
errors.append(f" ❌ {f.name}: revision='{rev_value}' 与文件名前缀 {file_num} 不一致")
rev_mismatch += 1
if rev_mismatch == 0:
print(f" ✅ 全部 {len(py_files)} 个文件的 revision 与文件名一致")
return errors
def main() -> int:
versions_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("alembic/versions")
errors = check_naming(versions_dir)
print()
if errors:
print(f"❌ 发现 {len(errors)} 个命名问题")
print()
print("命名规范:")
print(" - 文件名格式: NNN_description.py3位数字前缀 + 下划线 + 小写描述)")
print(" - 编号必须连续,不能跳号")
print(" - revision 变量的数字前缀必须与文件名一致")
return 1
print("✅ 所有 migration 文件命名规范检查通过")
return 0
if __name__ == "__main__":
sys.exit(main())