mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 02:22:26 +08:00
216 lines
6.9 KiB
Python
216 lines
6.9 KiB
Python
#!/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()
|