| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- #!/usr/bin/env python3
- """
- Webhook 部署脚本
- 接收 Gogs/GitHub webhook 请求,自动执行部署
- 用法:
- 1. 访问 https://book.rrbrr.com/webhook-deploy.py?secret=YOUR_SECRET
- 2. 或使用 curl: curl -X POST https://book.rrbrr.com/webhook-deploy.py -d "secret=YOUR_SECRET"
- """
- import os
- import sys
- import hashlib
- import hmac
- import subprocess
- from http.server import HTTPServer, BaseHTTPRequestHandler
- import json
- # 配置
- WEBHOOK_SECRET = os.environ.get('WEBHOOK_SECRET', 'your-secret-key-change-me')
- DEPLOY_SCRIPT = '/data/ai/deploy.sh'
- LOG_FILE = '/tmp/webhook-deploy.log'
- def log(msg):
- """写入日志"""
- timestamp = subprocess.check_output(['date', '+%Y-%m-%d %H:%M:%S']).decode().strip()
- with open(LOG_FILE, 'a') as f:
- f.write(f"[{timestamp}] {msg}\n")
- print(f"[{timestamp}] {msg}")
- def verify_signature(secret, payload, signature):
- """验证请求签名"""
- if not signature:
- return False
- mac = hmac.new(secret.encode(), payload, hashlib.sha256)
- return mac.hexdigest() == signature.replace('sha256=', '')
- def execute_deploy():
- """执行部署脚本"""
- try:
- log("开始执行部署...")
- # 切换到项目目录
- os.chdir('/data/ai/audio_codebuddy')
- # Git pull
- log("执行 git pull...")
- result = subprocess.run(['git', 'pull', 'origin', 'master'],
- capture_output=True, text=True, timeout=60)
- log(f"Git pull 结果: {result.returncode}")
- if result.stdout:
- log(f"stdout: {result.stdout}")
- if result.stderr:
- log(f"stderr: {result.stderr}")
- # 执行部署脚本
- log("执行部署脚本...")
- result = subprocess.run(['bash', DEPLOY_SCRIPT],
- capture_output=True, text=True, timeout=600)
- log(f"部署脚本返回: {result.returncode}")
- if result.stdout:
- log(f"stdout: {result.stdout[-2000:]}") # 只保留最后2000字符
- if result.stderr:
- log(f"stderr: {result.stderr[-2000:]}")
- if result.returncode == 0:
- log("部署成功!")
- return True
- else:
- log(f"部署失败! 返回码: {result.returncode}")
- return False
- except subprocess.TimeoutExpired:
- log("部署超时!")
- return False
- except Exception as e:
- log(f"部署异常: {e}")
- return False
- class WebhookHandler(BaseHTTPRequestHandler):
- def do_GET(self):
- """处理 GET 请求(健康检查)"""
- self.send_response(200)
- self.send_header('Content-type', 'text/plain')
- self.end_headers()
- self.wfile.write(b'Webhook is running!')
- def do_POST(self):
- """处理 POST 请求(webhook)"""
- # 读取请求体
- content_length = int(self.headers.get('Content-Length', 0))
- body = self.rfile.read(content_length)
- # 获取签名
- signature = self.headers.get('X-Hub-Signature-256') or \
- self.headers.get('X-Gogs-Signature') or \
- self.headers.get('X-Gitea-Signature')
- # 验证签名(如果有 secret)
- if WEBHOOK_SECRET != 'your-secret-key-change-me':
- if not verify_signature(WEBHOOK_SECRET, body, signature):
- log("签名验证失败!")
- self.send_response(401)
- self.send_header('Content-type', 'application/json')
- self.end_headers()
- self.wfile.write(json.dumps({'error': 'Invalid signature'}).encode())
- return
- # 解析 payload
- try:
- payload = json.loads(body)
- log(f"收到 webhook: {payload.get('ref', 'unknown')}")
- except:
- log(f"无法解析 payload: {body[:200]}")
- payload = {}
- # 执行部署
- success = execute_deploy()
- # 返回结果
- self.send_response(200 if success else 500)
- self.send_header('Content-type', 'application/json')
- self.end_headers()
- result = {'success': success, 'message': 'Deployment completed'}
- self.wfile.write(json.dumps(result).encode())
- def log_message(self, format, *args):
- """禁用默认日志"""
- pass
- def main():
- port = int(os.environ.get('WEBHOOK_PORT', 8080))
- server = HTTPServer(('0.0.0.0', port), WebhookHandler)
- log(f"Webhook 服务启动在端口 {port}")
- print(f"Webhook 服务启动在端口 {port}")
- server.serve_forever()
- if __name__ == '__main__':
- main()
|