25d895d 🐛 fix(gitea_workflow): clean up temp repos after job steps 2bc3b11 🐛 fix(gitea_workflow): clean up temporary repo dirs in workflows 98c3f30 📝 docs(agent_rules): allow plan execution on current branch 16c7230 📝 docs(prompts): define custom verify layering 8efc4dd 🐛 fix(skills): quote commit-message description bc8498f 🐛 fix(ci): install tomli for gitea tests 55cda3b 🐛 fix(tests): report missing toml parser clearly c0729c7 🐛 fix(playbook): import Optional for cli compatibility 63e24bf 📦 deps(skills): sync thirdparty skills d2f9356 🐛 fix(ci): isolate gitea workflow repos 588b81d 🐛 fix(ci): inline gitea workflow bootstrap e0b1c3a ♻️ refactor(skills): standardize first-party skill contracts 2c5050d ♻️ refactor(skills): rename repo skills source dir f049dfb 📦 deps(skills): drop duplicate first-party superpowers skills 234b335 ✨ feat(workflow): add superpowers planning and execution state tracking c1702a6 📝 docs(markdown): format tracked markdown and drop stale templates 2325409 📝 docs(markdown): clarify optional markdownlint usage 214c44e 🔧 chore(markdown): add markdownlint baseline and lint fixes a22b324 📝 docs(templates): add execution and memory-bank prompt templates 223a797 📝 docs(templates): update README for Claude Code and current features 4ac8672 📝 docs: simplify README + platform-agnostic tools + auto-create local rules 2431c9d 📝 docs: add claude_md config and use cross-platform paths d64b248 📝 docs: fix README.md inaccuracies and add Claude Code info c8d6bf2 🐛 fix(playbook): use relative paths in CLAUDE.md when not at project root 6518f0f ✨ feat(playbook): auto-create CLAUDE.md with path discovery 6ec9a45 ✨ feat(skills): add skill_link symlink support + platform-agnostic prompt 9f8b6b5 📝 docs: update README and config example for Claude Code support 79cff6c 📝 docs(skills): add Claude Code platform support 452c6f5 ✨ feat(playbook): auto-inject AGENTS.md into CLAUDE.md e1dbf3c 🐛 fix(skills): remove dual-path from commit-message skill f3a7259 🔧 chore(ci): use prepare_repo.sh in both workflows da08212 🔧 chore(ci): extract prepare_repo.sh and clean up workflows 7ade85e 🗑️ remove(tsl): drop syntax_book/, data/ source and build script f94dba0 ♻️ refactor(skills): update playbook.py and tests for thirdparty/ layout b3df412 ♻️ refactor(skills): separate thirdparty skills into thirdparty/ subdirectory 64950e7 📦 deps(skills): sync thirdparty skills a2e3cb0 ✨ feat(playbook): add no_backup deploy controls 8609d59 🐛 fix(docs): repair reference catalog source links 956da11 🐛 fix(playbook): publish hidden ci test fixes 3f67754 📦 deps(skills): sync thirdparty skills 08ca87b 📦 deps(skills): add karpathy thirdparty sync 96b705b 📝 docs(tsl): rebuild canonical syntax and routing manual 3ed5052 📦 deps(skills): sync thirdparty skills 60108dd 📦 deps(skills): sync thirdparty skills da85d4e 🐛 fix(thirdparty): prune nested project snapshots a2a697e 📦 deps(skills): sync thirdparty skills 9df610a 🐛 fix(thirdparty): exclude duplicated superpowers skills 33dd5bb 🐛 fix(thirdparty): preserve optional manifest fields 91b0ea7 🐛 fix(thirdparty): preserve manifest during snapshot update 2e26f98 🔧 chore(thirdparty): generalize skills sync pipeline 5b9c1e3 📦 deps(skills): sync superpowers 2f2d34a 📝 docs(readme): normalize subtree command spacing 62db7db 🐛 fix(ci): serialize superpowers update and sync 3463223 🐛 fix(ci): use literal superpowers sync paths 48f6de8 📦 deps(skills): sync superpowers 4b23529 🔧 chore(ci): merge superpowers update and sync workflow a56d75b 📦 deps(skills): sync superpowers 84bcefa 🔧 chore(ci): use ci[bot] commit author name 00a07e5 📦 deps(skills): sync superpowers 7b84daf 🐛 fix(templates): enforce main loop progress tracking 51373d7 🔧 chore(ci): automate superpowers sync workflow eaaa39c 🐛 fix(ci): prevent stale superpowers sync from restoring skills block 79755c6 📦 deps(skills): sync superpowers 836d878 📦 deps(skills): sync superpowers 8216c9f 📦 deps(skills): sync superpowers 9439505 🐛 fix(playbook): address reported repo issues git-subtree-dir: docs/standards/playbook git-subtree-split: 25d895d8b3f56624ccfe99ad7289e9eb49e0f316
340 lines
9.7 KiB
Bash
340 lines
9.7 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
|
||
parser = None
|
||
for module_name in ("tomli", "tomllib", "toml"):
|
||
try:
|
||
parser = __import__(module_name)
|
||
break
|
||
except ImportError:
|
||
continue
|
||
|
||
if parser is None:
|
||
print("缺少 TOML 解析器(需要 tomli、tomllib 或 toml)", file=sys.stderr)
|
||
sys.exit(2)
|
||
|
||
try:
|
||
with open("$file", "rb") as f:
|
||
parser.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
|
||
status=$?
|
||
if [ "$status" -eq 2 ]; then
|
||
echo " ❌ $description: 缺少 TOML 解析器"
|
||
echo "缺少 TOML 解析器: $file" >> "$ERRORS_FILE"
|
||
else
|
||
echo " ❌ $description: TOML 语法错误"
|
||
echo "TOML 语法错误: $file" >> "$ERRORS_FILE"
|
||
fi
|
||
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
|