This commit is contained in:
wenfp
2026-07-27 12:53:13 +08:00
parent 5ecf77335e
commit 31a7b08f03
5 changed files with 79 additions and 39 deletions
+67 -36
View File
@@ -2,11 +2,12 @@ import hashlib
import hmac
import os
import subprocess
from datetime import datetime
from pathlib import Path
from typing import Any
from urllib.parse import parse_qs
from fastapi import APIRouter, HTTPException, Request, status
from fastapi import APIRouter, BackgroundTasks, HTTPException, Request, status
from app.config import deploy_settings
@@ -113,8 +114,66 @@ def release_lock(fd: int) -> None:
pass
@router.post("/webhook")
async def deploy_webhook(request: Request) -> dict[str, Any]:
def append_deploy_log(content: str) -> None:
log_path = Path(deploy_settings.log_path)
log_path.parent.mkdir(parents=True, exist_ok=True)
with log_path.open("a", encoding="utf-8") as file:
file.write(content)
if not content.endswith("\n"):
file.write("\n")
def build_deploy_env() -> dict[str, str]:
env = os.environ.copy()
env.update(
{
"DEPLOY_PROJECT_DIR": deploy_settings.project_dir,
"DEPLOY_FRONT_DIR": deploy_settings.front_dir,
"DEPLOY_BRANCH": deploy_settings.branch,
"DEPLOY_NODE_BIN_DIR": deploy_settings.node_bin_dir,
}
)
return env
def run_deploy_task(lock_fd: int) -> None:
started_at = datetime.utcnow().replace(microsecond=0).isoformat()
append_deploy_log(f"\n[{started_at}] deploy started")
try:
result = subprocess.run(
[deploy_settings.script_path],
cwd=deploy_settings.project_dir,
env=build_deploy_env(),
capture_output=True,
text=True,
timeout=deploy_settings.command_timeout,
check=False,
)
finished_at = datetime.utcnow().replace(microsecond=0).isoformat()
append_deploy_log(
"\n".join(
[
f"[{finished_at}] deploy finished returncode={result.returncode}",
"--- stdout ---",
output_tail(result.stdout, 20000),
"--- stderr ---",
output_tail(result.stderr, 20000),
"--- end ---",
]
)
)
except subprocess.TimeoutExpired:
finished_at = datetime.utcnow().replace(microsecond=0).isoformat()
append_deploy_log(f"[{finished_at}] deploy timeout after {deploy_settings.command_timeout}s")
except Exception as exc:
finished_at = datetime.utcnow().replace(microsecond=0).isoformat()
append_deploy_log(f"[{finished_at}] deploy error: {exc}")
finally:
release_lock(lock_fd)
@router.post("/webhook", status_code=status.HTTP_202_ACCEPTED)
async def deploy_webhook(request: Request, background_tasks: BackgroundTasks) -> dict[str, Any]:
body = await request.body()
verify_webhook_secret(request, body)
@@ -134,38 +193,10 @@ async def deploy_webhook(request: Request) -> dict[str, Any]:
)
lock_fd = acquire_lock()
try:
env = os.environ.copy()
env.update(
{
"DEPLOY_PROJECT_DIR": deploy_settings.project_dir,
"DEPLOY_FRONT_DIR": deploy_settings.front_dir,
"DEPLOY_BRANCH": deploy_settings.branch,
}
)
result = subprocess.run(
[str(script_path)],
cwd=deploy_settings.project_dir,
env=env,
capture_output=True,
text=True,
timeout=deploy_settings.command_timeout,
check=False,
)
except subprocess.TimeoutExpired as exc:
raise HTTPException(
status_code=status.HTTP_504_GATEWAY_TIMEOUT,
detail=f"部署命令超过 {deploy_settings.command_timeout} 秒未完成",
) from exc
finally:
release_lock(lock_fd)
background_tasks.add_task(run_deploy_task, lock_fd)
response = {
"status": "success" if result.returncode == 0 else "failed",
"returncode": result.returncode,
"stdout": output_tail(result.stdout),
"stderr": output_tail(result.stderr),
return {
"status": "accepted",
"message": "已接收推送事件,前端构建已在后台开始",
"log_path": deploy_settings.log_path,
}
if result.returncode != 0:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=response)
return response