Add core service auto deploy

This commit is contained in:
AppFactory Bot
2026-05-27 12:45:15 +02:00
parent ccde0a02cb
commit 26bfba5a73
+68 -41
View File
@@ -1,69 +1,96 @@
import hmac
import hashlib import hashlib
import hmac
import json
import os
import subprocess import subprocess
import threading import threading
from fastapi import FastAPI, Request, Header, HTTPException
WEBHOOK_SECRET = "change-me-webhook-secret" from fastapi import FastAPI, Header, HTTPException, Request
DEPLOY_SCRIPT = "/tools/deploy-app.sh"
WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET", "change-me-webhook-secret")
APP_DEPLOY_SCRIPT = "/tools/deploy-app.sh"
CORE_DEPLOY_SCRIPT = "/tools/deploy-core-service.sh"
CORE_SERVICES = {
"appfactory-portal",
"appfactory-webhook",
}
app = FastAPI(title="AppFactory Webhook") app = FastAPI(title="AppFactory Webhook")
def verify_signature(body: bytes, signature: str | None):
if not signature:
raise HTTPException(status_code=401, detail="Missing signature")
expected = hmac.new(
WEBHOOK_SECRET.encode("utf-8"),
body,
hashlib.sha256
).hexdigest()
valid_signatures = [
expected,
f"sha256={expected}",
]
if not any(hmac.compare_digest(s, signature) for s in valid_signatures):
raise HTTPException(status_code=401, detail="Invalid signature")
def run_deploy(app_id: str):
subprocess.run(
[DEPLOY_SCRIPT, app_id],
capture_output=True,
text=True
)
@app.get("/health") @app.get("/health")
def health(): def health():
return {"status": "ok"} return {"status": "ok"}
def verify_signature(body: bytes, signature: str | None):
if not signature:
raise HTTPException(status_code=401, detail="Missing signature")
valid_signatures = [
"sha256=" + hmac.new(
WEBHOOK_SECRET.encode("utf-8"),
body,
hashlib.sha256
).hexdigest()
]
if not any(hmac.compare_digest(s, signature) for s in valid_signatures):
raise HTTPException(status_code=401, detail="Invalid signature")
def run_command(command: list[str], repo_name: str):
print(f"Deploy started: {repo_name}", flush=True)
print(f"Command: {' '.join(command)}", flush=True)
result = subprocess.run(
command,
capture_output=True,
text=True
)
print(f"Deploy finished: {repo_name}", flush=True)
print(f"Return code: {result.returncode}", flush=True)
if result.stdout:
print("STDOUT:", flush=True)
print(result.stdout, flush=True)
if result.stderr:
print("STDERR:", flush=True)
print(result.stderr, flush=True)
@app.post("/gitea") @app.post("/gitea")
async def gitea_webhook( async def gitea_webhook(
request: Request, request: Request,
x_gitea_signature: str | None = Header(default=None) x_gitea_signature: str | None = Header(default=None),
): ):
body = await request.body() body = await request.body()
verify_signature(body, x_gitea_signature) verify_signature(body, x_gitea_signature)
payload = await request.json() payload = json.loads(body.decode("utf-8"))
repo_name = payload.get("repository", {}).get("name") repo_name = payload.get("repository", {}).get("name")
ref = payload.get("ref", "")
if not repo_name: if not repo_name:
raise HTTPException(status_code=400, detail="Missing repo name") raise HTTPException(status_code=400, detail="Missing repository name")
if ref != "refs/heads/main": if repo_name in CORE_SERVICES:
return { command = [CORE_DEPLOY_SCRIPT, repo_name]
"status": "ignored", else:
"reason": "not main branch", command = [APP_DEPLOY_SCRIPT, repo_name]
"ref": ref
}
threading.Thread(target=run_deploy, args=(repo_name,), daemon=True).start() thread = threading.Thread(
target=run_command,
args=(command, repo_name),
daemon=True,
)
thread.start()
return { return {
"status": "accepted", "status": "accepted",
"repo": repo_name "repo": repo_name,
"core": repo_name in CORE_SERVICES,
} }