diff --git a/scripts/ci/validate_code_quality.sh b/scripts/ci/validate_code_quality.sh index ce67490eb..874c9c6cf 100644 --- a/scripts/ci/validate_code_quality.sh +++ b/scripts/ci/validate_code_quality.sh @@ -56,95 +56,18 @@ for fpath, items in data.get('results', {}).items(): fi echo "✅ Secret scan passed" -# --- 增量/全量模式判断 --- +# --- 代码质量检查(全量,PR 和 push 统一标准)--- +# 历史:PR 侧用增量检查以加速,但会导致 push 侧全量检查失败时 PR 侧感知不到 +# 现在统一全量检查,确保 CI 真正保护主分支(black/isort/ruff 全量仅多几十秒) echo "" -echo "=== [2/6] Code quality checks ===" +echo "=== [2/6] Code quality checks (full scan) ===" SCAN_MODE="full" -CHANGED_PY_FILES="" +echo "Full scan mode" +python3 -m compileall -q alembic apps packages tests scripts +python3 -m black --check --fast alembic apps packages tests scripts +python3 -m isort --check-only alembic apps packages tests scripts +python3 -m ruff check apps packages tests --statistics -if [ "${GITHUB_EVENT_NAME:-}" = "pull_request" ] && [ -n "${GITHUB_REF_NAME:-}" ] && [ -n "${GITHUB_TOKEN:-}" ]; then - PR_NUMBER=$(echo "$GITHUB_REF" | sed 's|refs/pull/||; s|/.*||') - API_URL="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/files?limit=100" - set +e - RESPONSE=$(curl -s -w "\n%{http_code}" -H "Authorization: token ${GITHUB_TOKEN}" "$API_URL") - HTTP_CODE=$(echo "$RESPONSE" | tail -n1) - BODY=$(echo "$RESPONSE" | sed '$d') - set -e - if [ "$HTTP_CODE" = "200" ]; then - CHANGED_PY_FILES=$(echo "$BODY" | python3 -c " -import json, sys -try: - files = json.load(sys.stdin) - py_files = [f['filename'] for f in files if f['filename'].endswith('.py') and f['status'] != 'removed'] - print(' '.join(py_files)) -except Exception: - print('') -") - # 新增文件(added)强制全量检查,防止增量漏检 - ADDED_PY_FILES=$(echo "$BODY" | python3 -c " -import json, sys -try: - files = json.load(sys.stdin) - added = [f['filename'] for f in files if f['filename'].endswith('.py') and f['status'] == 'added'] - print(' '.join(added)) -except Exception: - print('') -") - MODIFIED_PY_FILES=$(echo "$BODY" | python3 -c " -import json, sys -try: - files = json.load(sys.stdin) - modified = [f['filename'] for f in files if f['filename'].endswith('.py') and f['status'] not in ('removed', 'added')] - print(' '.join(modified)) -except Exception: - print('') -") - if [ -n "$CHANGED_PY_FILES" ]; then - SCAN_MODE="incremental" - echo "Incremental mode: $(echo "$CHANGED_PY_FILES" | wc -w) Python files changed" - else - SCAN_MODE="skip_py" - echo "No Python files changed in this PR" - fi - else - echo "WARN: API returned HTTP $HTTP_CODE, falling back to full scan" - fi -else - echo "Full scan mode (not a PR event)" -fi - -if [ "$SCAN_MODE" = "incremental" ]; then - # 防御性过滤 - EXISTING_PY_FILES="" - for f in $CHANGED_PY_FILES; do - if [ -f "$f" ]; then - if [ -z "$EXISTING_PY_FILES" ]; then - EXISTING_PY_FILES="$f" - else - EXISTING_PY_FILES="$EXISTING_PY_FILES $f" - fi - fi - done - CHANGED_PY_FILES="$EXISTING_PY_FILES" - - python3 -m compileall -q $CHANGED_PY_FILES - python3 -m black --check --fast $CHANGED_PY_FILES - python3 -m isort --check-only $CHANGED_PY_FILES - RUFF_FILES=$(echo "$CHANGED_PY_FILES" | tr ' ' '\n' | grep -v '^scripts/' | grep -v '^$' | xargs) - if [ -n "$RUFF_FILES" ]; then - python3 -m ruff check $RUFF_FILES --statistics - else - echo "No ruff-checkable files changed, skipping" - fi -elif [ "$SCAN_MODE" = "skip_py" ]; then - echo "No Python files changed - skipping Python lint checks" -else - echo "Full scan mode" - python3 -m compileall -q alembic apps packages tests scripts - python3 -m black --check --fast alembic apps packages tests scripts - python3 -m isort --check-only alembic apps packages tests scripts - python3 -m ruff check apps packages tests --statistics -fi echo "✅ Code quality checks passed" # --- Bandit 安全扫描(仅告警) --- diff --git a/tests/unit/test_bgm_utils.py b/tests/unit/test_bgm_utils.py index c1cd83ea5..9b7c36ad6 100755 --- a/tests/unit/test_bgm_utils.py +++ b/tests/unit/test_bgm_utils.py @@ -11,8 +11,7 @@ class TestMergeBgmConfigBothEmpty: def test_both_empty(self): result = merge_bgm_config({}, {}) assert result == {} - # 确保返回的是新字典,不是同一个引用 - assert result is not {} + # 返回新字典(值已通过 == 验证,is not {} 无实际意义(每次{}每次新建对象) def test_user_none_returns_template_copy(self): """用户传 None 视为空配置,返回模板副本。""" diff --git a/tests/unit/test_classification_domain.py b/tests/unit/test_classification_domain.py index 5219983f9..7bdc90ec8 100755 --- a/tests/unit/test_classification_domain.py +++ b/tests/unit/test_classification_domain.py @@ -245,7 +245,7 @@ class TestClassificationJobState: assert job.confidence == 1.0 -class TestClassificationJobStatusMissing: +class TestClassificationJobStatusMissingAliases: """ClassificationJobStatus._missing_ 兼容行为测试""" def test_done_maps_to_completed(self):