KeyHunter parser v10 + 18 scanners + tests (synthetic fixtures only, no keys/db/tokens)

This commit is contained in:
2026-10-20 07:50:07 +00:00
commit a5008d837b
178 changed files with 36909 additions and 0 deletions

106
fleet_dispatch.py Executable file
View File

@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""Dispatch imba.yml workflow on selected fleet nodes (with SSL retries).
SAFEGATE: requires explicit --node, --limit, or --all. No args = dry-run + exit.
Usage examples:
python fleet_dispatch.py # dry-run: print node count, exit
python fleet_dispatch.py --node imba-node-0002
python fleet_dispatch.py --limit 10
python fleet_dispatch.py --all
"""
import os
KF_BASE = os.path.dirname(os.path.abspath(__file__))
os.makedirs(os.path.join(KF_BASE, "data"), exist_ok=True)
import argparse
import concurrent.futures as cf
import json
import time
import sys
import requests
from requests.adapters import HTTPAdapter, Retry
DEPLOY = os.path.join(KF_BASE, "data/fleet_deploy.json")
H = {"Accept": "application/vnd.github+json"}
s = requests.Session()
s.trust_env = False
s.mount("https://", HTTPAdapter(max_retries=Retry(total=5, backoff_factor=1)))
def dispatch(rec):
full = f"{rec['login']}/{rec['repo']}"
h = {**H, "Authorization": f"Bearer {rec['pat']}"}
for attempt in range(3):
try:
r = s.post(
f"https://api.github.com/repos/{full}/actions/workflows/imba.yml/dispatches",
headers=h, json={"ref": "main"}, timeout=30)
if r.status_code in (201, 204):
return full, True, r.status_code
return full, False, f"{r.status_code} {r.text[:120]}"
except Exception as e:
if attempt == 2:
return full, False, str(e)[:120]
time.sleep(3)
def main():
ap = argparse.ArgumentParser(
description="Dispatch imba.yml workflow on fleet nodes.",
epilog="Without --node/--limit/--all: dry-run + exit (no dispatch).")
target = ap.add_mutually_exclusive_group()
target.add_argument("--node", metavar="REPO",
help="exact repo name or login/repo")
target.add_argument("--limit", type=int, metavar="N",
help="first N ok nodes")
target.add_argument("--all", action="store_true",
help="ALL ok nodes (1391)")
ap.add_argument("--offset", type=int, default=0,
help="starting index")
ap.add_argument("--dry-run", action="store_true",
help="print target count and exit")
args, _ = ap.parse_known_args()
recs = [r for r in json.load(open(DEPLOY, encoding="utf-8")) if r.get("ok")]
if args.node:
recs = [r for r in recs
if r.get("repo") == args.node
or f"{r.get('login')}/{r.get('repo')}" == args.node]
elif args.limit:
recs = recs[args.offset:args.offset + args.limit]
elif args.all:
recs = recs[args.offset:] # support offset even with --all
else:
# dry-run default when no explicit target
print(f"fleet_dispatch.py SAFEGATE: no target specified.\n"
f" --node REPO | --limit N | --all required for live dispatch.\n"
f" ok nodes available: {len(recs)}\n"
f" (dry-run; use --dry-run to see target count)")
sys.exit(0)
if args.dry_run or not (args.node or args.limit or args.all):
print(f"DRY-RUN: would dispatch {len(recs)} nodes")
return
if not recs:
sys.exit("no nodes selected (--node/--limit matched nothing)")
print(f"dispatching {len(recs)} nodes", flush=True)
ok = 0
t0 = time.time()
with cf.ThreadPoolExecutor(max_workers=12) as ex:
for i, (full, good, info) in enumerate(ex.map(dispatch, recs), 1):
ok += good
if not good:
print(f" FAIL {full}: {info}", flush=True)
if i % 50 == 0:
# ":.0s" is not a format code: every dispatch died here on the
# final summary (and on the per-50 progress line), after the
# ex.map loop had already run, so runs were dispatched but the
# ok/total was never printed and the exit code was a traceback.
print(f" [{i}/{len(recs)}] ok={ok} {time.time()-t0:.0f}s", flush=True)
print(f"\nDONE {time.time()-t0:.0f}s: {ok}/{len(recs)} dispatched", flush=True)
if __name__ == "__main__":
main()