Queue webhook deployments as jobs

This commit is contained in:
AppFactory Bot
2026-05-28 14:01:20 +02:00
parent 6f25cccc57
commit a9a38e4f25
+95 -37
View File
@@ -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"],