mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 18:52:27 +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()
|