#!/usr/bin/env sh # Python 模板验证脚本 set -eu echo "========================================" echo "🐍 Python 模板验证" echo "========================================" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PLAYBOOK_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" TEMPLATES_DIR="$PLAYBOOK_ROOT/templates/python" VALIDATION_PASSED=0 VALIDATION_FAILED=0 ERRORS_FILE="/tmp/python_template_validation_errors.txt" REPORT_FILE="$SCRIPT_DIR/python_validation_report.txt" > "$ERRORS_FILE" > "$REPORT_FILE" echo "📁 模板目录: $TEMPLATES_DIR" echo "" # ============================================ # 辅助函数 # ============================================ validate_file_exists() { local file="$1" local description="$2" if [ -f "$file" ]; then echo " ✅ $description: $(basename "$file")" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) return 0 else echo " ❌ $description: $(basename "$file") - 文件不存在" echo "文件不存在: $file" >> "$ERRORS_FILE" VALIDATION_FAILED=$((VALIDATION_FAILED + 1)) return 1 fi } validate_toml_syntax() { local file="$1" local description="$2" if ! command -v python3 >/dev/null 2>&1; then echo " ⚠️ $description: 跳过(Python3 未安装)" return 0 fi if python3 << EOF import sys try: import tomli except ImportError: try: import tomllib as tomli except ImportError: import toml as tomli try: with open("$file", "rb") as f: tomli.load(f) sys.exit(0) except Exception as e: print(f"TOML 语法错误: {e}", file=sys.stderr) sys.exit(1) EOF then echo " ✅ $description: TOML 语法正确" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) return 0 else echo " ❌ $description: TOML 语法错误" echo "TOML 语法错误: $file" >> "$ERRORS_FILE" VALIDATION_FAILED=$((VALIDATION_FAILED + 1)) return 1 fi } validate_required_sections() { local file="$1" local sections="$2" for section in $sections; do if grep -q "^\[$section\]" "$file"; then echo " ✓ 包含 [$section] 配置" else echo " ✗ 缺少 [$section] 配置" echo "缺少配置节: $section in $file" >> "$ERRORS_FILE" VALIDATION_FAILED=$((VALIDATION_FAILED + 1)) fi done } # ============================================ # 验证 pyproject.toml # ============================================ echo "🔍 验证 pyproject.toml" PYPROJECT="$TEMPLATES_DIR/pyproject.toml" if validate_file_exists "$PYPROJECT" "pyproject.toml"; then # 验证 TOML 语法 validate_toml_syntax "$PYPROJECT" "pyproject.toml" # 验证必要的配置节 echo " 📋 检查必要配置节:" validate_required_sections "$PYPROJECT" "tool.black tool.isort tool.pytest.ini_options" # 验证 black 配置 if grep -q "line-length = 80" "$PYPROJECT"; then echo " ✓ black line-length 配置正确" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) else echo " ✗ black line-length 配置缺失或不正确" VALIDATION_FAILED=$((VALIDATION_FAILED + 1)) fi # 验证 isort 配置 if grep -q "profile = \"google\"" "$PYPROJECT"; then echo " ✓ isort profile 配置正确" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) else echo " ✗ isort profile 配置缺失或不正确" VALIDATION_FAILED=$((VALIDATION_FAILED + 1)) fi fi echo "" # ============================================ # 验证 .flake8 # ============================================ echo "🔍 验证 .flake8" FLAKE8="$TEMPLATES_DIR/.flake8" if validate_file_exists "$FLAKE8" ".flake8"; then # 验证 [flake8] 节 if grep -q "^\[flake8\]" "$FLAKE8"; then echo " ✓ 包含 [flake8] 配置" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) # 验证 max-line-length if grep -q "^max-line-length" "$FLAKE8"; then echo " ✓ 配置了 max-line-length" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) else echo " ✗ 缺少 max-line-length 配置" VALIDATION_FAILED=$((VALIDATION_FAILED + 1)) fi # 验证 extend-ignore if grep -q "^extend-ignore" "$FLAKE8" || grep -q "^ignore" "$FLAKE8"; then echo " ✓ 配置了错误忽略规则" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) fi else echo " ✗ 缺少 [flake8] 配置节" VALIDATION_FAILED=$((VALIDATION_FAILED + 1)) fi fi echo "" # ============================================ # 验证 .pylintrc # ============================================ echo "🔍 验证 .pylintrc" PYLINTRC="$TEMPLATES_DIR/.pylintrc" if validate_file_exists "$PYLINTRC" ".pylintrc"; then # 验证关键配置节 for section in "MASTER" "MESSAGES CONTROL" "FORMAT"; do if grep -qi "^\[$section\]" "$PYLINTRC"; then echo " ✓ 包含 [$section] 配置" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) else echo " ✗ 缺少 [$section] 配置" VALIDATION_FAILED=$((VALIDATION_FAILED + 1)) fi done # 验证 max-line-length if grep -q "^max-line-length" "$PYLINTRC"; then echo " ✓ 配置了 max-line-length" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) fi fi echo "" # ============================================ # 验证 .pre-commit-config.yaml # ============================================ echo "🔍 验证 .pre-commit-config.yaml" PRECOMMIT="$TEMPLATES_DIR/.pre-commit-config.yaml" if validate_file_exists "$PRECOMMIT" ".pre-commit-config.yaml"; then # 验证 YAML 语法 if command -v yamllint >/dev/null 2>&1; then if yamllint -d relaxed "$PRECOMMIT" >/dev/null 2>&1; then echo " ✓ YAML 语法正确" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) else echo " ✗ YAML 语法错误" VALIDATION_FAILED=$((VALIDATION_FAILED + 1)) fi fi # 验证包含 pre-commit hooks if grep -q "^repos:" "$PRECOMMIT"; then echo " ✓ 包含 repos 配置" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) # 检查常用 hooks for hook in "black" "isort" "flake8"; do if grep -q "$hook" "$PRECOMMIT"; then echo " ✓ 配置了 $hook hook" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) fi done fi fi echo "" # ============================================ # 验证 .editorconfig # ============================================ echo "🔍 验证 .editorconfig" EDITORCONFIG="$TEMPLATES_DIR/.editorconfig" if validate_file_exists "$EDITORCONFIG" ".editorconfig"; then # 验证包含 root 标记 if grep -q "^root = true" "$EDITORCONFIG"; then echo " ✓ 包含 root = true" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) fi # 验证 Python 配置节 if grep -q "^\[\*.py\]" "$EDITORCONFIG" || grep -q "^\[*.py\]" "$EDITORCONFIG"; then echo " ✓ 包含 Python 文件配置" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) fi fi echo "" # ============================================ # 验证 VSCode 配置 # ============================================ echo "🔍 验证 .vscode/settings.json" VSCODE_SETTINGS="$TEMPLATES_DIR/.vscode/settings.json" if validate_file_exists "$VSCODE_SETTINGS" "settings.json"; then # 验证 JSON 语法 if command -v python3 >/dev/null 2>&1; then if python3 -m json.tool "$VSCODE_SETTINGS" >/dev/null 2>&1; then echo " ✓ JSON 语法正确" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) else echo " ✗ JSON 语法错误" VALIDATION_FAILED=$((VALIDATION_FAILED + 1)) fi fi # 验证 Python 相关配置 if grep -q "python" "$VSCODE_SETTINGS"; then echo " ✓ 包含 Python 配置" VALIDATION_PASSED=$((VALIDATION_PASSED + 1)) fi fi echo "" # ============================================ # 生成验证报告 # ============================================ echo "========================================" echo "📊 验证结果统计" echo "========================================" echo "✅ 通过: $VALIDATION_PASSED" echo "❌ 失败: $VALIDATION_FAILED" echo "📈 通过率: $(awk "BEGIN {printf \"%.1f\", ($VALIDATION_PASSED * 100.0) / ($VALIDATION_PASSED + $VALIDATION_FAILED)}")%" echo "" # 写入报告文件 { echo "Python 模板验证报告" echo "====================" echo "" echo "验证时间: $(date '+%Y-%m-%d %H:%M:%S')" echo "模板目录: $TEMPLATES_DIR" echo "" echo "统计结果:" echo " 通过: $VALIDATION_PASSED" echo " 失败: $VALIDATION_FAILED" echo " 通过率: $(awk "BEGIN {printf \"%.1f\", ($VALIDATION_PASSED * 100.0) / ($VALIDATION_PASSED + $VALIDATION_FAILED)}")%" echo "" if [ -s "$ERRORS_FILE" ]; then echo "错误详情:" cat "$ERRORS_FILE" fi } > "$REPORT_FILE" echo "📄 详细报告: $REPORT_FILE" echo "========================================" # 清理临时文件 rm -f "$ERRORS_FILE" # 返回结果 if [ "$VALIDATION_FAILED" -eq 0 ]; then echo "✅ 所有 Python 模板验证通过" exit 0 else echo "❌ Python 模板验证失败 ($VALIDATION_FAILED 个错误)" exit 1 fi