🚀 Gitea + Webhook 自动部署完整教程(规范版)

适用场景:代码推送到 Gitea 后,自动触发部署脚本,将构建产物发布到指定目录。支持多项目扩展。

📖 目录

  1. 项目结构
  2. 核心配置文件
  3. 启动 Webhook 服务
  4. 初始化项目工作区
  5. Gitea 端配置
  6. 验证完整流程
  7. 添加新项目(扩展多仓库)
  8. 常见问题排查
  9. 路径规范总览
  10. 总结

📁 一、项目结构

假设项目根目录为 /home/<your-user>/webhook-project/(可根据实际情况调整)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
webhook-project/
├── docker-compose.yml
└── data/
├── webhook/ # Webhook 配置与脚本
│ ├── Dockerfile
│ ├── hooks.json
│ └── scripts/
│ ├── common/
│ │ └── deploy.sh # 通用部署函数库
│ ├── hexo/
│ │ └── deploy.sh # Hexo 项目入口脚本
│ └── logs/ # 部署日志目录(自动生成)
│ └── hexo.log
├── workspace/ # 【所有项目源码工作区】
│ └── hexo/ # Hexo 项目(git clone 后生成)
│ ├── .git/
│ ├── source/
│ └── package.json
└── deploy/ # 【所有项目部署目标目录】
└── hexo/ # Hexo 静态文件(部署目标)
└── index.html

🐳 二、核心配置文件

2.1 docker-compose.yml

位置/home/<your-user>/webhook-project/docker-compose.yml

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# Docker Compose 版本(使用 3.8 支持最新特性)
version: '3.8'

# 服务定义
services:
# Webhook 服务
webhook:
# 使用当前目录下的 Dockerfile 构建镜像
build: ./data/webhook
# 容器名称(方便 docker ps 识别)
container_name: webhook
# 容器内部主机名(便于网络通信)
hostname: webhook
# 重启策略:除非手动停止,否则总是重启
restart: unless-stopped
# 暴露端口(只对同一网络内的容器开放,不映射到宿主机)
expose:
- "9000"
# 连接到的 Docker 网络(需提前创建)
networks:
- ipbridge
# 挂载卷(宿主机路径:容器路径)
volumes:
# Webhook 配置文件(只读)
- ./data/webhook/hooks.json:/etc/webhook/hooks.json:ro
# 部署脚本目录(只读)
- ./data/webhook/scripts:/scripts:ro
# 日志目录(可写,用于记录部署日志)
- ./data/webhook/scripts/logs:/scripts/logs
# 所有项目源码工作区
- ./data/workspace:/workspace
# 所有项目部署目标目录
- ./data/deploy:/deploy
# 容器启动后执行的命令
command:
- -verbose # 输出详细日志
- -hooks=/etc/webhook/hooks.json # 指定钩子配置文件
- -hotreload # 配置文件变化时自动重载(无需重启容器)
- -port=9000 # 监听端口
# 日志驱动配置(限制 Docker 日志大小,防止磁盘占满)
logging:
driver: "json-file"
options:
max-size: "10m" # 单个日志文件最大 10MB
max-file: "3" # 最多保留 3 个文件
compress: "true" # 压缩旧日志

# Docker 网络定义
networks:
# 使用外部网络(需提前创建)
ipbridge:
external: true

2.2 Dockerfile

用途说明

almir/webhook 官方镜像是一个精简的 Alpine Linux 基础镜像,仅包含 Webhook 服务本身,没有安装 gitrsync。而我们的部署脚本需要执行 git pull 拉取代码、rsync 同步文件,因此需要通过自定义 Dockerfile 来安装这两个依赖工具。

此外,官方镜像默认以非 root 用户运行,没有权限向系统目录写入文件,因此需要在 Dockerfile 中先切换到 root 用户才能执行 apk add 安装软件包。

位置/home/<your-user>/webhook-project/data/webhook/Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM almir/webhook:latest

# 切换到 root 用户以获得写入权限
USER root

# 更换为阿里云镜像源(加速下载)
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories

# 安装 git 和 rsync(部署脚本依赖)
RUN apk update && apk add --no-cache git rsync

# 可选:切回普通用户(安全考虑)
# USER webhook

构建说明

docker-compose.yml 中,我们使用了 build: ./data/webhook 而非 image: almir/webhook:latest,这样 Docker Compose 会读取此 Dockerfile 构建一个包含 gitrsync 的自定义镜像,而不是直接使用官方原版镜像。

1
2
3
4
services:
webhook:
build: ./data/webhook # 使用 Dockerfile 构建自定义镜像
# 而不是 image: almir/webhook:latest

2.3 hooks.json

位置/home/<your-user>/webhook-project/data/webhook/hooks.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[
{
"id": "deploy-hexo",
"execute-command": "/scripts/hexo/deploy.sh",
"command-working-directory": "/scripts/hexo",
"response-message": "Hexo deployment triggered successfully.",
"trigger-rule": {
"and": [
{
"match": {
"type": "payload-hmac-sha256",
"secret": "YOUR_SECRET_KEY_HERE",
"parameter": {
"source": "header",
"name": "X-Gitea-Signature"
}
}
}
]
}
}
]

生成密钥openssl rand -hex 32,将输出替换 YOUR_SECRET_KEY_HERE

2.4 通用部署函数

位置/home/<your-user>/webhook-project/data/webhook/scripts/common/deploy.sh

⚠️ 注意:以下脚本适用于 Hexo 源码仓库(包含 source/themes/package.json)。如果你的仓库直接存放的是 Hexo 生成的静态文件(即仓库根目录就是 index.htmlarchives/ 等),请使用下方的 「静态文件直接同步版」 脚本。

Hexo 源码构建版(推荐)

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
36
37
38
39
#!/bin/sh
# 通用部署函数
# 用法: deploy.sh <项目名> <源码目录> <部署目标目录>
PROJECT_NAME=$1
SOURCE_DIR=$2
TARGET_DIR=$3
LOG_FILE="/scripts/logs/${PROJECT_NAME}.log"

log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log "========== $PROJECT_NAME 部署开始 =========="

cd "$SOURCE_DIR" || { log "错误: 无法进入 $SOURCE_DIR"; exit 1; }

log "拉取最新代码..."
git pull origin master 2>&1 | tee -a "$LOG_FILE" || { log "git pull 失败"; exit 1; }

log "安装依赖..."
if [ -f "package.json" ]; then
npm install 2>&1 | tee -a "$LOG_FILE" || { log "npm install 失败"; exit 1; }
fi

log "执行项目构建..."
if [ -f "package.json" ]; then
npm run build 2>&1 | tee -a "$LOG_FILE" || { log "构建失败"; exit 1; }
fi

log "同步构建产物到 $TARGET_DIR..."
if [ -d "public" ]; then
rsync -av --delete public/ "$TARGET_DIR/" 2>&1 | tee -a "$LOG_FILE" || { log "rsync 失败"; exit 1; }
elif [ -d "dist" ]; then
rsync -av --delete dist/ "$TARGET_DIR/" 2>&1 | tee -a "$LOG_FILE" || { log "rsync 失败"; exit 1; }
else
log "警告: 未找到 public/ 或 dist/ 目录,跳过同步"
fi

log "========== $PROJECT_NAME 部署完成 =========="

静态文件直接同步版(适用于仓库直接存放 Hexo 生成产物)

如果你的 Gitea 仓库直接存放的是 Hexo 生成的静态文件(即仓库根目录是 index.htmlarchives/ 等),使用此版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/sh
# 通用部署函数(静态文件直接同步版)
# 用法: deploy.sh <项目名> <源码目录> <部署目标目录>
PROJECT_NAME=$1
SOURCE_DIR=$2
TARGET_DIR=$3
LOG_FILE="/scripts/logs/${PROJECT_NAME}.log"

log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log "========== $PROJECT_NAME 部署开始 =========="

cd "$SOURCE_DIR" || { log "错误: 无法进入 $SOURCE_DIR"; exit 1; }

log "拉取最新代码..."
git pull origin master 2>&1 | tee -a "$LOG_FILE" || { log "git pull 失败"; exit 1; }

log "同步到 $TARGET_DIR..."
rsync -av --delete ./ "$TARGET_DIR/" 2>&1 | tee -a "$LOG_FILE" || { log "rsync 失败"; exit 1; }

log "========== $PROJECT_NAME 部署完成 =========="

2.5 Hexo 入口脚本

位置/home/<your-user>/webhook-project/data/webhook/scripts/hexo/deploy.sh

1
2
#!/bin/sh
/scripts/common/deploy.sh "hexo" "/workspace/hexo" "/deploy/hexo"

2.6 赋予执行权限

1
2
3
cd /home/<your-user>/webhook-project
chmod +x data/webhook/scripts/common/deploy.sh
chmod +x data/webhook/scripts/hexo/deploy.sh

🚀 三、启动 Webhook 服务

3.1 准备 Docker 网络(如未创建)

1
docker network create ipbridge

3.2 构建并启动

1
2
3
cd /home/<your-user>/webhook-project
docker-compose build --no-cache
docker-compose up -d

3.3 查看运行状态

1
2
docker-compose ps
docker-compose logs -f webhook

📦 四、初始化项目工作区

Webhook 容器内需要有一个包含 .git 的完整工作区,用于执行 git pull

4.1 进入 Webhook 容器

1
docker exec -it webhook /bin/sh

4.2 初始化 Hexo 项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 创建工作区目录
mkdir -p /workspace/hexo

# 进入工作区目录
cd /workspace/hexo

# 克隆 Gitea 仓库(注意替换为你的实际地址)
# 方式一:Gitea 容器在同一网络(推荐)
git clone http://gitea:3000/<your-username>/hexo.git .

# 方式二:使用宿主机 IP
# git clone http://<宿主机IP>:3000/<your-username>/hexo.git .

# 如果仓库是私有的,带上认证信息
# git clone http://<用户名>:<密码>@gitea:3000/<your-username>/hexo.git .

4.3 检查仓库分支

1
git branch -a

如果看到 master 分支,脚本中的 git pull origin master 会正常工作。如果看到 main 分支,请修改脚本中的分支名。

4.4 安装项目依赖(仅源码版需要)

如果你的仓库是 Hexo 源码(有 package.json):

1
npm install

4.5 测试构建(仅源码版需要)

1
npx hexo generate

4.6 退出容器

1
exit

🔗 五、Gitea 端配置

5.1 修改 Gitea 配置文件(允许 Webhook 目标)

Gitea 默认限制 Webhook 可访问的主机,需要将 webhook 加入白名单。

找到 Gitea 的 app.ini 文件(宿主机路径示例):

1
/home/<your-user>/docker/gitea/data/gitea-data/gitea/conf/app.ini

添加以下配置

1
2
[webhook]
ALLOWED_HOST_LIST = webhook, localhost, 127.0.0.1

如果希望更宽松,可设为 ALLOWED_HOST_LIST = *(不推荐生产环境)。

保存后重启 Gitea 容器

1
docker restart gitea

5.2 在 Gitea 仓库中添加 Webhook

  1. 登录 Gitea,进入你的仓库页面。
  2. 点击 仓库设置Webhook添加 Webhook → 选择 Gitea

填写配置

配置项 填写内容
目标 URL http://webhook:9000/hooks/deploy-hexo
HTTP 方法 POST
POST 内容类型 application/json
密钥 填入 hooks.json 中生成的密钥
触发条件 选择 Push Events
激活 ✅ 勾选
  1. 点击 添加 Webhook 保存。

5.3 测试 Webhook

在 Webhook 列表页面,点击 测试推送,查看投递记录:

  • 状态码应为 200 OK
  • 响应内容应为 Hexo deployment triggered successfully.

✅ 六、验证完整流程

6.1 提交代码触发部署

在本地修改 Hexo 源码并推送:

1
2
3
git add .
git commit -m "test: auto deploy"
git push origin master

6.2 查看部署日志

1
2
3
4
5
# Webhook 服务日志
docker-compose logs -f webhook

# 项目部署日志
tail -f data/webhook/scripts/logs/hexo.log

6.3 检查部署结果

1
ls -la data/deploy/hexo/

应该能看到 Hexo 生成的静态文件(index.htmlarchives/ 等)。

➕ 七、添加新项目(扩展多仓库)

7.1 在 hooks.json 中添加新钩子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"id": "deploy-vuepress",
"execute-command": "/scripts/vuepress/deploy.sh",
"command-working-directory": "/scripts/vuepress",
"response-message": "VuePress deployment triggered successfully.",
"trigger-rule": {
"and": [
{
"match": {
"type": "payload-hmac-sha256",
"secret": "VUEPRESS_SECRET_KEY",
"parameter": {
"source": "header",
"name": "X-Gitea-Signature"
}
}
}
]
}
}

7.2 创建入口脚本

**data/webhook/scripts/vuepress/deploy.sh**:

1
2
#!/bin/sh
/scripts/common/deploy.sh "vuepress" "/workspace/vuepress" "/deploy/vuepress"

赋予执行权限:

1
chmod +x data/webhook/scripts/vuepress/deploy.sh

7.3 初始化工作区

1
2
3
4
5
6
docker exec -it webhook /bin/sh
mkdir -p /workspace/vuepress
cd /workspace/vuepress
git clone http://gitea:3000/<your-username>/vuepress.git .
npm install
exit

7.4 在 Gitea 中配置 Webhook

目标 URL 为:http://webhook:9000/hooks/deploy-vuepress

🔧 八、常见问题排查

问题 解决方法
Dockerfile 构建失败(权限) 确认已添加 USER root
无法克隆仓库 检查 Gitea 容器名和网络,或使用宿主机 IP
git pull 失败(分支名错误) 检查分支是 master 还是 main,修改脚本中对应的分支名
git pull 失败(权限) 确保 /workspace/ 挂载不是只读(去掉 :ro
Webhook 请求被拒绝 修改 Gitea 的 ALLOWED_HOST_LIST 并重启 Gitea
npm install 慢 设置淘宝镜像:npm config set registry https://registry.npmmirror.com
rsync 没有权限写入 检查宿主机目录权限:chmod 755 data/deploy/hexo
Hexo 构建失败 确认 package.json 中有 hexohexo-cli 依赖
脚本报错 bad interpreter: No such file or directory 确认脚本 shebang 是 #!/bin/sh 而非 #!/bin/bash(镜像无 bash)

📊 九、路径规范总览

用途 宿主机路径 容器内路径
所有项目工作区 data/workspace/ /workspace/
Hexo 工作区 data/workspace/hexo/ /workspace/hexo/
所有项目部署目标 data/deploy/ /deploy/
Hexo 部署目标 data/deploy/hexo/ /deploy/hexo/
Webhook 配置 data/webhook/hooks.json /etc/webhook/hooks.json
所有项目脚本 data/webhook/scripts/ /scripts/
通用脚本 data/webhook/scripts/common/ /scripts/common/
Hexo 脚本 data/webhook/scripts/hexo/ /scripts/hexo/
所有项目日志 data/webhook/scripts/logs/ /scripts/logs/
Hexo 日志 data/webhook/scripts/logs/hexo.log /scripts/logs/hexo.log

💎 十、总结

这套方案的核心优势:

  1. 通用性强:一套配置支持任意多个项目。
  2. 目录规范workspace/deploy/webhook/ 三个核心目录平级,职责清晰。
  3. 易于扩展:新增项目只需添加钩子配置、创建脚本、初始化工作区三步。
  4. 安全可控:支持 HMAC 签名验证,可配置允许的主机列表。
  5. 便于迁移:所有数据集中在 data/ 下,可整体迁移。

每次 git push 后,Gitea 自动通知 Webhook 服务,触发拉取、构建、部署全流程。🚀

📌 重要提示

分支名适配

根据你的仓库实际情况,在 data/webhook/scripts/common/deploy.sh 中修改分支名:

  • 如果仓库默认分支是 mastergit pull origin master
  • 如果仓库默认分支是 maingit pull origin main

shebang 说明

almir/webhook 镜像基于 Alpine Linux,默认只有 sh 而没有 bash,因此所有脚本必须使用 #!/bin/sh 作为第一行。

两种部署模式

模式 仓库内容 适用脚本
源码模式 source/themes/package.json Hexo 源码构建版
静态模式 index.htmlarchives/post/ 静态文件直接同步版

请根据你的仓库实际情况选择对应的 deploy.sh 版本。