ci(deploy): 改为 GitHub Webhook 触发部署

This commit is contained in:
admin committed 2026-10-20 14:23:31 +08:00
1 parent 69bfe3ce0e
commit 469630e7da
11 files changed
+267 -78

No files matched your search

+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""接收 GitHub push Webhook,并触发服务器上的固定部署脚本。"""
import hmac
import os
import subprocess
import threading
from hashlib import sha256
from flask import Flask, jsonify, request
DEFAULT_PROJECT_DIR = "/opt/weread-board/WeReadBoard"
DEFAULT_DEPLOY_SCRIPT = "deploy.sh"
DEFAULT_REPOSITORY = "KingHWJ/WeReadBoard"
DEFAULT_BRANCH = "master"
def create_app(config=None):
app = Flask(__name__)
app.config.from_mapping(
WEBHOOK_SECRET=os.environ.get("GITHUB_WEBHOOK_SECRET", ""),
REPOSITORY=os.environ.get("GITHUB_REPOSITORY", DEFAULT_REPOSITORY),
BRANCH=DEFAULT_BRANCH,
PROJECT_DIR=os.environ.get("DEPLOY_PROJECT_DIR", DEFAULT_PROJECT_DIR),
DEPLOY_SCRIPT=os.environ.get("DEPLOY_SCRIPT", DEFAULT_DEPLOY_SCRIPT),
)
if config:
app.config.update(config)
if not app.config["WEBHOOK_SECRET"]:
raise RuntimeError("GITHUB_WEBHOOK_SECRET 未配置")
if app.config["BRANCH"] != DEFAULT_BRANCH:
raise RuntimeError("Webhook 只允许部署 master 分支")
deployment_lock = threading.Lock()
deployment_process = None
def signature_valid(body, signature):
if not signature or not signature.startswith("sha256="):
return False
expected = "sha256=" + hmac.new(
app.config["WEBHOOK_SECRET"].encode("utf-8"), body, sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
def start_deployment():
nonlocal deployment_process
with deployment_lock:
if deployment_process is not None and deployment_process.poll() is None:
return False
env = os.environ.copy()
env["DEPLOY_BRANCH"] = DEFAULT_BRANCH
script = app.config["DEPLOY_SCRIPT"]
if not os.path.isabs(script):
script = os.path.join(app.config["PROJECT_DIR"], script)
deployment_process = subprocess.Popen(
[script],
cwd=app.config["PROJECT_DIR"],
env=env,
start_new_session=True,
)
return True
@app.get("/health")
def health():
return jsonify(ok=True)
@app.post("/github/webhook")
def github_webhook():
body = request.get_data(cache=True)
if not signature_valid(body, request.headers.get("X-Hub-Signature-256", "")):
return jsonify(error="签名无效"), 401
if request.headers.get("X-GitHub-Event") != "push":
return jsonify(error="只接受 push 事件"), 202
payload = request.get_json(silent=True) or {}
repository = (payload.get("repository") or {}).get("full_name")
if repository != app.config["REPOSITORY"]:
return jsonify(error="仓库不匹配"), 403
if payload.get("ref") != f"refs/heads/{app.config['BRANCH']}" or payload.get("deleted"):
return jsonify(error="分支不匹配"), 202
if not start_deployment():
return jsonify(status="already_deploying"), 202
return jsonify(status="accepted"), 202
return app
if __name__ == "__main__":
application = create_app()
application.run(
host=os.environ.get("WEBHOOK_HOST", "127.0.0.1"),
port=int(os.environ.get("WEBHOOK_PORT", "9001")),
)