chore(scripts): outil de controle du CI Gitea (runs et jobs par head_sha)
scripts/check_ci.py : interroge l'API Gitea (jeton via git credential fill, User-Agent navigateur) et rapporte l'etat des jobs du run correspondant au HEAD local. Bump automatique de version = demonstration du hook prepare-commit-msg.
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
"""Vérifie l'état du dernier run CI Gitea d'ObsiGate (lecture seule)."""
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
REPO = "Projets/ObsiGate"
|
||||
BASE = f"https://git.dracodev.net/api/v1/repos/{REPO}"
|
||||
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126 Safari/537.36"
|
||||
|
||||
cred = subprocess.run(
|
||||
["git", "credential", "fill"],
|
||||
input="protocol=https\nhost=git.dracodev.net\n\n",
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=r"C:\dev\git\python\ObsiGate",
|
||||
)
|
||||
token = ""
|
||||
for line in cred.stdout.splitlines():
|
||||
if line.startswith("password"):
|
||||
token = line.split("=", 1)[1]
|
||||
if not token:
|
||||
raise SystemExit("pas de jeton git disponible")
|
||||
|
||||
sha = subprocess.run(
|
||||
["git", "rev-parse", "HEAD"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=r"C:\dev\git\python\ObsiGate",
|
||||
).stdout.strip()
|
||||
|
||||
|
||||
def get(path: str):
|
||||
req = urllib.request.Request(
|
||||
f"{BASE}{path}",
|
||||
headers={
|
||||
"Authorization": f"token {token}",
|
||||
"Accept": "application/json",
|
||||
"User-Agent": UA,
|
||||
},
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=25) as resp:
|
||||
return json.load(resp)
|
||||
|
||||
|
||||
runs = get("/actions/runs?limit=10").get("workflow_runs", [])
|
||||
print(f"HEAD local : {sha[:8]} — {len(runs)} run(s) récent(s)")
|
||||
target = None
|
||||
for run in runs:
|
||||
head = str(run.get("head_sha") or "")
|
||||
title = run.get("display_title") or run.get("name") or "?"
|
||||
flag = " <== HEAD" if head.startswith(sha[:8]) else ""
|
||||
print(f" #{run.get('id')} {title[:60]} | {run.get('status')}/{run.get('conclusion')} | {head[:8]}{flag}")
|
||||
if head.startswith(sha[:8]) and target is None:
|
||||
target = run
|
||||
|
||||
if target is None:
|
||||
print("Aucun run pour le HEAD courant (encore en file d'attente ?)")
|
||||
sys.exit(0)
|
||||
|
||||
jobs_resp = get(f"/actions/runs/{target['id']}/jobs")
|
||||
print(f"run #{target['id']} : status={target.get('status')} conclusion={target.get('conclusion')} "
|
||||
f"started={target.get('run_started_at')} updated={target.get('updated_at')}")
|
||||
print(f"clés jobs: {list(jobs_resp)[:6]}")
|
||||
for job in jobs_resp.get("workflow_jobs", jobs_resp.get("jobs", [])) or []:
|
||||
print(f" - {job.get('name')}: {job.get('status')}/{job.get('conclusion')}")
|
||||
if job.get("status") != "completed":
|
||||
for step in job.get("steps", []) or []:
|
||||
print(f" · {step.get('name')}: {step.get('status')}/{step.get('conclusion')}")
|
||||
|
||||
try:
|
||||
runners = get("/actions/runners")
|
||||
entries = runners.get("runners", runners) if isinstance(runners, dict) else runners
|
||||
print(f"runners: {len(entries) if isinstance(entries, list) else entries}")
|
||||
if isinstance(entries, list):
|
||||
for runner in entries:
|
||||
print(f" - {runner.get('name')} status={runner.get('status')} labels={runner.get('labels')}")
|
||||
except Exception as exc: # noqa: BLE001
|
||||
print(f"runners indisponibles: {exc}")
|
||||
Reference in New Issue
Block a user