#!/usr/bin/env python3 """Service d'écoute du webhook Gitea pour www.alpinux.org. N'écoute que sur la boucle locale : c'est Apache qui lui transmet les requêtes reçues sur https://alpinux.org/deploy. La signature HMAC envoyée par Gitea est vérifiée avant de lancer quoi que ce soit, et seuls les push sur main déclenchent un déploiement. Calqué sur le service du wiki (/opt/wiki-mkdocs/webhook.py), à trois différences près : le port, le script appelé, et le secret qui vient d'un fichier d'environnement plutôt que de l'unité systemd — ce dépôt est public. """ import hashlib import hmac import http.server import json import os import subprocess import threading SECRET = os.environ.get("WWW_WEBHOOK_SECRET", "") DEPLOY_SCRIPT = "/opt/www-alpinux/deploy-www.sh" LOG = "/var/log/www-deploy.log" PORT = 9877 class Handler(http.server.BaseHTTPRequestHandler): def do_POST(self): if self.path != "/deploy": self.send_response(404); self.end_headers(); return length = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(length) signature = self.headers.get("X-Gitea-Signature", "") attendue = hmac.new(SECRET.encode(), body, hashlib.sha256).hexdigest() if not SECRET or not hmac.compare_digest(attendue, signature): self.send_response(403); self.end_headers() self.wfile.write(b"Forbidden"); return ref = json.loads(body).get("ref", "") if ref not in ("refs/heads/main", "refs/heads/master"): self.send_response(200); self.end_headers() self.wfile.write(b"Skipped: not main"); return # Déploiement en tâche de fond : Gitea a sa réponse tout de suite. threading.Thread( target=subprocess.run, args=([DEPLOY_SCRIPT],), kwargs={"capture_output": True}, daemon=True, ).start() self.send_response(200); self.end_headers() self.wfile.write(b"Deploy triggered") def log_message(self, fmt, *args): with open(LOG, "a") as journal: journal.write(self.log_date_time_string() + " " + fmt % args + "\n") if __name__ == "__main__": http.server.HTTPServer(("127.0.0.1", PORT), Handler).serve_forever()