webhook+shell脚本实现代码自动部署


0, 编写python脚本来做webhook,我这里使用的是python3.6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
pip3 install flask
from flask import Flask
from flask import request, jsonify, abort
import subprocess

app = Flask(__name__)

WEBHOOK_VERIFY_TOKEN = "test_token"

@app.route('/webhook', methods=['GET', 'POST']) # 监听的路径,可按照项目定义多个
def webhook():
if request.method == 'GET':
verify_token = request.headers.get('X-Gitlab-Token')
if verify_token == WEBHOOK_VERIFY_TOKEN: # 验证密钥,如果不对直接报错退出
return jsonify({'status': 'success'}), 200
else:
return jsonify({'status': 'bad token'}), 401

elif request.method == 'POST':
verify_token = request.headers.get('X-Gitlab-Token')
if verify_token == WEBHOOK_VERIFY_TOKEN:
# 定义需要执行的命令,我这边使用salt来批量管理
retcode = subprocess.call("echo '触发成功!'", shell=True)
if retcode == 0:
return jsonify({'status': 'success'}), 200
else:
return jsonify({'status': 'git pull error'}), 503
else:
return jsonify({'status': 'bad token'}), 401

else:
abort(400)

if __name__ == '__main__':
app.run(host='0.0.0.0', port='5050') # 监听地址和对外开放的端口

1, 使用systemd管理这个flask程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[[email protected] ~]# cat /etc/init.d/webhook
#!/usr/bin/env bash

/usr/bin/python3 /home/stark/app.py &

[[email protected] ~]# cat /usr/lib/systemd/system/webhook.service
[Unit]
Description=Webhook server daemon

[Service]
Type=forking
ExecStart=/etc/init.d/webhook
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target

2, 载入webhook程序

1
2
3
systemctl daemon-reload
systemctl restart webhook
systemctl enable webhook

手动验证是否可以正常使用
Snipaste_2020-01-09_20-15-06.png

1
2
[[email protected] ~]# curl -X POST -H 'X-Gitlab-Token: test_token' http://192.168.131.128:5050/webhook
{"status":"success"} # 使用任意一台可以访问到他的主机进行测试

Snipaste_2020-01-09_20-19-09.png
可以看到带上token访问该网页之后已经触发了网页里定义的命令
gitlab配置webhook
Snipaste_2020-01-09_20-27-19.png
Snipaste_2020-01-09_20-24-35.png

3, 既然我们已经可以做到push后自动触发命令了,那么下面就需要写脚本对git log和本地的log进行对比了

下面脚本中PRIVATE-TOKEN获取方式
Snipaste_2020-01-09_23-06-46.png
Snipaste_2020-01-09_23-07-13.png
Snipaste_2020-01-09_23-08-54.png
Snipaste_2020-01-09_23-10-14.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env bash

for i in epel-release yajl git expect sshpass
do
rpm -q $i &> /dev/null || yum install -y $i
done

for dist_path in "项目路径1" "项目路径2" "项目路径N";do
# 过滤出来项目地址,然后通过gitlab api去找对应的id
Git_url=$(awk '/url/ {print $NF}' ${dist_path}.git/config |grep -o "xxxxgit.*")
# 找到该项目对应的分支
Git_branch=$(cat ${dist_path}.git/config |awk -F'/' '/merge/ {print $NF}')
# 找到项目id
Project_id=$(curl -H "PRIVATE-TOKEN:test_token" -s "https://xxxxgit.com/api/v3/projects" |json_reformat |grep -B 10 ${Git_url} |grep '"id"' |grep -o '[0-9]\{1,\}')
# 查看项目id的信息,也就是git log信息
git_log=$(curl -H "PRIVATE-TOKEN:test_token" -s "https://xxxxgit.com/api/v3/projects/${Project_id}/repository/commits/${Git_branch}" |awk -F'"' '{print $4}')
# 将服务器端的git log和本地的git log信息进行对比,如果不一致就在本地拉代码
dist_log=$(cd ${dist_path} ; git log |head -1 |awk '{print $NF}')
if [[ $dist_log != $git_log ]]
then
cd ${dist_path}
/usr/bin/expect <<-EOF > /tmp/result.log # /将拉代码结果保存到这个路径,方便后续发送通知
set timeout 90
spawn git pull
expect "Username"
send "test\r"
expect "Password"
send "1234567890\r"
expect eof
EOF
# 拉取成功之后发送通知
mail -s "${HOSTNAME} 更新了 ${dist_path}的代码" [email protected] < /tmp/result.log
fi
done

4, 将上面的脚本放在项目所在的服务器,然后通过触发之后执行的命令来执行脚本就可以了