- docker-compose.yml: deployer에 TZ=Asia/Seoul 환경변수 추가 - deployer/app.py: 로그 datefmt에 %Z 추가하여 KST 시간대 명시 - deployer 재시작 필요: docker compose up -d --build deployer Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
72 lines
2.2 KiB
Python
72 lines
2.2 KiB
Python
import os, hmac, hashlib, subprocess, threading
|
|
from fastapi import FastAPI, Request, HTTPException, BackgroundTasks
|
|
import logging
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s [%(name)s] %(levelname)s %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S %Z",
|
|
)
|
|
logger = logging.getLogger("deployer")
|
|
|
|
app = FastAPI()
|
|
SECRET = os.getenv("WEBHOOK_SECRET", "")
|
|
|
|
if not SECRET:
|
|
logger.warning("WEBHOOK_SECRET is not set! All webhooks will be rejected.")
|
|
|
|
_deploy_lock = threading.Lock()
|
|
|
|
def verify(sig: str, body: bytes) -> bool:
|
|
if not SECRET or not sig:
|
|
return False
|
|
|
|
mac = hmac.new(SECRET.encode(), msg=body, digestmod=hashlib.sha256).hexdigest()
|
|
candidates = {mac, f"sha256={mac}"}
|
|
return any(hmac.compare_digest(sig, c) for c in candidates)
|
|
|
|
def run_deploy_script():
|
|
"""배포 스크립트를 백그라운드에서 실행 (동시 실행 방지)"""
|
|
if not _deploy_lock.acquire(blocking=False):
|
|
logger.info("Deploy already in progress, skipping")
|
|
return
|
|
|
|
try:
|
|
logger.info("Starting deployment script...")
|
|
p = subprocess.run(["/bin/bash", "/scripts/deploy.sh"], capture_output=True, text=True, timeout=600)
|
|
|
|
if p.returncode == 0:
|
|
logger.info(f"Deployment SUCCESS:\n{p.stdout}")
|
|
else:
|
|
logger.error(f"Deployment FAILED ({p.returncode}):\n{p.stdout}\n{p.stderr}")
|
|
|
|
except subprocess.TimeoutExpired:
|
|
logger.error("Deployment TIMEOUT (10 min exceeded)")
|
|
except Exception as e:
|
|
logger.exception(f"Exception during deployment: {e}")
|
|
finally:
|
|
_deploy_lock.release()
|
|
|
|
@app.get("/health")
|
|
def health():
|
|
return {"status": "healthy", "service": "deployer"}
|
|
|
|
@app.post("/webhook")
|
|
async def webhook(req: Request, background_tasks: BackgroundTasks):
|
|
body = await req.body()
|
|
|
|
sig = (
|
|
req.headers.get("X-Gitea-Signature")
|
|
or req.headers.get("X-Hub-Signature-256")
|
|
or ""
|
|
)
|
|
|
|
if not verify(sig, body):
|
|
raise HTTPException(401, "bad signature")
|
|
|
|
# ✅ 비동기 실행: Gitea에게는 즉시 OK 응답을 주고, 배포는 뒤에서 실행
|
|
background_tasks.add_task(run_deploy_script)
|
|
|
|
return {"ok": True, "message": "Deployment started in background"}
|
|
|