用python徒手撸一个webhook,让git提交后在服务器自动拉取,通过nginx反向代理,防止暴露端口

  • token验证(监听post和get) √

  • nginx反向代理隐藏端口 √

  • 配置放到顶部,方便调整 √

  • 同步git仓库 √


宝塔的webhook很好用,上次分享了一个在宝塔写webhook的文章,今天要在一台没有宝塔的服务器使用webhook功能,服务器已经有abc.com这样的域名在运行,所以我计划是在abc.com/webhook这样的链接访问时触发webhook拉取。然后在git仓库设置webhook回调即可。


一开始想直接用shell写的,和宝塔的一样,但是发现还是要做网络请求,于是用了netcat这个网络工具,后来发现虽然可以实现,但是并不好控制netcat,这玩意更适合用于测试网络情况。

这还不如写个go或者python呢,于是我看看服务器环境,有python没有go,那就用python吧,服务器项目是php的,不要问我为什么不用php直接执行git命令!!!

服务器的python是2.7.2,所以用python2的语法写如下(pull.py):

# -*- coding: utf-8 -*-
import BaseHTTPServer
import SimpleHTTPServer
import urlparse
import subprocess

# 全局变量
WEBHOOK_TOKEN = '我是token'
WEBHOOK_PORT = 8072
GIT_DIR = '/www/wwwroot/abc.com'

class WebhookHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def do_GET(self):
        # 检查请求路径是否为/webhook
        if self.path.startswith('/webhook'):
            query_components = urlparse.urlparse(self.path).query
            query_params = urlparse.parse_qs(query_components)
            token = query_params.get('token', [''])[0]
            if token == WEBHOOK_TOKEN:
                self.handle_webhook_request()
            else:
                self.send_response(403)
                self.send_header('Content-type', 'text/plain')
                self.end_headers()
                self.wfile.write("Forbidden")
        else:
            self.send_response(404)
            self.send_header('Content-type', 'text/plain')
            self.end_headers()
            self.wfile.write("Not Found")

    def do_POST(self):
        # 检查请求路径是否为/webhook
        if self.path.startswith('/webhook'):
            query_components = urlparse.urlparse(self.path).query
            query_params = urlparse.parse_qs(query_components)
            token = query_params.get('token', [''])[0]

            # 如果Token通过URL传递,直接使用
            if not token:
                # 从请求体中获取Token(假设请求体为JSON格式)
                content_length = int(self.headers.getheader('content-length', 0))
                body = self.rfile.read(content_length)
                import json
                post_data = json.loads(body)
                token = post_data.get('token', '')

            if token == WEBHOOK_TOKEN:
                self.handle_webhook_request()
            else:
                self.send_response(403)
                self.send_header('Content-type', 'text/plain')
                self.end_headers()
                self.wfile.write("Forbidden")
        else:
            self.send_response(404)
            self.send_header('Content-type', 'text/plain')
            self.end_headers()
            self.wfile.write("Not Found")

    def handle_webhook_request(self):
        # 指定项目目录
        project_dir = GIT_DIR

        # 切换到项目目录并执行git pull
        try:
            result = subprocess.Popen(['git', 'pull'], cwd=project_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            stdout, stderr = result.communicate()

            # 构造响应
            response = "Result of git pull in %s:\n%s\nErrors:\n%s" % (project_dir, stdout, stderr)
            self.send_response(200)
            self.send_header('Content-type', 'text/plain')
            self.end_headers()
            self.wfile.write(response)
        except Exception as e:
            self.send_response(500)
            self.send_header('Content-type', 'text/plain')
            self.end_headers()
            self.wfile.write("Error executing git pull: %s" % e)

if __name__ == '__main__':
    server_address = ('', WEBHOOK_PORT)
    httpd = BaseHTTPServer.HTTPServer(server_address, WebhookHandler)
    print "Starting HTTP server on port %s..." % WEBHOOK_PORT
    httpd.serve_forever()


上面指定了项目目录/www/wwwroot/abc.com;指定了映射端口8072;根据实际情况自己调整哈,上面仅限测试。


接下来给这个pull.py执行权限,我就直接给775了:

chmod 775 pull.py

然后运行这个python脚本

python pull.py

出现这样的信息就是成功的:

Starting HTTP server on port 8072...


也可以后端进程的方式运行(我就用一种方式,其它的自己研究哈):

nohup python pull.py > git_pull.log 2>&1 &


重启脚本(先kill掉再启动即可)

kill -9 `ps -ef|grep pull.py|grep -v grep|awk '{print $2}'` 

nohup python pull.py > git_pull.log 2>&1 &

然后在nginx里的abc.com.conf配置加上反向代理:

location /webhook {
        proxy_pass http://localhost:8072/webhook; # 将请求转发到8072端口
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

记得重启一下nginx

sudo systemctl restart nginx


然后测试效果如下

image.png

看日志也是可以知道什么时候有推送的

image.png

可以说是非常成功了。


今天到此结束,祝大家开心过每一天。

评论/留言