462d528bef
The ufw fix (prior commit) was necessary but insufficient. The DECISIVE blocker
was gitea webhook.ALLOWED_HOST_LIST = 'external, 10.100.0.0/16' (NH3 only) —
corviduo-dev is 10.250.50.152 (Anaheim), so gitea refused to deliver ('deny
10.250.50.152') and never opened the TCP connection. Fixed to 'external,
10.0.0.0/8' (whole fleet, matches the ufw choice) + gitea restart.
Listener now logs every delivery (source-IP/hmac_ok/ref/action) — the old
log_message=pass silence hid the whole failure. Proven end-to-end: real gitea
delivery -> hmac_ok=True, ref=main, 202 deploying -> green deploy.
59 lines
2.6 KiB
Python
59 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Gitea push-webhook listener for soong-lab: on a verified push to main, run
|
|
~/soong-lab-deploy.sh. HMAC-SHA256 (X-Gitea-Signature) vs ~/.config/soong/webhook-secret.
|
|
|
|
Logs each delivery (source IP / event / HMAC result / ref / action) to journald.
|
|
Added 2026-07-14 to diagnose the gitea->:9010 REAL-delivery path after the ufw fix
|
|
alone didn't restore auto-deploy (the prior `log_message = pass` made the listener
|
|
silent, so we couldn't see whether gitea was delivering / authenticating / ref-matching)."""
|
|
import hashlib, hmac, json, os, subprocess, sys, threading
|
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
|
|
SECRET = open(os.path.expanduser("~/.config/soong/webhook-secret"), "rb").read().strip()
|
|
DEPLOY = os.path.expanduser("~/soong-lab-deploy.sh")
|
|
STATUS = os.path.expanduser("~/.config/soong/last-deploy.json")
|
|
|
|
|
|
def _log(msg):
|
|
print(f"[webhook] {msg}", file=sys.stderr, flush=True)
|
|
|
|
|
|
class H(BaseHTTPRequestHandler):
|
|
def do_POST(self):
|
|
body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
|
|
recv = self.headers.get("X-Gitea-Signature", "")
|
|
mac = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
|
|
ok = hmac.compare_digest(mac, recv)
|
|
event = self.headers.get("X-Gitea-Event", "?")
|
|
_log(f"POST from {self.client_address[0]} event={event} clen={len(body)} "
|
|
f"sig_recv={recv[:12]!r} sig_exp={mac[:12]!r} hmac_ok={ok}")
|
|
if not ok:
|
|
_log("-> 401 bad signature (gitea-hook secret != listener secret)")
|
|
self.send_response(401); self.end_headers(); self.wfile.write(b"bad signature\n"); return
|
|
try:
|
|
ref = json.loads(body).get("ref", "")
|
|
except Exception:
|
|
_log("-> 400 bad json")
|
|
self.send_response(400); self.end_headers(); return
|
|
_log(f"ref={ref!r}")
|
|
if ref != "refs/heads/main":
|
|
_log("-> 200 ignored (ref != refs/heads/main)")
|
|
self.send_response(200); self.end_headers(); self.wfile.write(b"ignored " + ref.encode() + b"\n"); return
|
|
_log("-> 202 deploying")
|
|
self.send_response(202); self.end_headers(); self.wfile.write(b"deploying\n")
|
|
threading.Thread(target=lambda: subprocess.run(["bash", DEPLOY]), daemon=True).start()
|
|
|
|
def do_GET(self):
|
|
self.send_response(200); self.end_headers()
|
|
try:
|
|
st = open(STATUS).read().strip()
|
|
except Exception:
|
|
st = "no deploy yet"
|
|
self.wfile.write(b"soong-webhook ok | last: " + st.encode())
|
|
|
|
def log_message(self, *a):
|
|
pass
|
|
|
|
|
|
HTTPServer(("0.0.0.0", 9010), H).serve_forever()
|