mcp-feedback-enhanced/scripts/validate_workflows.py
2025-06-15 18:17:45 +08:00

216 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
GitHub Actions 工作流程驗證腳本
此腳本驗證 GitHub Actions 工作流程文件的語法和配置正確性。
"""
import sys
from pathlib import Path
import yaml
def validate_yaml_syntax(file_path: Path) -> bool:
"""驗證 YAML 文件語法"""
try:
with open(file_path, encoding="utf-8") as f:
yaml.safe_load(f)
print(f"{file_path.name}: YAML 語法正確")
return True
except yaml.YAMLError as e:
print(f"{file_path.name}: YAML 語法錯誤 - {e}")
return False
except Exception as e:
print(f"{file_path.name}: 讀取文件失敗 - {e}")
return False
def validate_workflow_structure(file_path: Path) -> bool:
"""驗證工作流程結構"""
try:
with open(file_path, encoding="utf-8") as f:
workflow = yaml.safe_load(f)
# 檢查是否成功解析
if workflow is None:
print(f"{file_path.name}: 文件為空或解析失敗")
return False
# 檢查必需的頂級字段
# 注意YAML 會將 'on' 解析為 True所以我們需要特殊處理
required_fields = ["name", "jobs"]
for field in required_fields:
if field not in workflow:
print(f"{file_path.name}: 缺少必需字段 '{field}'")
print(f" 實際字段: {list(workflow.keys())}")
return False
# 檢查 'on' 字段(可能被解析為 True
if "on" not in workflow and True not in workflow:
print(f"{file_path.name}: 缺少觸發條件 'on'")
print(f" 實際字段: {list(workflow.keys())}")
return False
# 檢查 jobs 結構
if not isinstance(workflow["jobs"], dict):
print(f"{file_path.name}: 'jobs' 必須是字典")
return False
if not workflow["jobs"]:
print(f"{file_path.name}: 'jobs' 不能為空")
return False
print(f"{file_path.name}: 工作流程結構正確")
return True
except Exception as e:
print(f"{file_path.name}: 結構驗證失敗 - {e}")
return False
def validate_build_desktop_workflow(file_path: Path) -> bool:
"""驗證桌面構建工作流程的特定配置"""
try:
with open(file_path, encoding="utf-8") as f:
workflow = yaml.safe_load(f)
# 檢查 matrix 配置
build_job = workflow["jobs"].get("build-desktop", {})
strategy = build_job.get("strategy", {})
matrix = strategy.get("matrix", {})
if "include" not in matrix:
print(f"{file_path.name}: 缺少 matrix.include 配置")
return False
# 檢查平台配置
platforms = matrix["include"]
expected_platforms = {"windows", "macos-intel", "macos-arm64", "linux"}
actual_platforms = {item.get("name") for item in platforms}
if not expected_platforms.issubset(actual_platforms):
missing = expected_platforms - actual_platforms
print(f"{file_path.name}: 缺少平台配置: {missing}")
return False
print(f"{file_path.name}: 桌面構建配置正確")
return True
except Exception as e:
print(f"{file_path.name}: 桌面構建驗證失敗 - {e}")
return False
def validate_publish_workflow(file_path: Path) -> bool:
"""驗證發佈工作流程的特定配置"""
try:
with open(file_path, encoding="utf-8") as f:
workflow = yaml.safe_load(f)
# 檢查輸入參數 - 注意 'on' 可能被解析為 True
on_section = workflow.get("on") or workflow.get(True)
if not on_section:
print(f"{file_path.name}: 找不到觸發條件")
return False
workflow_dispatch = on_section.get("workflow_dispatch", {})
inputs = workflow_dispatch.get("inputs", {})
required_inputs = {"version_type", "include_desktop"}
actual_inputs = set(inputs.keys())
if not required_inputs.issubset(actual_inputs):
missing = required_inputs - actual_inputs
print(f"{file_path.name}: 缺少輸入參數: {missing}")
print(f" 實際輸入參數: {actual_inputs}")
return False
# 檢查是否有桌面應用處理步驟
release_job = workflow["jobs"].get("release", {})
steps = release_job.get("steps", [])
has_desktop_steps = any(
"desktop" in step.get("name", "").lower() for step in steps
)
if not has_desktop_steps:
print(f"{file_path.name}: 缺少桌面應用處理步驟")
return False
print(f"{file_path.name}: 發佈工作流程配置正確")
return True
except Exception as e:
print(f"{file_path.name}: 發佈工作流程驗證失敗 - {e}")
return False
def main():
"""主函數"""
print("🔍 驗證 GitHub Actions 工作流程...")
print()
# 獲取工作流程目錄
workflows_dir = Path(__file__).parent.parent / ".github" / "workflows"
if not workflows_dir.exists():
print(f"❌ 工作流程目錄不存在: {workflows_dir}")
sys.exit(1)
# 查找所有工作流程文件
workflow_files = list(workflows_dir.glob("*.yml")) + list(
workflows_dir.glob("*.yaml")
)
if not workflow_files:
print(f"❌ 在 {workflows_dir} 中沒有找到工作流程文件")
sys.exit(1)
print(f"📁 找到 {len(workflow_files)} 個工作流程文件")
print()
# 驗證每個文件
all_valid = True
for workflow_file in sorted(workflow_files):
print(f"🔍 驗證 {workflow_file.name}...")
# 基本語法驗證
if not validate_yaml_syntax(workflow_file):
all_valid = False
continue
# 結構驗證
if not validate_workflow_structure(workflow_file):
all_valid = False
continue
# 特定工作流程驗證
if workflow_file.name == "build-desktop.yml":
if not validate_build_desktop_workflow(workflow_file):
all_valid = False
elif workflow_file.name == "publish.yml":
if not validate_publish_workflow(workflow_file):
all_valid = False
print()
# 總結
if all_valid:
print("🎉 所有工作流程文件驗證通過!")
print()
print("📋 下一步:")
print(" 1. 提交並推送更改到 GitHub")
print(" 2. 測試 'Build Desktop Applications' 工作流程")
print(" 3. 測試 'Build Desktop & Release' 工作流程")
print(" 4. 驗證桌面應用是否正確包含在發佈中")
else:
print("❌ 部分工作流程文件驗證失敗")
print("請修復上述問題後重新運行驗證")
sys.exit(1)
if __name__ == "__main__":
main()