# Gitea Git Hooks vs Woodpecker CI — 方案评估报告

> 报告日期: 2026-07-12
> 评估对象: cma-management 项目全流程自动化
> 场景: 单服务器、单开发者（全栈Bot）、前端Vue3 + 后端FastAPI

---

## 1. 当前环境状态

| 组件 | 版本 | 状态 |
|------|------|------|
| Gitea | 1.22.6 (Docker) | ✅ 正常运行 |
| Woodpecker Server | v3.12.0 (Docker) | ❌ OAuth集成故障 |
| Woodpecker Agent | v3.12.0 (Docker) | ❌ 无任务可执行 |
| cma-management | main分支 | ✅ Git仓库正常 |

---

## 2. 核心发现

### Woodpecker CI 故障根因

```
GetRepo: invalid character 'o' in literal null (expecting 'u')
```

- Woodpecker v3.12.0 调用 Gitea API 获取仓库信息时，Gitea 返回了非预期的 null 响应
- JSON 解析器期望一个对象（`{`），但收到了 `null`（字母 `n` 的 ascii 是 110，`o` 是 111 — 所以错误信息中出现了 `'o'` 字符）
- 这是一个 **Woodpecker 与 Gitea 1.22 之间的兼容性 bug**，需要等待上游修复
- Cron 任务每 30 分钟重复失败，Webhook 调用也返回 400
- **结论：Woodpecker CI 在当前环境下不可用，且无短期可修复的 workaround**

### Gitea Git Hooks 机制已验证

Gitea 使用 `hooks/post-receive` → `hooks/post-receive.d/*` 的 hook 分发机制：

```
hooks/post-receive         # 主脚本（Gitea自动生成）
hooks/post-receive.d/
├── gitea                   # Gitea内部hook（处理Webhook/Gitea Actions等）
└── deploy                  # 自定义部署hook ← 我们添加的
```

关键发现：
- Hook 脚本在 **Gitea Docker 容器内**以 `git` 用户（UID 1000）身份运行
- 容器与宿主机共享 Gitea 数据卷，但无法直接访问宿主机文件系统或 systemd
- Gitea 容器内已安装 SSH 客户端，可通过 SSH 回到宿主机执行命令

---

## 3. 方案对比

| 维度 | Git Hooks ✅（本方案） | Woodpecker CI ❌ |
|------|:---------------------:|:----------------:|
| **实现复杂度** | ★☆☆ 极简 — 1个shell脚本 | ★★★ 复杂 — 需要修复OAuth bug |
| **运维成本** | ★☆☆ 零 — 无额外服务 | ★★★ 高 — Docker容器+SSH密钥+Agent维护 |
| **故障排查** | ★★☆ 直接看push输出 | ★★★ 日志链路长：Gitea→OAuth→Woodpecker→Agent→容器 |
| **流水线可视化** | ❌ 无Web UI（push输出可见） | ✅ 有Web UI（但当前不可用） |
| **安全性（沙箱隔离）** | ❌ 直接在宿主机执行 | ✅ Docker容器隔离 |
| **多步骤并行** | ❌ 顺序执行 | ✅ 支持并行 |
| **Secrets管理** | ⚠️ 使用现有SSH密钥 | ✅ 内置Secrets管理 |
| **是否满足当前需求** | ✅ **完全满足** | ❌ 当前不可用 |
| **资源占用** | 0（零额外资源） | ~256MB内存 + Docker容器 |
| **PR集成** | 不需要（单开发者） | 需要但用不到 |
| **push到部署耗时** | ~10-15秒（含build） | ~20-30秒（含容器启动） |

### 详细分析

#### Git Hooks 为什么更适合这个场景？

1. **单服务器** — 所有操作都在本机，SSH回环比Docker容器隔离更直接
2. **单开发者（Bot）** — 不需要多用户协作、不需要PR流水线
3. **已有 deploy.sh** — 部署逻辑已封装好，hook只需要触发它
4. **零额外基础设施** — 不增加新的Docker容器，不增加API调用链路
5. **即时反馈** — `git push` 输出中直接看到构建部署日志
6. **Woodpecker 方案做了多余的事** — 它的 `.woodpecker/main.yml` 用 SSH key 从容器SSH回宿主机部署，而我们直接用 Gitea hook SSH 回宿主机 — 逻辑完全相同，但少了一层 Woodpecker 中间件

#### Woodpecker 的优势（在这个场景下用不到）

- 多容器环境下的沙箱隔离 → 单服务器，信任域内
- 并行步骤/矩阵构建 → 构建+部署是线性流程
- Web UI 流水线可视化 → push输出可见，部署频率低（一天几次）

---

## 4. 推荐方案：方案A（Git Hooks）

**明确推荐 Git Hooks 方案，并已实际部署验证通过。**

### 已实施的实现

#### a) Gitea Post-Receive Hook

**位置**: `/data/git/repositories/sxbh_admin/cma-management.git/hooks/post-receive.d/deploy`
（实际路径: `/root/docker/gitea/gitea/data/git/repositories/sxbh_admin/cma-management.git/hooks/post-receive.d/deploy`）

```bash
#!/bin/bash
# 每次 push 到 main 分支后，通过 SSH 到宿主机执行部署
set -e

HOST="172.17.0.1"
SSH_KEY="/data/git/.ssh/id_ed25519"
SSH_CMD="ssh -i ${SSH_KEY} -o StrictHostKeyChecking=no root@${HOST}"

while read oldrev newrev refname; do
  if [ "$refname" = "refs/heads/main" ]; then
    ${SSH_CMD} '
      cd /root/cma-management
      git pull origin main
      bash deploy.sh
    '
  fi
done
```

#### b) 部署脚本（已修复）

**位置**: `/root/cma-management/deploy.sh`

修复内容：
- 移除 `pnpm build --no-frozen-lockfile`（Vite不认识该选项）
- 后端 pip install 使用 `source venv/bin/activate`（解决 Ubuntu PEP 668 externally-managed-environment）

#### c) SSH 密钥配置

- 使用已有的 `id_ed25519_ci_deploy` 密钥（原为 Woodpecker 准备）
- 公钥已添加到宿主机的 `~/.ssh/authorized_keys`
- 私钥存放在 Gitea 容器内 `/data/git/.ssh/id_ed25519`
- 容器内 `git` 用户可读

### 工作流程

```
开发者 push → Gitea 接收 → post-receive hook 触发
  → post-receive.d/deploy 执行
    → SSH 到宿主机（172.17.0.1）
      → cd /root/cma-management
      → git pull origin main
      → bash deploy.sh
        → [1/4] pnpm build（前端构建）
        → [2/4] cp dist/* /var/www/cma/（前端部署）
        → [3/4] pip install（后端依赖更新）
        → [4/4] systemctl restart cma-backend（后端重启）
      ← 部署完成 ✓
    ← SSH 返回
  ← push 完成
```

### 验证结果

已通过实际 push 验证（3次自动部署均成功）：
- 前端构建耗时: ~8-9秒
- 前端部署: ~1秒
- 后端依赖更新: ~1秒
- 后端服务重启: ~2秒
- **总计: ~12-15秒完成全流程部署**

---

## 5. 后续建议

### Woodpecker CI 保留（可选）
- **不要删除** Woodpecker 容器，上游修复后可以重新启用
- 未来如果需要多环境部署、并行测试等高级功能，Woodpecker 仍是更好的选择
- 当前版本（v3.12.0 + Gitea 1.22）存在兼容性问题，升级 Gitea 或 Woodpecker 任一版本可能修复

### 扩展考虑
- 如果需要部署失败通知，可以在 hook 中添加企业微信/钉钉 webhook
- 如果需要增量构建，可以在 hook 中检测改动的文件路径（仅构建有改动的部分）
- 如果需要回滚，hook 可以保留上次构建的备份

---

## 6. 文件变更清单

| 文件 | 操作 | 说明 |
|------|------|------|
| `hooks/post-receive.d/deploy` | **新建** | Gitea 自动部署 hook |
| `/root/cma-management/deploy.sh` | **修改** | 移除 `--no-frozen-lockfile`，使用 venv pip |
| `/root/cma-management/scripts/post-receive-hook.sh` | **新建** | Hook 脚本的版本控制副本 |
| `/root/cma-management/docs/ci-comparison-report.md` | **新建** | 本报告 |
