From 3d4cb554b55c384ec5f8b6aec0d3af4073bc5775 Mon Sep 17 00:00:00 2001 From: lingying Date: Wed, 8 Jul 2026 21:48:37 +0800 Subject: [PATCH] =?UTF-8?q?fix(ci):=20=E4=BF=AE=E5=A4=8D=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E5=AE=89=E5=85=A8=E9=97=B8=E9=97=A8=E8=AF=AF=E6=8A=A5?= =?UTF-8?q?=EF=BC=8C=E6=94=B9=E4=B8=BA=E5=8F=AA=E5=AF=B9=E6=AF=94main?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E6=96=B0=E5=A2=9E=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - check_migration_safety.py 新增 --diff-against 参数,支持通过 git diff 只检查新增迁移 - CI 中 prepare git 环境并 fetch origin/main,安全闸门对比 main 分支 - 避免历史已执行迁移的破坏性操作导致误报 --- .gitea/workflows/ci-cd.yml | 25 +++++++++++++- scripts/check_migration_safety.py | 54 +++++++++++++++++++++++++++---- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/.gitea/workflows/ci-cd.yml b/.gitea/workflows/ci-cd.yml index 7b61bcd53..bb0e32e2b 100755 --- a/.gitea/workflows/ci-cd.yml +++ b/.gitea/workflows/ci-cd.yml @@ -129,11 +129,34 @@ jobs: grep -q "Running upgrade" /tmp/alembic-upgrade.sql python3 scripts/check_schema_metadata.py + - name: Prepare git for migration diff + shell: sh + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + set -eu + if command -v git >/dev/null 2>&1; then + git init -q + git config user.email "ci@localhost" + git config user.name "CI" + git add . + git commit -q -m "current" + REPO_URL="https://x-access-token:${GITHUB_TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git" + git remote add origin "$REPO_URL" + git fetch origin main --depth=1 -q 2>/dev/null || echo "WARN: cannot fetch main, will check all migrations" + else + echo "WARN: git not available, will check all migrations" + fi + - name: Check migration safety shell: sh run: | set -eu - python3 scripts/check_migration_safety.py --allow-medium-risk + if git rev-parse origin/main >/dev/null 2>&1; then + python3 scripts/check_migration_safety.py --allow-medium-risk --diff-against origin/main + else + python3 scripts/check_migration_safety.py --allow-medium-risk + fi - name: Run unit tests shell: sh diff --git a/scripts/check_migration_safety.py b/scripts/check_migration_safety.py index ad76c30bb..8038bb059 100644 --- a/scripts/check_migration_safety.py +++ b/scripts/check_migration_safety.py @@ -11,6 +11,16 @@ 忽略 downgrade 函数中的操作(那是回滚逻辑,正常的)。 +使用方式: + # 检查所有迁移(不推荐,会扫历史已执行的迁移) + python3 scripts/check_migration_safety.py + + # 只检查与目标分支相比新增的迁移(推荐用于CI) + python3 scripts/check_migration_safety.py --diff-against origin/main + + # 只检查指定版本之后的迁移 + python3 scripts/check_migration_safety.py --since 030_xxx + 退出码: 0 - 安全 / 只有非破坏性变更 1 - 检测到高风险破坏性变更 @@ -22,6 +32,7 @@ from __future__ import annotations import argparse import os import re +import subprocess import sys from pathlib import Path from typing import List, Tuple @@ -60,7 +71,6 @@ def extract_upgrade_content(content: str) -> str: 从迁移文件中提取 upgrade 函数的内容。 只检查 upgrade 中的操作,忽略 downgrade。 """ - # 匹配 def upgrade(): 或 def upgrade() -> None: 等格式 upgrade_match = re.search(r"def upgrade\b[^:]*:", content) if not upgrade_match: return "" @@ -78,12 +88,35 @@ def extract_upgrade_content(content: str) -> str: return content[upgrade_start:upgrade_end] -def find_new_migrations(since_revision: str | None = None) -> List[Path]: +def get_new_migrations_via_diff(diff_target: str) -> List[Path]: """ - 找出新增的迁移文件。 - 如果指定了 since_revision,则找出该版本之后的所有迁移; - 否则找出所有迁移文件。 + 通过 git diff 对比目标分支/commit,找出 alembic/versions/ 下新增的迁移文件。 + 只包含新增文件(A状态),不包含修改或删除的文件。 """ + try: + result = subprocess.run( + ["git", "diff", "--name-only", "--diff-filter=A", diff_target, "HEAD", "--", "alembic/versions/"], + cwd=str(REPO_ROOT), + capture_output=True, + text=True, + check=True, + ) + files = [line.strip() for line in result.stdout.strip().split("\n") if line.strip()] + return [REPO_ROOT / f for f in files] + except subprocess.CalledProcessError as e: + print(f"⚠️ git diff 失败({diff_target}):{e.stderr.strip()}") + print(f" 降级为检查所有迁移文件") + return sorted(ALEMBIC_VERSIONS_DIR.glob("*.py")) + + +def find_new_migrations(since_revision: str | None = None, diff_against: str | None = None) -> List[Path]: + """ + 找出需要检查的迁移文件。 + 优先级:diff_against > since_revision > 全部 + """ + if diff_against: + return get_new_migrations_via_diff(diff_against) + all_migrations = sorted(ALEMBIC_VERSIONS_DIR.glob("*.py")) if not since_revision: return all_migrations @@ -134,6 +167,11 @@ def main() -> int: default=os.getenv("MIGRATION_SINCE_REVISION"), help="只检查指定版本之后的迁移(如:030_xxx),不传则检查所有迁移", ) + parser.add_argument( + "--diff-against", + default=os.getenv("MIGRATION_DIFF_AGAINST"), + help="对比指定分支/commit,只检查新增的迁移文件(推荐用于CI,如 origin/main)", + ) parser.add_argument( "--warn-only", action="store_true", @@ -146,13 +184,15 @@ def main() -> int: ) args = parser.parse_args() - migrations = find_new_migrations(args.since) + migrations = find_new_migrations(args.since, args.diff_against) if not migrations: - print("✅ 未找到需要检查的迁移文件") + print("✅ 未找到需要检查的新增迁移文件,跳过") return 0 print(f"🔍 正在检查 {len(migrations)} 个迁移文件的 upgrade 操作...") + if args.diff_against: + print(f" (对比基准:{args.diff_against},仅检查新增迁移)") print() all_high = []