330 lines
9.4 KiB
Bash
330 lines
9.4 KiB
Bash
#!/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
|