sync: auto-sync from DESKTOP-0O8A1RL at 2026-05-15 15:23:02
Author: Mike Swanson Machine: DESKTOP-0O8A1RL Timestamp: 2026-05-15 15:23:02
This commit is contained in:
109
session-logs/webhook-handler-fixed.py
Normal file
109
session-logs/webhook-handler-fixed.py
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Simple webhook handler for Gitea push events"""
|
||||
import http.server
|
||||
import subprocess
|
||||
import threading
|
||||
import json
|
||||
import hmac
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
WEBHOOK_SECRET = os.environ.get('WEBHOOK_SECRET', 'gururmm-build-secret')
|
||||
BUILD_SCRIPT = '/opt/gururmm/build-agents.sh'
|
||||
LOCK_FILE = '/var/run/gururmm-build.lock'
|
||||
PORT = 9000
|
||||
|
||||
def is_build_running():
|
||||
if not os.path.exists(LOCK_FILE):
|
||||
return False
|
||||
with open(LOCK_FILE) as f:
|
||||
pid = f.read().strip()
|
||||
try:
|
||||
pid = int(pid)
|
||||
except ValueError:
|
||||
os.remove(LOCK_FILE)
|
||||
return False
|
||||
# Check if process exists and is not a zombie
|
||||
try:
|
||||
with open(f'/proc/{pid}/status') as f:
|
||||
status = f.read()
|
||||
if 'State:\tZ' in status:
|
||||
# Zombie — build is done, clean up
|
||||
try:
|
||||
os.waitpid(pid, os.WNOHANG)
|
||||
except ChildProcessError:
|
||||
pass
|
||||
os.remove(LOCK_FILE)
|
||||
return False
|
||||
return True
|
||||
except FileNotFoundError:
|
||||
os.remove(LOCK_FILE)
|
||||
return False
|
||||
|
||||
def run_build():
|
||||
proc = subprocess.Popen(
|
||||
['sudo', BUILD_SCRIPT],
|
||||
stdout=open('/var/log/gururmm-build.log', 'a'),
|
||||
stderr=subprocess.STDOUT
|
||||
)
|
||||
with open(LOCK_FILE, 'w') as f:
|
||||
f.write(str(proc.pid))
|
||||
proc.wait() # Reap the child — prevents zombie
|
||||
if os.path.exists(LOCK_FILE):
|
||||
os.remove(LOCK_FILE)
|
||||
print(f"Build complete, exit code: {proc.returncode}")
|
||||
|
||||
class WebhookHandler(http.server.BaseHTTPRequestHandler):
|
||||
def log_message(self, format, *args):
|
||||
pass # suppress default access log noise
|
||||
|
||||
def do_POST(self):
|
||||
if self.path != '/webhook/build':
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
return
|
||||
|
||||
content_length = int(self.headers.get('Content-Length', 0))
|
||||
body = self.rfile.read(content_length)
|
||||
|
||||
signature = self.headers.get('X-Gitea-Signature', '')
|
||||
if signature:
|
||||
expected = hmac.new(WEBHOOK_SECRET.encode(), body, hashlib.sha256).hexdigest()
|
||||
if not hmac.compare_digest(signature, expected):
|
||||
self.send_response(403)
|
||||
self.end_headers()
|
||||
self.wfile.write(b'Invalid signature')
|
||||
return
|
||||
|
||||
try:
|
||||
data = json.loads(body)
|
||||
ref = data.get('ref', '')
|
||||
|
||||
if ref == 'refs/heads/main':
|
||||
if is_build_running():
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(b'Build already in progress, skipped')
|
||||
return
|
||||
|
||||
print(f"Triggering build for push to main")
|
||||
t = threading.Thread(target=run_build, daemon=True)
|
||||
t.start()
|
||||
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(b'Build triggered')
|
||||
else:
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(f'Ignored push to {ref}'.encode())
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
self.send_response(500)
|
||||
self.end_headers()
|
||||
self.wfile.write(str(e).encode())
|
||||
|
||||
if __name__ == '__main__':
|
||||
server = http.server.HTTPServer(('127.0.0.1', PORT), WebhookHandler)
|
||||
print(f'Webhook handler listening on port {PORT}')
|
||||
server.serve_forever()
|
||||
Reference in New Issue
Block a user