Add core service auto deploy
This commit is contained in:
+68
-41
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user