# Gitea Webhook 整合設計規格

**日期**: 2026-04-05 (台北時區)  
**作者**: Claude Code + 首席架構師 Review  
**狀態**: 已批准，待實作  
**ADR**: ADR-059 (待建立)  
**關聯**: ADR-039 (Gitea CI/CD 遷移), ADR-029 (CI/CD AI 整合架構)

---

## 背景

Phase 13.1 實作了 GitHub Webhook → OpenClaw AI 代碼審查整合，但：
1. CI/CD 已於 ADR-039 全面遷移至 Gitea，GitHub 只剩唯讀備份
2. `GITHUB_WEBHOOK_SECRET` 從未注入 K8s Secrets，功能實際上處於死路狀態
3. 代碼審查的觸發源應改為 Gitea（實際開發倉庫）

**決策：遷移至 Gitea Webhook，廢棄 GitHub Webhook 整合。**

---

## 設計原則

**最少改動策略 (Minimal Viable Change)**

Gitea 與 GitHub 的 webhook payload 欄位幾乎相同，差異只在 HTTP header 名稱。
因此策略是：重命名 + Header 常數替換，業務邏輯層（OpenClaw 呼叫、Redis 儲存、Telegram 通知、Approval 建立）完全不動。

---

## Header 差異對照

| 項目 | GitHub (廢棄) | Gitea (新) |
|------|-------------|-----------|
| 事件類型 Header | `X-GitHub-Event` | `X-Gitea-Event` |
| 簽章 Header | `X-Hub-Signature-256` | `X-Gitea-Signature` |
| 投遞 ID Header | `X-GitHub-Delivery` | `X-Gitea-Delivery` |
| 簽章格式 | `sha256=<hex>` | `sha256=<hex>` ← **相同** |
| Payload 欄位 | — | — ← **幾乎相同** |

> Gitea 會同時送出 `X-GitHub-Event` 相容 header，但我們應讀取原生的 `X-Gitea-Event`，
> 語意清晰，未來不依賴相容層。

---

## 遷移範圍

### 支援事件

| 事件 | GitHub (舊) | Gitea (新) | 說明 |
|------|------------|-----------|------|
| `pull_request` | ✅ | ✅ 遷移 | PR 代碼審查 |
| `push` | ✅ | ✅ 遷移 | 主分支推送審查 |
| `workflow_run` | ✅ | ❌ 廢棄 | CD pipeline 已有 TG 通知，重複覆蓋無必要 |
| `ping` | ✅ | ✅ 遷移 | 連線測試 |

### 檔案變更清單

| 檔案 | 動作 | 說明 |
|------|------|------|
| `src/api/v1/github_webhook.py` | 重命名 → `gitea_webhook.py` | Header 常數改 Gitea，移除 `workflow_run` handler |
| `src/services/github_webhook_service.py` | 重命名 → `gitea_webhook_service.py` | 移除 `diagnose_ci_failure()`，class 改名 |
| `src/main.py` | 修改 | 更新 import，掛載路徑改 `/webhooks/gitea` |
| `src/core/config.py` | 修改 | `GITHUB_WEBHOOK_SECRET` → `GITEA_WEBHOOK_SECRET`，`GITHUB_ALLOWED_REPOS` → `GITEA_ALLOWED_REPOS` |
| `k8s/awoooi-prod/03-secrets.yaml` | 修改 | 新增 `GITEA_WEBHOOK_SECRET`，移除 `GITHUB_WEBHOOK_SECRET` |
| `tests/conftest.py` | 修改 | 更新 env var 名稱 |
| `tests/test_github_webhook.py` | 重命名 → `test_gitea_webhook.py` | Header 常數改 Gitea |
| `docs/adr/ADR-059-gitea-webhook-integration.md` | 新增 | 決策記錄 |

---

## API 端點

```
舊: POST /api/v1/webhooks/github
新: POST /api/v1/webhooks/gitea

舊: GET  /api/v1/webhooks/github/reviews/{review_id}
新: GET  /api/v1/webhooks/gitea/reviews/{review_id}
```

---

## Config 變更

```python
# 舊 (廢棄)
GITHUB_WEBHOOK_SECRET: str = Field(default="")
GITHUB_ALLOWED_REPOS: str = Field(default="")

# 新
GITEA_WEBHOOK_SECRET: str = Field(
    default="",
    description="Gitea Webhook secret for HMAC-SHA256 signature verification (X-Gitea-Signature)",
)
GITEA_ALLOWED_REPOS: str = Field(
    default="wooo/awoooi",
    description="Comma-separated list of allowed Gitea repositories (e.g., 'wooo/awoooi')",
)
```

---

## 防禦性 Payload 解析（首席架構師護欄 #1）

Gitea 部分欄位為 nullable 或可能缺失。所有欄位存取使用 `.get()` chain，禁止直接 key access：

```python
# ❌ 禁止
repo_name = payload["repository"]["full_name"]

# ✅ 正確
repo_name = payload.get("repository", {}).get("full_name", "")
pr_number = payload.get("pull_request", {}).get("number", 0)
head_sha = payload.get("pull_request", {}).get("head", {}).get("sha", "")
```

現有 `github_webhook_service.py` 中已部分採用此模式，遷移時需全面審查確認。

---

## Gitea UI 設定步驟（首席架構師護欄 #2）

部署完成後在 Gitea 設定 Webhook：

1. 進入 `http://192.168.0.110:3001/wooo/awoooi` → Settings → Webhooks → Add Webhook → Gitea
2. **Target URL**: `https://awoooi.wooo.work/api/v1/webhooks/gitea`
3. **HTTP Method**: POST
4. **Content Type**: `application/json` ← **必須明確選擇，否則 FastAPI 無法解析**
5. **Secret**: 與 `GITEA_WEBHOOK_SECRET` K8s Secret 值相同
6. **Trigger On**: Pull Request events + Push events
7. **Active**: 勾選

> ⚠️ Content-Type 若選 `application/x-www-form-urlencoded`，FastAPI 的 `Request.json()` 
> 將無法解析，導致所有 webhook 請求失敗。

---

## 簽章驗證邏輯

驗證邏輯與 GitHub 完全相同，只需更換 header 名稱：

```python
# 舊 header
x_hub_signature_256: str | None = Header(default=None, alias="X-Hub-Signature-256")

# 新 header
x_gitea_signature: str | None = Header(default=None, alias="X-Gitea-Signature")
```

HMAC-SHA256 計算方式不變：`sha256=<hmac_sha256(secret, body).hexdigest()>`

---

## K8s Secrets 注入

`k8s/awoooi-prod/03-secrets.yaml` 新增：
```yaml
GITEA_WEBHOOK_SECRET: "CHANGE_ME"  # CD 自動注入
```

`cd.yaml` 的 `Inject K8s Secrets` step 需新增注入：
```bash
kubectl patch secret awoooi-secrets \
  --patch "{\"stringData\": {\"GITEA_WEBHOOK_SECRET\": \"${{ secrets.GITEA_WEBHOOK_SECRET }}\"}}"
```

Gitea Actions secrets 需新增 `GITEA_WEBHOOK_SECRET`。

---

## 測試策略

`test_gitea_webhook.py` 複用現有 8 個測試案例，更新：
1. Header 名稱：`X-Hub-Signature-256` → `X-Gitea-Signature`、`X-GitHub-Event` → `X-Gitea-Event`
2. Router import：`from src.api.v1.gitea_webhook import router as gitea_webhook_router`
3. `conftest.py`：`GITHUB_WEBHOOK_SECRET` → `GITEA_WEBHOOK_SECRET`、`GITHUB_ALLOWED_REPOS` → `GITEA_ALLOWED_REPOS`

---

## 不在此 Scope

- Gitea 端實際 webhook secret 的生成與管理（統帥決定）
- `workflow_run` 等值替代方案（未來 Phase 規劃）
- GitHub mirror repo 的 webhook 設定清除（可手動處理）
