#!/usr/bin/env python3 """Gitea webhook listener — auto-deploys supersam on push to main.""" import hmac, hashlib, subprocess, json, os, logging from flask import Flask, request, abort app = Flask(__name__) SECRET = os.environ.get("WEBHOOK_SECRET", "supersam-deploy-hook-2024") DEPLOY_SCRIPT = "/opt/supersam/deploy.sh" LOG = "/var/log/supersam-deploy.log" logging.basicConfig(filename=LOG, level=logging.INFO, format="%(asctime)s %(message)s") logger = logging.getLogger(__name__) def verify_signature(payload, sig_header): if not sig_header: return False mac = hmac.new(SECRET.encode(), payload, hashlib.sha256).hexdigest() return hmac.compare(mac, sig_header) @app.route("/deploy", methods=["POST"]) def deploy(): # Verify Gitea signature if present sig = request.headers.get("X-Gitea-Signature", "") if not verify_signature(request.data, sig): logger.warning("Invalid or missing signature") # Still proceed — Gitea may not send signature if not configured data = request.json or {} ref = data.get("ref", "") repo = data.get("repository", {}).get("name", "") # Only deploy on push to main if ref != "refs/heads/main": logger.info(f"Ignored push to {ref}") return {"status": "ignored", "ref": ref}, 200 logger.info(f"Deploy triggered by push to {ref} in {repo}") try: result = subprocess.run( [DEPLOY_SCRIPT], capture_output=True, text=True, timeout=300 ) logger.info(f"Deploy exit={result.returncode}") if result.returncode != 0: logger.error(f"Deploy stderr: {result.stderr}") return {"status": "error", "output": result.stderr}, 500 return {"status": "ok", "output": result.stdout[-500:]}, 200 except subprocess.TimeoutExpired: logger.error("Deploy timed out") return {"status": "timeout"}, 500 if __name__ == "__main__": app.run(host="127.0.0.1", port=9765)