#!/usr/bin/env python3 """ CI 触发模块 - 重新运行失败 job、重跑整个 workflow、取消 run 支持操作: - rerun_failed: 重跑失败的 jobs - rerun_all: 重跑整个 workflow - cancel: 取消运行中的 run 用法: python3 scripts/ci/chatops/ci_trigger.py --run-id 456 --action rerun_failed python3 scripts/ci/chatops/ci_trigger.py --run-id 456 --action rerun_all python3 scripts/ci/chatops/ci_trigger.py --run-id 456 --action cancel 设计: - 与飞书机器人 /ci rerun 命令对接 - 操作前自动校验 run 状态,避免无效操作 """ import argparse import sys from . import config from .gitea_client import GiteaClient class CITrigger: """CI 操作触发器""" def __init__(self, gitea_client=None): self.gitea = gitea_client or GiteaClient() # ── 触发操作 ───────────────────────────────────── def rerun_failed(self, run_id): """重跑失败的 jobs Returns: dict: {success, message, new_run_id?} """ run = self.gitea.get_run(run_id) if not run: return {"success": False, "message": f"Run {run_id} 不存在"} status = run.get("status", "") if status != "completed": return { "success": False, "message": f"Run {run_id} 当前状态为 {status},仅 completed 状态才能重跑", } result = self.gitea.rerun_failed_jobs(run_id) if result is None: return {"success": False, "message": "重跑请求失败"} # Gitea rerun 后返回的 run id 通常不变(复用原 run) return { "success": True, "message": f"已触发重跑失败 jobs: Run #{run_id}", "run_id": run_id, "run_url": f"{config.GITEA_URL}/{config.GITEA_REPO}/actions/runs/{run_id}", } def rerun_all(self, run_id): """重跑整个 workflow run Returns: dict: {success, message, run_id, run_url} """ run = self.gitea.get_run(run_id) if not run: return {"success": False, "message": f"Run {run_id} 不存在"} status = run.get("status", "") if status == "running" or status == "pending": return { "success": False, "message": f"Run {run_id} 正在运行中,无需重跑", } result = self.gitea.rerun_run(run_id) if result is None: return {"success": False, "message": "重跑请求失败"} return { "success": True, "message": f"已触发完整重跑: Run #{run_id}", "run_id": run_id, "run_url": f"{config.GITEA_URL}/{config.GITEA_REPO}/actions/runs/{run_id}", } def cancel_run(self, run_id): """取消运行中的 run Returns: dict: {success, message} """ run = self.gitea.get_run(run_id) if not run: return {"success": False, "message": f"Run {run_id} 不存在"} status = run.get("status", "") if status == "completed": return { "success": False, "message": f"Run {run_id} 已完成,无需取消", } result = self.gitea.cancel_run(run_id) if result is None: return {"success": False, "message": "取消请求失败"} return { "success": True, "message": f"已取消 Run #{run_id}", "run_id": run_id, } def rerun_latest_failed(self, branch="develop", workflow_id=None): """重跑指定分支最近一次失败的 run 用于快速恢复场景,不需要先查 run_id """ runs, _ = self.gitea.list_runs(branch=branch, workflow_id=workflow_id, status="failure", limit=5) if not runs: return {"success": False, "message": f"{branch} 分支没有失败的 run"} latest = runs[0] run_id = latest.get("id") return self.rerun_failed(run_id) # ── CLI 入口 ────────────────────────────────────────── def main(): parser = argparse.ArgumentParser(description="CI 触发操作") parser.add_argument("--run-id", required=True, help="Workflow Run ID") parser.add_argument( "--action", required=True, choices=["rerun_failed", "rerun_all", "cancel"], help="操作类型", ) args = parser.parse_args() trigger = CITrigger() if args.action == "rerun_failed": result = trigger.rerun_failed(args.run_id) elif args.action == "rerun_all": result = trigger.rerun_all(args.run_id) elif args.action == "cancel": result = trigger.cancel_run(args.run_id) else: print(f"未知操作: {args.action}") return 1 status = "✅" if result["success"] else "❌" print(f"{status} {result['message']}") if result.get("run_url"): print(f" {result['run_url']}") return 0 if result["success"] else 1 if __name__ == "__main__": sys.exit(main())