PyCourt 武器库 - 皇帝节仗¶
You can translate it into your own country's language.
#!/bin/bash
# ==============================================================================
# 🏛️ 皇帝节仗 v1.1
# 定位:对整个项目进行全面审计
# 用法: ./qa.sh
# 场景:跨域模块和组件综合验收以及在代码准备推送前进行全面审计。
# 要求:项目必须通过所有静态审计与测试,且测试覆盖率符合预设阈值。
# 说明:此脚适用于大多数常见场景,可根据实际需要自由搭配、修改和扩展。
# 更新:新增 AI 模式,排除干扰,降低 token 成本 命令:./qa.sh -a
# ==============================================================================
# 1. 引入“职责分离”,将“读取配置”与“执行审计”彻底解耦。
# 2. 全面覆盖静态审计与动态测试,确保代码质量万无一失。
# 3. 强化配置解析的健壮性,使用专门的Python脚本和jq进行安全解析。
# 4. 通过 PythonCourt 最高法院 (TP001/TP002/TP003) 审查测试纯净度与真实性。
# 5. 重构作战序列,流程如史诗般清晰,意图自明。
# ==============================================================================
# ===============
# 准备开始:环境配置
# ===============
# ---------------------------------------------------
# --- 1. 帝国军法:零容忍,立即终止 ---
# ---------------------------------------------------
set -euo pipefail
# ---------------------------------------------------
# --- 2. 核心:以当前位置为帝国中心 ---
# 确保所有子进程(包括 Python 脚本)都知道项目根目录在哪里
# ---------------------------------------------------
export PYTHONPATH=$(pwd)
# ---------------------------------------------------
# --- 3. PyCourt 语言切换:默认为中文,可选`-en` ---
# ---------------------------------------------------
export PYCOURT_LANG="${PYCOURT_LANG:-zh}"
# ---------------------------------------------------
# --- 4. 颜色与辅助函数:本脚本内联实现 ---
# ---------------------------------------------------
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
print_chapter_header() {
# 人类模式下输出章节标题;AI 模式下默认关闭以减少噪音
if [[ "${PYCOURT_UI_MODE:-human}" != "ai" ]]; then
echo -e "\n${BLUE}================== $1 ==================${NC}"
fi
}
print_sub_header() {
# 人类模式下输出小节标题;AI 模式下默认关闭以减少噪音
if [[ "${PYCOURT_UI_MODE:-human}" != "ai" ]]; then
echo -e "\n${YELLOW}--- $1 ---${NC}"
fi
}
print_success() {
echo -e "${GREEN}✅ $1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_error() {
echo -e "${RED}❌ $1${NC}"
}
run_mypy_judgement() {
poetry run mypy "$@"
}
# ---------------------------------------------------
# --- 5. 模式配置参数 ---
# ---------------------------------------------------
# 是否启用“特种部队演习”(E2E 测试),默认 0:关闭。
# 可通过在命令行添加 --with-e2e 参数启用。
RUN_E2E=0
if [[ " $@ " =~ " --with-e2e " ]]; then
RUN_E2E=1
fi
# 是否启用 AI 模式(紧凑文案 + 抑制叙事输出)。
# 通过 -a 或 --ai 开启,环境变量优先级更高(若外部已设置则保留外部值)。
for arg in "$@"; do
case "$arg" in
-a|--ai)
export PYCOURT_AUDIENCE=${PYCOURT_AUDIENCE:-ai}
export PYCOURT_UI_MODE=${PYCOURT_UI_MODE:-ai}
# 若当前语言为中文系 (zh*),在 AI 模式下自动切换到英文文案,
# 若用户已显式设为 en/其他语言,则尊重用户选择。
if [[ "${PYCOURT_LANG:-}" == zh* ]]; then
export PYCOURT_LANG=en
fi
;;
esac
done
# 是否启用“测试阶段”(第三/四章),默认 0:关闭。
# 需要时可通过修改为 `ENABLE_TEST_PHASE:-1` 打开统一测试能力。
ENABLE_TEST_PHASE=${ENABLE_TEST_PHASE:-0}
# ---------------------------------------------------
# --- 6. 读取审计参数 ---
# 从 pyproject.toml 中读取 [tool.pycourt] 配置
# ---------------------------------------------------
CONFIG_JSON=$(poetry run python -m pycourt.config.read_toml --for-ci)
FAIL_UNDER=$(echo "$CONFIG_JSON" | jq -r '.fail_under')
CIVILIZED_PATHS=($(echo "$CONFIG_JSON" | jq -r '.civilized_paths[]'))
COVERAGE_PATHS=($(echo "$CONFIG_JSON" | jq -r '.coverage_paths[]'))
print_sub_header "覆盖率阈值: ${FAIL_UNDER}%"
print_sub_header "帝国总疆域: ${CIVILIZED_PATHS[*]}"
# ---------------------------------------------------
# --- 准备就绪 ---
# 根据 coverage_paths 生成 pytest 覆盖率参数列表
# ---------------------------------------------------
COV_ARGS=()
for path in "${COVERAGE_PATHS[@]}"; do
COV_ARGS+=("--cov=$path")
done
print_sub_header "军备清单(覆盖率源)已生成"
print_chapter_header "皇帝节仗 - 准备就绪"
# ==============================================================================
# 第一章:调用军刀执行静态大巡查
# ==============================================================================
print_chapter_header "第一章:静态大巡查"
# 审计范围:pyproject 的 civilized_paths 的内容
CIV_SOURCE_PATHS=("${CIVILIZED_PATHS[@]}")
for SCOPE in "${CIV_SOURCE_PATHS[@]}"; do
print_sub_header "派遣军刀对【${SCOPE}】进行静态审计..."
./qas.sh "${SCOPE}" || { print_error "❌军刀在 ${SCOPE} 静态审计失败"; exit 1; }
done
print_success "所有疆域静态审查全面通过!"
# ==============================================================================
# 第二章:帝国三军联合作战大演习
# 仅对审计目标(civilized_paths)关联的 tests 目录展开审计与测试。
# ==============================================================================
if [ "$ENABLE_TEST_PHASE" -eq 1 ]; then
print_chapter_header "第二章:帝国军团联合作战大演习"
# ---------------------------------------------------
# 智能推导测试目录
# ---------------------------------------------------
if [ ! -d "tests" ]; then
print_warning "未发现 tests 目录,跳过联合测试大演习。"
else
# 构建与 civilized_paths 相关联的测试目录列表。
# 策略:
# 对每个 SCOPE in CIV_SOURCE_PATHS:
# 1) 优先尝试严格镜像:tests/SCOPE
# 2) 其次尝试顶层镜像:tests/<top-level-of-SCOPE>
# 3) 若仍未命中,则退化为全局 tests/
TEST_DIRS=()
add_test_dir() {
local d="$1"
# 简单去重,避免对同一 tests 目录重复执行 TP/静态审计/pytest。
for existing in "${TEST_DIRS[@]}"; do
if [ "$existing" = "$d" ]; then
return
fi
done
TEST_DIRS+=("$d")
}
for SCOPE in "${CIV_SOURCE_PATHS[@]}"; do
# 1) 严格镜像
candidate="tests/${SCOPE}"
if [ -d "$candidate" ]; then
add_test_dir "$candidate"
continue
fi
# 2) 顶层目录镜像
top_level="${SCOPE%%/*}"
candidate="tests/${top_level}"
if [ -d "$candidate" ]; then
add_test_dir "$candidate"
else
# 3) 退化为全局 tests/
add_test_dir "tests"
fi
done
if [ "${#TEST_DIRS[@]}" -eq 0 ]; then
print_warning "未找到与 civilized_paths 匹配的测试目录,跳过联合测试大演习。"
else
for TDIR in "${TEST_DIRS[@]}"; do
print_sub_header "测试战区镜像: $TDIR"
# ---------------------------------------------
# 2.1 TP 系列军规审查(仅针对匹配到的测试战区)
# ---------------------------------------------
print_sub_header "2.1 帝国戎卫兵团军规审查 (TP 系列)"
poetry run python pycourt/judge.py "$TDIR" --select TP001,TP002,TP003 \
|| { print_error "❌ 测试战区 ${TDIR} 发现违反军规!"; exit 1; }
print_success "✅ 测试战区【${TDIR}】纯净度通过!"
# ---------------------------------------------
# 2.2 测试战区静态审计(复用军刀的静态能力)
# ---------------------------------------------
print_sub_header "2.2 派遣军刀对测试战区执行静态审计"
./qas.sh "$TDIR" || { print_error "❌ 军刀在测试战区 ${TDIR} 静态审计失败"; exit 1; }
print_success "✅ 测试战区【${TDIR}】静态审计通过!"
done
# ---------------------------------------------
# 2.3 常规军总决战 (单元 + 集成)
# ---------------------------------------------
print_sub_header "2.3 常规军总决战 (单元 + 集成)"
BATTLE_MARKER="unit or integration"
# 覆盖率阈值完全由开发者在 [tool.pycourt].coverage 中定义,并统一用于
# pytest 的 --cov-fail-under 参数:
# - FAIL_UNDER = 0 表示“只要测试运行通过即可”,不对覆盖率比例做约束;
# - FAIL_UNDER > 0 表示要求覆盖率至少达到该百分比,否则视为失败。
PYTEST_ARGS=("${COV_ARGS[@]}" --cov-report=term-missing --cov-report=html:htmlcov)
if [ "$FAIL_UNDER" -le 0 ]; then
print_warning "当前覆盖率阈值为 ${FAIL_UNDER}%,仅要求测试运行通过,不对覆盖率做约束。"
fi
PYTEST_ARGS+=(--cov-fail-under="$FAIL_UNDER")
poetry run pytest -m "$BATTLE_MARKER" \
"${PYTEST_ARGS[@]}" \
"${TEST_DIRS[@]}" || { print_error "❌ 常规军总决战失败!"; exit 1; }
print_success "覆盖率裁决通过,常规军总决战胜利!"
# ---------------------------------------------
# 2.4 特种部队演习 (E2E)
# ---------------------------------------------
if [ "$RUN_E2E" -eq 1 ]; then
print_sub_header "2.4 特种部队演习 (E2E)"
# E2E 测试不计入覆盖率,只做端到端验收。
poetry run pytest -m "e2e" --no-cov "${TEST_DIRS[@]}" \
|| { print_error "E2E 演习失败"; exit 1; }
print_success "E2E 演习成功!"
fi
fi
fi
fi
# ==============================================================================
# 终章:胜利宣告
# ==============================================================================
print_chapter_header "终章:胜利宣言"
print_success "帝国统一审计全面通过!代码值得信赖."
print_sub_header "可以放心推送了!"