333 lines
9.8 KiB
Bash
333 lines
9.8 KiB
Bash
#!/usr/bin/env sh
|
||
# CI 模板验证脚本
|
||
|
||
set -eu
|
||
|
||
echo "========================================"
|
||
echo "🔧 CI 模板验证"
|
||
echo "========================================"
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
PLAYBOOK_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||
TEMPLATES_DIR="$PLAYBOOK_ROOT/templates/ci"
|
||
|
||
VALIDATION_PASSED=0
|
||
VALIDATION_FAILED=0
|
||
ERRORS_FILE="/tmp/ci_template_validation_errors.txt"
|
||
REPORT_FILE="$SCRIPT_DIR/ci_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_yaml_syntax() {
|
||
local file="$1"
|
||
local description="$2"
|
||
|
||
if ! command -v yamllint >/dev/null 2>&1; then
|
||
echo " ⚠️ $description: 跳过(yamllint 未安装)"
|
||
return 0
|
||
fi
|
||
|
||
if yamllint -d relaxed "$file" >/dev/null 2>&1; then
|
||
echo " ✅ $description: YAML 语法正确"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
return 0
|
||
else
|
||
echo " ❌ $description: YAML 语法错误"
|
||
echo "YAML 语法错误: $file" >> "$ERRORS_FILE"
|
||
VALIDATION_FAILED=$((VALIDATION_FAILED + 1))
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
validate_workflow_structure() {
|
||
local file="$1"
|
||
|
||
echo " 📋 检查 workflow 结构:"
|
||
|
||
# 检查必要字段
|
||
if grep -q "^name:" "$file"; then
|
||
echo " ✓ 包含 name 字段"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
else
|
||
echo " ✗ 缺少 name 字段"
|
||
VALIDATION_FAILED=$((VALIDATION_FAILED + 1))
|
||
fi
|
||
|
||
if grep -q "^on:" "$file"; then
|
||
echo " ✓ 包含 on 触发器"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
else
|
||
echo " ✗ 缺少 on 触发器"
|
||
VALIDATION_FAILED=$((VALIDATION_FAILED + 1))
|
||
fi
|
||
|
||
if grep -q "^jobs:" "$file"; then
|
||
echo " ✓ 包含 jobs 定义"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
else
|
||
echo " ✗ 缺少 jobs 定义"
|
||
VALIDATION_FAILED=$((VALIDATION_FAILED + 1))
|
||
fi
|
||
|
||
# 检查 runs-on
|
||
if grep -q "runs-on:" "$file"; then
|
||
runner=$(grep "runs-on:" "$file" | head -1 | awk '{print $2}')
|
||
echo " ✓ 配置了 runner: $runner"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
else
|
||
echo " ✗ 缺少 runs-on 配置"
|
||
VALIDATION_FAILED=$((VALIDATION_FAILED + 1))
|
||
fi
|
||
|
||
# 检查 steps
|
||
if grep -q "steps:" "$file"; then
|
||
step_count=$(grep -c "^ - name:" "$file" || echo 0)
|
||
echo " ✓ 包含 $step_count 个步骤"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
else
|
||
echo " ✗ 缺少 steps 定义"
|
||
VALIDATION_FAILED=$((VALIDATION_FAILED + 1))
|
||
fi
|
||
}
|
||
|
||
# ============================================
|
||
# 查找并验证 Gitea workflow 文件
|
||
# ============================================
|
||
|
||
echo "🔍 查找 Gitea workflow 文件"
|
||
GITEA_WORKFLOWS_DIR="$TEMPLATES_DIR/gitea/.gitea/workflows"
|
||
|
||
if [ ! -d "$GITEA_WORKFLOWS_DIR" ]; then
|
||
echo "⚠️ Gitea workflows 目录不存在: $GITEA_WORKFLOWS_DIR"
|
||
echo "跳过 Gitea workflow 验证"
|
||
else
|
||
WORKFLOW_FILES=$(find "$GITEA_WORKFLOWS_DIR" -name "*.yml" -o -name "*.yaml" 2>/dev/null || true)
|
||
|
||
if [ -z "$WORKFLOW_FILES" ]; then
|
||
echo "⚠️ 未找到 Gitea workflow 文件"
|
||
else
|
||
for workflow in $WORKFLOW_FILES; do
|
||
echo ""
|
||
echo "🔍 验证 $(basename "$workflow")"
|
||
|
||
if validate_file_exists "$workflow" "$(basename "$workflow")"; then
|
||
# 验证 YAML 语法
|
||
validate_yaml_syntax "$workflow" "$(basename "$workflow")"
|
||
|
||
# 验证 workflow 结构
|
||
validate_workflow_structure "$workflow"
|
||
|
||
# 检查是否包含中文注释(符合项目风格)
|
||
if grep -q "# .*[\u4e00-\u9fa5]" "$workflow" 2>/dev/null || grep -qP "[\x{4e00}-\x{9fa5}]" "$workflow" 2>/dev/null; then
|
||
echo " ✓ 包含中文注释(符合项目风格)"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
fi
|
||
|
||
# 检查配置区域标记
|
||
if grep -q "# ====.*配置.*====" "$workflow"; then
|
||
echo " ✓ 包含配置区域标记"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
fi
|
||
|
||
# 检查环境变量配置
|
||
if grep -q "^env:" "$workflow"; then
|
||
echo " ✓ 包含环境变量配置"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
fi
|
||
fi
|
||
done
|
||
fi
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# ============================================
|
||
# 查找并验证 GitHub Actions workflow 文件
|
||
# ============================================
|
||
|
||
echo "🔍 查找 GitHub Actions workflow 文件"
|
||
GITHUB_WORKFLOWS_DIR="$TEMPLATES_DIR/github/.github/workflows"
|
||
|
||
if [ -d "$GITHUB_WORKFLOWS_DIR" ]; then
|
||
WORKFLOW_FILES=$(find "$GITHUB_WORKFLOWS_DIR" -name "*.yml" -o -name "*.yaml" 2>/dev/null || true)
|
||
|
||
if [ -n "$WORKFLOW_FILES" ]; then
|
||
for workflow in $WORKFLOW_FILES; do
|
||
echo ""
|
||
echo "🔍 验证 $(basename "$workflow")"
|
||
|
||
if validate_file_exists "$workflow" "$(basename "$workflow")"; then
|
||
# 验证 YAML 语法
|
||
validate_yaml_syntax "$workflow" "$(basename "$workflow")"
|
||
|
||
# 验证 workflow 结构
|
||
validate_workflow_structure "$workflow"
|
||
fi
|
||
done
|
||
fi
|
||
else
|
||
echo "ℹ️ GitHub Actions workflows 目录不存在(可选)"
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# ============================================
|
||
# 验证特定 workflow 模板
|
||
# ============================================
|
||
|
||
echo "🔍 验证特定 workflow 模板"
|
||
|
||
# 检查 standards-check workflow
|
||
STANDARDS_CHECK="$GITEA_WORKFLOWS_DIR/standards-check.yml"
|
||
if [ -f "$STANDARDS_CHECK" ]; then
|
||
echo ""
|
||
echo "📋 验证 standards-check.yml:"
|
||
|
||
# 检查是否包含格式化检查
|
||
if grep -q "格式化\|format" "$STANDARDS_CHECK"; then
|
||
echo " ✓ 包含格式化检查"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
fi
|
||
|
||
# 检查是否包含 lint 检查
|
||
if grep -q "lint\|检查" "$STANDARDS_CHECK"; then
|
||
echo " ✓ 包含 lint 检查"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
fi
|
||
else
|
||
echo "ℹ️ 未找到 standards-check.yml(可选)"
|
||
fi
|
||
|
||
# 检查 test workflow
|
||
TEST_WORKFLOW="$GITEA_WORKFLOWS_DIR/test.yml"
|
||
if [ -f "$TEST_WORKFLOW" ]; then
|
||
echo ""
|
||
echo "📋 验证 test.yml:"
|
||
|
||
# 检查是否包含测试步骤
|
||
if grep -q "测试\|test" "$TEST_WORKFLOW"; then
|
||
echo " ✓ 包含测试步骤"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
fi
|
||
|
||
# 检查是否配置了测试矩阵
|
||
if grep -q "strategy:" "$TEST_WORKFLOW" && grep -q "matrix:" "$TEST_WORKFLOW"; then
|
||
echo " ✓ 配置了测试矩阵"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
fi
|
||
else
|
||
echo "ℹ️ 未找到 test.yml(可选)"
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# ============================================
|
||
# 验证 README 文档
|
||
# ============================================
|
||
|
||
echo "🔍 验证 CI 模板文档"
|
||
|
||
if [ -d "$TEMPLATES_DIR" ]; then
|
||
# 查找 README
|
||
README_FILE=""
|
||
for name in README.md readme.md README README.txt; do
|
||
if [ -f "$TEMPLATES_DIR/$name" ]; then
|
||
README_FILE="$TEMPLATES_DIR/$name"
|
||
break
|
||
fi
|
||
done
|
||
|
||
if [ -n "$README_FILE" ]; then
|
||
echo " ✅ 找到说明文档: $(basename "$README_FILE")"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
|
||
# 检查文档内容
|
||
if grep -qi "使用\|usage\|how to" "$README_FILE"; then
|
||
echo " ✓ 包含使用说明"
|
||
VALIDATION_PASSED=$((VALIDATION_PASSED + 1))
|
||
fi
|
||
else
|
||
echo " ⚠️ 未找到 README 文档(建议添加)"
|
||
fi
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# ============================================
|
||
# 生成验证报告
|
||
# ============================================
|
||
|
||
echo "========================================"
|
||
echo "📊 验证结果统计"
|
||
echo "========================================"
|
||
echo "✅ 通过: $VALIDATION_PASSED"
|
||
echo "❌ 失败: $VALIDATION_FAILED"
|
||
|
||
if [ $((VALIDATION_PASSED + VALIDATION_FAILED)) -gt 0 ]; then
|
||
echo "📈 通过率: $(awk "BEGIN {printf \"%.1f\", ($VALIDATION_PASSED * 100.0) / ($VALIDATION_PASSED + $VALIDATION_FAILED)}")%"
|
||
else
|
||
echo "📈 通过率: N/A (无测试项)"
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# 写入报告文件
|
||
{
|
||
echo "CI 模板验证报告"
|
||
echo "===================="
|
||
echo ""
|
||
echo "验证时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo "模板目录: $TEMPLATES_DIR"
|
||
echo ""
|
||
echo "统计结果:"
|
||
echo " 通过: $VALIDATION_PASSED"
|
||
echo " 失败: $VALIDATION_FAILED"
|
||
if [ $((VALIDATION_PASSED + VALIDATION_FAILED)) -gt 0 ]; then
|
||
echo " 通过率: $(awk "BEGIN {printf \"%.1f\", ($VALIDATION_PASSED * 100.0) / ($VALIDATION_PASSED + $VALIDATION_FAILED)}")%"
|
||
fi
|
||
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 "✅ 所有 CI 模板验证通过"
|
||
exit 0
|
||
else
|
||
echo "❌ CI 模板验证失败 ($VALIDATION_FAILED 个错误)"
|
||
exit 1
|
||
fi
|