一、准备服务器环境

sudo apt update
sudo apt install git

Python 虚拟环境:

sudo apt install python3-venv
sudo mkdir -p /opt/gitee-webhook/
cd /opt/gitee-webhook/
python3 -m venv venv
source venv/bin/activate

此时命令行前应显示 (venv)

在虚拟环境中安装 Python 依赖(因为有些服务器会有保护机制,只能用虚拟环境):

pip install Flask waitress

克隆你的 Gitee 仓库到服务器:

cd /var/www/
git clone git@gitee.com:your_username/your_repo.git your_project_directory_name

ls -la,确保能看到 .git 目录

二、创建 WebHook 接收脚本

sudo nano /opt/gitee-webhook/webhook_receiver.py

输入:

# /opt/gitee-webhook/webhook_receiver.py

from flask import Flask, request, jsonify
import subprocess
import os
import hmac
import hashlib
import logging

app = Flask(__name__)

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 配置项
# !!! 替换为你的 Git 仓库在服务器上的绝对路径
REPO_PATH = "/var/www/your-project"
# !!! 替换为你将在 Gitee WebHook 设置中填写的密码
WEBHOOK_SECRET = "YOUR_GITEE_WEBHOOK_SECRET"

@app.route('/webhook', methods=['POST'])
def webhook():
logging.info("Received a webhook request.")

# 1. 验证请求来源 (使用 Gitee WebHook 密码)
gitee_token = request.headers.get('X-Gitee-Token')
if gitee_token != WEBHOOK_SECRET:
logging.warning(f"Invalid X-Gitee-Token: {gitee_token}")
return jsonify({"message": "Invalid token"}), 403
logging.info("X-Gitee-Token verified successfully.")

# 2. 检查事件类型 (只处理 Push 和 Tag Push)
event = request.headers.get('X-Gitee-Event')
if event not in ['Push Hook', 'Tag Push Hook']:
logging.info(f"Ignoring event type: {event}")
return jsonify({"message": f"Event type {event} ignored"}), 200
logging.info(f"Processing event type: {event}")

# 3. 执行 Git 操作 (重置并拉取 - 如果需要完全覆盖本地)
try:
os.chdir(REPO_PATH)
logging.info(f"Executing git reset --hard origin/main and git pull in {REPO_PATH}...")

# 确保在正确的远程分支上 (例如 master 或 main)
# 替换 'master' 为你的主分支名称
result_reset = subprocess.run(['git', 'reset', '--hard', 'origin/main'], capture_output=True, text=True, check=True)
logging.info(f"Git reset --hard successful: {result_reset.stdout}")
if result_reset.stderr:
logging.warning(f"Git reset --hard had stderr output: {result_reset.stderr}")

# 然后执行普通的 git pull (此时通常会是 fast-forward)
result_pull = subprocess.run(['git', 'pull'], capture_output=True, text=True, check=True)
logging.info(f"Git pull successful: {result_pull.stdout}")
if result_pull.stderr:
logging.warning(f"Git pull had stderr output: {result_pull.stderr}")

return jsonify({"message": "Git reset and pull successful", "output": result_pull.stdout}), 200
except subprocess.CalledProcessError as e:
logging.error(f"Git operation failed with exit code {e.returncode}: {e.stderr}")
return jsonify({"message": "Git operation failed", "error": e.stderr}), 500
except FileNotFoundError:
logging.error(f"Git command not found. Ensure Git is installed and in PATH for user running the service.")
return jsonify({"message": "Git command not found"}), 500
except Exception as e:
logging.error(f"An unexpected error occurred: {e}")
return jsonify({"message": "An unexpected error occurred", "error": str(e)}), 500

三、配置 Systemd 服务

sudo nano /etc/systemd/system/gitee-webhook.service

输入:

[Unit]
Description=Gitee Webhook Receiver
After=network.target

[Service]
User=www-data
WorkingDirectory=/opt/gitee-webhook
ExecStart=/opt/gitee-webhook/venv/bin/python /opt/gitee-webhook/webhook_receiver.py
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=gitee-webhook

[Install]
WantedBy=multi-user.target

确保 www-data 用户对 /opt/gitee-webhook/ 目录及其内容有读取和执行权限

sudo chown -R www-data:www-data /opt/gitee-webhook/
sudo chmod -R u+rwX,go+rX /opt/gitee-webhook/

确保 www-data 用户对你的 Git 仓库目录有写入权限

sudo chown -R www-data:www-data /var/www/your_project_directory_name/

重载 Systemd、启动服务并设置开机自启:

sudo systemctl daemon-reload
sudo systemctl start gitee-webhook
sudo systemctl enable gitee-webhook

检查服务状态:

sudo systemctl status gitee-webhook

没问题的话就可以看到:

Image 3

四、配置服务器防火墙

确保你的云服务器的防火墙和云服务商的安全组都允许 5000 端口的入站连接

五、在 Gitee 仓库中配置 WebHook

Image 4

六、测试

  1. 在本地修改代码并 git push 到 Gitee 仓库。
  2. 检查 Gitee WebHook 状态: 在 Gitee 3. WebHooks 页面查看请求历史,确认状态为“成功”。
  3. 检查服务器日志
sudo journalctl -u gitee-webhook -f

七、后话:两个月之后,被gitee封ip了

真无语了,要不是服务器在国内,谁用你gitee

换ssh连接了,但不知道后续还会不会被封

SSH 密钥配置流程:

1、停止 Webhook 服务

sudo systemctl stop gitee-webhook.service

2、确保运行 Webhook 服务的用户 (例如 www-data) 对 REPO_PATH 目录有读写权限

sudo chown -R www-data:www-data /var/www/your-project-directory
sudo find /var/www/your-project-directory -type d -exec chmod 755 {} \;
sudo find /var/www/your-project-directory -type f -exec chmod 644 {} \;

3、以运行 Webhook 的用户身份生成 SSH 密钥对:

sudo mkdir -p /var/www/your-project-directory/.ssh
sudo chown www-data:www-data /var/www/your-project-directory/.ssh
sudo chmod 700 /var/www/your-project-directory/.ssh
sudo -u www-data ssh-keygen -t rsa -b 4096 -C "your_email@example.com" -f /var/www/your-project-directory/.ssh/id_rsa

4、复制生成的公钥内容

sudo -u www-data cat /var/www/your-project-directory/.ssh/id_rsa.pub

5、登录 Gitee,进入你的仓库设置 -> WebHooks -> SSH 公钥,将复制的公钥粘贴并添加

6、确保你的 Git 仓库的远程 URL 已设置为 SSH 格式

cd /var/www/your-project-directory
sudo -u www-data git remote set-url origin git@gitee.com:your_username/your_repo.git

7、测试 SSH 连接 (以 www-data 用户身份)

sudo -u www-data ssh -T git@gitee.com -i /var/www/your-project-directory/.ssh/id_rsa

8、webhook_receiver.py 文件修改:

# /opt/gitee-webhook/webhook_receiver.py

from flask import Flask, request, jsonify
import subprocess
import os
import hmac
import hashlib
import logging

app = Flask(__name__)

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 配置项
# !!! 替换为你的 Git 仓库在服务器上的绝对路径
REPO_PATH = "/var/www/your-project"
# !!! 替换为你将在 Gitee WebHook 设置中填写的密码
WEBHOOK_SECRET = "YOUR_GITEE_WEBHOOK_SECRET"

# SSH 密钥路径,确保 www-data 用户可以访问
# !!! 请确保这个路径下的 id_rsa 是以 www-data 用户身份生成的私钥
SSH_KEY_PATH = os.path.join(REPO_PATH, ".ssh", "id_rsa")

@app.route('/webhook', methods=['POST'])
def webhook():
logging.info("Received a webhook request.")

# 1. 验证请求来源
gitee_token = request.headers.get('X-Gitee-Token')
if gitee_token != WEBHOOK_SECRET:
logging.warning(f"Invalid X-Gitee-Token: {gitee_token}")
return jsonify({"message": "Invalid token"}), 403
logging.info("X-Gitee-Token verified successfully.")

# 2. 检查事件类型
event = request.headers.get('X-Gitee-Event')
if event not in ['Push Hook', 'Tag Push Hook']: # 只处理 push 和 tag push 事件
logging.info(f"Ignoring event type: {event}")
return jsonify({"message": f"Event type {event} ignored"}), 200
logging.info(f"Processing event type: {event}")

# 3. 执行 Git 操作 (获取最新并强制重置到远程最新状态)
try:
os.chdir(REPO_PATH)
# 更新日志信息,更准确地反映操作
logging.info(f"Executing git fetch origin and git reset --hard origin/main in {REPO_PATH} using SSH...")

# 设置 GIT_SSH_COMMAND 环境变量来指定 SSH 密钥
# -i {SSH_KEY_PATH} 指定私钥文件
# -o StrictHostKeyChecking=no 避免首次连接时需要手动确认主机指纹
env = os.environ.copy()
env["GIT_SSH_COMMAND"] = f"ssh -i {SSH_KEY_PATH} -o StrictHostKeyChecking=no"

# 1. 首先获取远程仓库的最新信息,包括所有分支的最新提交
result_fetch = subprocess.run(
['git', 'fetch', 'origin'],
capture_output=True,
text=True,
check=True,
env=env # 传递环境变量
)
logging.info(f"Git fetch successful: {result_fetch.stdout}")
if result_fetch.stderr:
logging.warning(f"Git fetch had stderr output: {result_fetch.stderr}")

# 2. 然后强制本地分支和工作区与远程主分支完全一致
result_reset = subprocess.run(
['git', 'reset', '--hard', 'origin/main'],
capture_output=True,
text=True,
check=True,
env=env # 传递环境变量
)
logging.info(f"Git reset --hard successful: {result_reset.stdout}")
if result_reset.stderr:
logging.warning(f"Git reset --hard had stderr output: {result_reset.stderr}")

return jsonify({"message": "Git fetch and reset successful", "output": result_reset.stdout}), 200
except subprocess.CalledProcessError as e:
logging.error(f"Git operation failed with exit code {e.returncode}: {e.stderr}")
return jsonify({"message": "Git operation failed", "error": e.stderr}), 500
except FileNotFoundError:
logging.error(f"Git command not found. Ensure Git is installed and in PATH for user running the service.")
return jsonify({"message": "Git command not found"}), 500
except Exception as e:
logging.error(f"An unexpected error occurred: {e}")
return jsonify({"message": "An unexpected error occurred", "error": str(e)}), 500

9、重启服务

sudo systemctl restart gitee-webhook.service