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.
80 lines
2.8 KiB
Python
80 lines
2.8 KiB
Python
"""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}")
|