From a9a38e4f25fba6d1971ee0769e31a6d66cbc9b7e Mon Sep 17 00:00:00 2001 From: AppFactory Bot Date: Thu, 28 May 2026 14:01:20 +0200 Subject: [PATCH] Queue webhook deployments as jobs --- app/main.py | 132 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 95 insertions(+), 37 deletions(-) diff --git a/app/main.py b/app/main.py index 960d258..28cf73b 100755 --- a/app/main.py +++ b/app/main.py @@ -2,12 +2,14 @@ import hashlib import hmac import json import os -import subprocess -import threading +import sqlite3 from fastapi import FastAPI, Header, HTTPException, Request +DB_FILE = "/opt/appfactory/data/appfactory/appfactory.db" + + def read_env_file_value(key: str, default: str = "") -> str: try: with open("/opt/appfactory/config/appfactory.env", "r", encoding="utf-8") as f: @@ -23,12 +25,10 @@ def read_env_file_value(key: str, default: str = "") -> str: WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET", read_env_file_value("WEBHOOK_SECRET", "")) -APP_DEPLOY_SCRIPT = "/tools/deploy-app.sh" -CORE_DEPLOY_SCRIPT = "/tools/deploy-core-service.sh" - CORE_SERVICES = { "appfactory-portal", "appfactory-webhook", + "appfactory-worker", } app = FastAPI(title="AppFactory Webhook") @@ -59,6 +59,7 @@ def first_commit(payload: dict) -> dict: commits = payload.get("commits") or [] if commits: return commits[-1] or {} + return payload.get("head_commit") or {} @@ -104,28 +105,78 @@ def extract_attribution(payload: dict) -> dict: } -def run_command(command: list[str], repo_name: str, deploy_env: dict[str, str]): - env = os.environ.copy() - env.update(deploy_env) +def create_job( + job_type: str, + target_type: str, + target_id: str, + payload: dict, + created_by_username: str, + created_by_display_name: str, + source: str, +) -> int: + con = sqlite3.connect(DB_FILE) - print(f"Deploy started: {repo_name}", flush=True) - print(f"Command: {' '.join(command)}", flush=True) - print(f"Trigger source: {env.get('APPFACTORY_TRIGGER_SOURCE')}", flush=True) - print(f"Pusher: {env.get('APPFACTORY_PUSHER')}", flush=True) - print(f"Commit author: {env.get('APPFACTORY_COMMIT_AUTHOR')}", flush=True) + cur = con.execute( + """ + INSERT INTO jobs ( + type, + target_type, + target_id, + payload_json, + status, + created_by_username, + created_by_display_name, + source + ) + VALUES (?, ?, ?, ?, 'queued', ?, ?, ?) + """, + ( + job_type, + target_type, + target_id, + json.dumps(payload, ensure_ascii=False), + created_by_username, + created_by_display_name, + source, + ), + ) - result = subprocess.run(command, capture_output=True, text=True, env=env) + job_id = cur.lastrowid - print(f"Deploy finished: {repo_name}", flush=True) - print(f"Return code: {result.returncode}", flush=True) + con.execute( + """ + INSERT INTO audit_events ( + username, + action, + target_type, + target_id, + source, + metadata, + created_at + ) + VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) + """, + ( + created_by_username, + "webhook.deploy.queued", + target_type, + target_id, + "webhook", + json.dumps( + { + "job_id": job_id, + "job_type": job_type, + **payload, + }, + ensure_ascii=False, + ), + ), + ) - if result.stdout: - print("STDOUT:", flush=True) - print(result.stdout, flush=True) + con.commit() + con.close() - if result.stderr: - print("STDERR:", flush=True) - print(result.stderr, flush=True) + return job_id @app.post("/gitea") @@ -149,27 +200,34 @@ async def gitea_webhook( attr = extract_attribution(payload) if repo_name in CORE_SERVICES: - command = [CORE_DEPLOY_SCRIPT, repo_name] + job_type = "deploy_core_service" + target_type = "core_service" else: - command = [APP_DEPLOY_SCRIPT, repo_name] + job_type = "deploy_app" + target_type = "app" - deploy_env = { - "APPFACTORY_TRIGGER_SOURCE": "webhook", - "APPFACTORY_TRIGGERED_BY_USERNAME": attr["pusher_username"], - "APPFACTORY_TRIGGERED_BY_DISPLAY_NAME": attr["pusher"], - "APPFACTORY_COMMIT_AUTHOR": attr["commit_author"], - "APPFACTORY_PUSHER": attr["pusher"], - "APPFACTORY_COMMIT_SHA": attr["commit_sha"], + ref = payload.get("ref") or "" + job_payload = { + "repository": repo_name, + "ref": ref, + "commit_sha": attr["commit_sha"], + "commit_author": attr["commit_author"], + "pusher": attr["pusher"], } - threading.Thread( - target=run_command, - args=(command, repo_name, deploy_env), - daemon=True, - ).start() + job_id = create_job( + job_type=job_type, + target_type=target_type, + target_id=repo_name, + payload=job_payload, + created_by_username=attr["pusher_username"], + created_by_display_name=attr["pusher"], + source="webhook", + ) return { - "status": "accepted", + "status": "queued", + "job_id": job_id, "repo": repo_name, "core": repo_name in CORE_SERVICES, "triggered_by": attr["pusher_username"],