Files
awoooi/docs/superpowers/plans/2026-04-05-gitea-webhook-migration.md
2026-04-05 14:22:29 +08:00

775 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Gitea Webhook Migration Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 將 GitHub Webhook 整合Phase 13.1)遷移至 Gitea Webhook讓 Gitea PR/Push 事件能觸發 OpenClaw AI 代碼審查。
**Architecture:** 最少改動策略 — Gitea 與 GitHub 的 payload 欄位幾乎相同,差異只在 HTTP header 名稱。重命名檔案、替換 Header 常數、移除 `workflow_run` handler業務邏輯層OpenClaw 呼叫、Redis 儲存、Telegram 通知、Approval 建立)完全不動。
**Tech Stack:** FastAPI, Python 3.11, pytest-asyncio, httpx ASGITransport, K8s Secrets, Gitea Webhook
---
## File Map
| 動作 | 路徑 |
|------|------|
| 新增 | `apps/api/src/api/v1/gitea_webhook.py` |
| 新增 | `apps/api/src/services/gitea_webhook_service.py` |
| 修改 | `apps/api/src/main.py` |
| 修改 | `apps/api/src/core/config.py` |
| 修改 | `k8s/awoooi-prod/03-secrets.yaml` |
| 修改 | `.gitea/workflows/cd.yaml` |
| 新增 | `apps/api/tests/test_gitea_webhook.py` |
| 修改 | `apps/api/tests/conftest.py` |
| 刪除 | `apps/api/src/api/v1/github_webhook.py` |
| 刪除 | `apps/api/src/services/github_webhook_service.py` |
| 刪除 | `apps/api/tests/test_github_webhook.py` |
| 新增 | `docs/adr/ADR-059-gitea-webhook-integration.md` |
---
## Task 1: Config 層 — 替換 GITHUB_ → GITEA_ 環境變數
**Files:**
- Modify: `apps/api/src/core/config.py:490-505`
- [ ] **Step 1: 讀取現有 config 確認位置**
```bash
grep -n "GITHUB_WEBHOOK_SECRET\|GITHUB_ALLOWED_REPOS\|get_github_allowed" apps/api/src/core/config.py
```
Expected output 類似:
```
490: GITHUB_WEBHOOK_SECRET: str = Field(
494: GITHUB_ALLOWED_REPOS: str = Field(
501: raw = self.GITHUB_ALLOWED_REPOS
```
- [ ] **Step 2: 替換 config.py 中的 GITHUB_ 欄位**
`apps/api/src/core/config.py` 中這段:
```python
GITHUB_WEBHOOK_SECRET: str = Field(
default="",
description="GitHub Webhook secret for signature verification (X-Hub-Signature-256)",
)
GITHUB_ALLOWED_REPOS: str = Field(
default="",
description="Comma-separated list of allowed repositories (e.g., 'owner/repo1,owner/repo2')",
)
def get_github_allowed_repos(self) -> list[str]:
"""Parse comma-separated allowed repos to list"""
raw = self.GITHUB_ALLOWED_REPOS
if not raw or not raw.strip():
return []
return [repo.strip() for repo in raw.split(",") if repo.strip()]
```
改為:
```python
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')",
)
def get_gitea_allowed_repos(self) -> list[str]:
"""Parse comma-separated allowed repos to list"""
# 2026-04-05 Claude Code (ADR-059): GitHub → Gitea webhook 遷移
raw = self.GITEA_ALLOWED_REPOS
if not raw or not raw.strip():
return []
return [repo.strip() for repo in raw.split(",") if repo.strip()]
```
- [ ] **Step 3: 驗證 config 語法正確**
```bash
cd apps/api && python3.11 -c "from src.core.config import settings; print('GITEA_WEBHOOK_SECRET:', repr(settings.GITEA_WEBHOOK_SECRET)); print('GITEA_ALLOWED_REPOS:', repr(settings.GITEA_ALLOWED_REPOS))"
```
Expected:
```
GITEA_WEBHOOK_SECRET: ''
GITEA_ALLOWED_REPOS: 'wooo/awoooi'
```
- [ ] **Step 4: Commit**
```bash
git add apps/api/src/core/config.py
git commit -m "refactor(config): GITHUB_WEBHOOK_SECRET → GITEA_WEBHOOK_SECRET (ADR-059)"
```
---
## Task 2: Service 層 — 建立 gitea_webhook_service.py
**Files:**
- Create: `apps/api/src/services/gitea_webhook_service.py`
這個 task 從 `github_webhook_service.py` 複製並修改:
- class 名稱 `GitHub*``Gitea*`
- Redis key prefix `github_review:``gitea_review:`
- Telegram 訊息中 `GITHUB CODE REVIEW``GITEA CODE REVIEW`
- 移除 `CIFailureDiagnosis` class、`_call_openclaw_ci_diagnosis()``_send_ci_failure_telegram_alert()``_create_ci_failure_approval()``diagnose_ci_failure()` 整個 method
- 所有 payload 存取改用 `.get()` chain防禦性解析
- [ ] **Step 1: 複製並重命名**
```bash
cp apps/api/src/services/github_webhook_service.py apps/api/src/services/gitea_webhook_service.py
```
- [ ] **Step 2: 批次替換 class/常數名稱**
```bash
cd apps/api/src/services
# 替換 class 名稱和常數
sed -i \
-e 's/GITHUB_REVIEW_TTL_SECONDS/GITEA_REVIEW_TTL_SECONDS/g' \
-e 's/IGitHubReviewRepository/IGiteaReviewRepository/g' \
-e 's/GitHubReviewRedisRepository/GiteaReviewRedisRepository/g' \
-e 's/GitHubWebhookService/GiteaWebhookService/g' \
-e 's/get_github_webhook_service/get_gitea_webhook_service/g' \
-e 's/github_review:/gitea_review:/g' \
-e 's/GITHUB CODE REVIEW/GITEA CODE REVIEW/g' \
-e 's/github-webhook/gitea-webhook/g' \
gitea_webhook_service.py
```
- [ ] **Step 3: 更新 config 存取settings.GITHUB_ → settings.GITEA_**
```bash
sed -i \
-e 's/settings\.GITHUB_WEBHOOK_SECRET/settings.GITEA_WEBHOOK_SECRET/g' \
-e 's/settings\.GITHUB_ALLOWED_REPOS/settings.GITEA_ALLOWED_REPOS/g' \
-e 's/settings\.get_github_allowed_repos/settings.get_gitea_allowed_repos/g' \
apps/api/src/services/gitea_webhook_service.py
```
- [ ] **Step 4: 移除 CI 診斷相關程式碼**
手動編輯 `apps/api/src/services/gitea_webhook_service.py`,刪除以下區塊:
1. `class CIFailureDiagnosis(BaseModel):` 整個 class約 51-67 行附近)
2. `async def _call_openclaw_ci_diagnosis(self, ...)` 整個 method約 268-320 行附近)
3. `async def _send_ci_failure_telegram_alert(self, ...)` 整個 method約 448-548 行附近)
4. `async def _create_ci_failure_approval(self, ...)` 整個 method約 633-719 行附近)
5. `async def diagnose_ci_failure(self, ...)` 整個 method約 908-1042 行附近)
確認方式:
```bash
grep -n "ci_failure\|CIFailure\|diagnose_ci\|workflow_run" apps/api/src/services/gitea_webhook_service.py
```
Expected: 無輸出(全部移除)
- [ ] **Step 5: 更新檔案 docstring 和版本資訊**
`gitea_webhook_service.py` 頂部 docstring 改為:
```python
"""
AWOOOI API - Gitea Webhook Service
====================================
ADR-059: GitHub → Gitea Webhook 遷移
整合流程:
1. Gitea Webhook (PR/Push) → AWOOOI API
2. HMAC-SHA256 簽章驗證 (X-Gitea-Signature)
3. 解析 PR diff / Push commits
4. 呼叫 OpenClaw 進行 AI 代碼審查
5. 儲存審查結果到 Redis
6. 發送 Telegram 通知
7. (可選) 建立 Approval 等待人工確認
支援事件:
- pull_request: PR 代碼審查
- push: 主分支推送審查
版本: v1.0
最後修改: 2026-04-05 (台北時區)
修改者: Claude Code (ADR-059 GitHub → Gitea 遷移)
"""
```
- [ ] **Step 6: 審查所有 payload .get() 防禦性存取**
搜尋所有直接 key access 並改為 `.get()` chain
```bash
grep -n 'payload\["' apps/api/src/services/gitea_webhook_service.py | head -20
```
對每個直接存取(`payload["key"]`)改為 `payload.get("key", default)`
重要位置包含:
- `review_pull_request()` 中存取 `pull_request``repository``sender` 欄位
- `review_push()` 中存取 `commits``repository``ref` 欄位
範例修改:
```python
# ❌ 舊
repo_name = payload["repository"]["full_name"]
# ✅ 新
repo_name = payload.get("repository", {}).get("full_name", "")
```
- [ ] **Step 7: 語法驗證**
```bash
cd apps/api && python3.11 -c "from src.services.gitea_webhook_service import get_gitea_webhook_service; print('✅ import OK')"
```
Expected: `✅ import OK`
- [ ] **Step 8: Commit**
```bash
git add apps/api/src/services/gitea_webhook_service.py
git commit -m "feat(service): 新增 GiteaWebhookService (ADR-059移除 CI 診斷)"
```
---
## Task 3: Router 層 — 建立 gitea_webhook.py
**Files:**
- Create: `apps/api/src/api/v1/gitea_webhook.py`
`github_webhook.py` 複製並修改:
- Header 常數:`X-GitHub-Event``X-Gitea-Event``X-Hub-Signature-256``X-Gitea-Signature`
- Router prefix`/webhooks/github``/webhooks/gitea`
- 移除 `handle_workflow_run()` 和相關 Pydantic models (`GitHubWorkflowRun`, `GitHubWorkflowJob`)
- 所有 `GitHub*` class → `Gitea*`
- import 改為 `gitea_webhook_service`
- [ ] **Step 1: 複製並重命名**
```bash
cp apps/api/src/api/v1/github_webhook.py apps/api/src/api/v1/gitea_webhook.py
```
- [ ] **Step 2: 批次替換 header 名稱和 class 名稱**
```bash
sed -i \
-e 's/X-GitHub-Event/X-Gitea-Event/g' \
-e 's/X-Hub-Signature-256/X-Gitea-Signature/g' \
-e 's/X-GitHub-Delivery/X-Gitea-Delivery/g' \
-e 's/x_hub_signature_256/x_gitea_signature/g' \
-e 's/x_github_event/x_gitea_event/g' \
-e 's/x_github_delivery/x_gitea_delivery/g' \
-e 's/GitHubUser/GiteaUser/g' \
-e 's/GitHubRepository/GiteaRepository/g' \
-e 's/GitHubPullRequest/GiteaPullRequest/g' \
-e 's/GitHubCommit/GiteaCommit/g' \
-e 's/GitHubWebhookPayload/GiteaWebhookPayload/g' \
-e 's/GitHubWebhookResponse/GiteaWebhookResponse/g' \
-e 's/GitHubSignatureError/GiteaSignatureError/g' \
-e 's/verify_github_signature/verify_gitea_signature/g' \
-e 's/verify_allowed_repo/verify_gitea_allowed_repo/g' \
-e 's/github_webhook_service/gitea_webhook_service/g' \
-e 's/get_github_webhook_service/get_gitea_webhook_service/g' \
-e 's/GitHubWebhookService/GiteaWebhookService/g' \
apps/api/src/api/v1/gitea_webhook.py
```
- [ ] **Step 3: 替換 router prefix 和 config 存取**
```bash
sed -i \
-e 's|prefix="/webhooks/github"|prefix="/webhooks/gitea"|g' \
-e 's/tags=\["GitHub Webhook"\]/tags=["Gitea Webhook"]/g' \
-e 's/settings\.GITHUB_WEBHOOK_SECRET/settings.GITEA_WEBHOOK_SECRET/g' \
-e 's/settings\.get_github_allowed_repos/settings.get_gitea_allowed_repos/g' \
apps/api/src/api/v1/gitea_webhook.py
```
- [ ] **Step 4: 移除 workflow_run 相關程式碼**
手動編輯 `apps/api/src/api/v1/gitea_webhook.py`,刪除以下區塊:
1. `class GitHubWorkflowRun(BaseModel):` 整個 class
2. `class GitHubWorkflowJob(BaseModel):` 整個 class若存在
3. `async def handle_workflow_run(...)` 整個 function
4. 主 handler `handle_gitea_webhook()``workflow_run` 的 dispatch branch
```python
elif event_type == "workflow_run":
return await handle_workflow_run(payload)
```
確認移除:
```bash
grep -n "workflow_run\|WorkflowRun\|WorkflowJob" apps/api/src/api/v1/gitea_webhook.py
```
Expected: 無輸出
- [ ] **Step 5: 更新 docstring**
在 `gitea_webhook.py` 頂部 docstring 改為:
```python
"""
AWOOOI API - Gitea Webhook Handler
====================================
ADR-059: GitHub → Gitea Webhook 遷移
整合流程:
1. Gitea Webhook (PR/Push) → AWOOOI API
2. HMAC-SHA256 簽章驗證 (X-Gitea-Signature)
3. 解析 PR diff / Push commits
4. 呼叫 OpenClaw 進行 AI 代碼審查
5. 儲存審查結果到 Redis
6. 發送 Telegram 通知
7. (可選) 建立 Approval 等待人工確認
支援事件:
- pull_request: PR 代碼審查 (opened, synchronize, reopened)
- push: 主分支推送審查
- ping: 連線測試
安全要求:
- HMAC 簽章驗證 (X-Gitea-Signature)
- Webhook Secret 存放於 K8s Secret
- 倉庫白名單驗證
版本: v1.0
最後修改: 2026-04-05 (台北時區)
修改者: Claude Code (ADR-059 GitHub → Gitea 遷移)
"""
```
- [ ] **Step 6: 語法驗證**
```bash
cd apps/api && python3.11 -c "from src.api.v1.gitea_webhook import router; print('✅ router OK, prefix:', router.prefix)"
```
Expected: `✅ router OK, prefix: /webhooks/gitea`
- [ ] **Step 7: Commit**
```bash
git add apps/api/src/api/v1/gitea_webhook.py
git commit -m "feat(api): 新增 gitea_webhook router (ADR-059Header 改 X-Gitea-*)"
```
---
## Task 4: main.py — 替換掛載
**Files:**
- Modify: `apps/api/src/main.py:43,467`
- [ ] **Step 1: 確認目前掛載位置**
```bash
grep -n "github_webhook" apps/api/src/main.py
```
Expected:
```
43: github_webhook as github_webhook_v1, # Phase 13.1: GitHub → OpenClaw
467: github_webhook_v1.router, prefix="/api/v1", tags=["GitHub Webhook"]
```
- [ ] **Step 2: 替換 import**
將 main.py 第 42-44 行附近的:
```python
from src.api.v1 import (
github_webhook as github_webhook_v1, # Phase 13.1: GitHub → OpenClaw
)
```
改為:
```python
from src.api.v1 import (
gitea_webhook as gitea_webhook_v1, # ADR-059: Gitea → OpenClaw (GitHub → Gitea 遷移)
)
```
- [ ] **Step 3: 替換 router 掛載**
將 main.py 中:
```python
app.include_router(
github_webhook_v1.router, prefix="/api/v1", tags=["GitHub Webhook"]
)
```
改為:
```python
app.include_router(
gitea_webhook_v1.router, prefix="/api/v1", tags=["Gitea Webhook"]
)
```
- [ ] **Step 4: 驗證 main.py 可 import**
```bash
cd apps/api && MOCK_MODE=true python3.11 -c "
import os; os.environ['MOCK_MODE']='true'; os.environ['DATABASE_URL']='postgresql+asyncpg://x:x@localhost/x'
from src.main import app
routes = [r.path for r in app.routes if hasattr(r, 'path')]
gitea_routes = [r for r in routes if 'gitea' in r]
print('Gitea routes:', gitea_routes)
github_routes = [r for r in routes if 'github' in r and 'gitea' not in r]
print('Leftover GitHub routes:', github_routes)
"
```
Expected:
```
Gitea routes: ['/api/v1/webhooks/gitea', '/api/v1/webhooks/gitea/reviews/{review_id}']
Leftover GitHub routes: []
```
- [ ] **Step 5: Commit**
```bash
git add apps/api/src/main.py
git commit -m "refactor(main): github_webhook → gitea_webhook router 掛載 (ADR-059)"
```
---
## Task 5: Tests — 建立 test_gitea_webhook.py
**Files:**
- Create: `apps/api/tests/test_gitea_webhook.py`
- Modify: `apps/api/tests/conftest.py`
- [ ] **Step 1: 更新 conftest.py**
將 `apps/api/tests/conftest.py` 中:
```python
os.environ.setdefault("GITHUB_WEBHOOK_SECRET", "test-secret-key-12345")
os.environ.setdefault("GITHUB_ALLOWED_REPOS", "test-owner/test-repo,wooo-ai/awoooi")
```
改為:
```python
os.environ.setdefault("GITEA_WEBHOOK_SECRET", "test-secret-key-12345")
os.environ.setdefault("GITEA_ALLOWED_REPOS", "test-owner/test-repo,wooo/awoooi")
```
- [ ] **Step 2: 建立 test_gitea_webhook.py**
```bash
cp apps/api/tests/test_github_webhook.py apps/api/tests/test_gitea_webhook.py
```
- [ ] **Step 3: 批次替換 header 和 import**
```bash
sed -i \
-e 's/X-Hub-Signature-256/X-Gitea-Signature/g' \
-e 's/X-GitHub-Event/X-Gitea-Event/g' \
-e 's/X-GitHub-Delivery/X-Gitea-Delivery/g' \
-e 's/from src\.api\.v1\.github_webhook import router as github_webhook_router/from src.api.v1.gitea_webhook import router as gitea_webhook_router/g' \
-e 's/github_webhook_router/gitea_webhook_router/g' \
-e 's|prefix="/api/v1"|prefix="/api/v1"|g' \
apps/api/tests/test_gitea_webhook.py
```
- [ ] **Step 4: 更新 app 建立**
在 `test_gitea_webhook.py` 找到:
```python
from src.api.v1.gitea_webhook import router as gitea_webhook_router
app = FastAPI()
app.include_router(gitea_webhook_router, prefix="/api/v1")
```
確認這兩行正確sed 應已處理 import確認 `include_router` 使用 `gitea_webhook_router`)。
- [ ] **Step 5: 執行測試,確認全部通過**
```bash
cd apps/api && source /opt/api-venv/bin/activate 2>/dev/null || python3.11 -m venv /tmp/test-venv && source /tmp/test-venv/bin/activate && pip install -q uv && uv pip install -q -e ".[dev]"
cd apps/api && python3.11 -m pytest tests/test_gitea_webhook.py -v
```
Expected: 8 tests PASSED
```
PASSED tests/test_gitea_webhook.py::test_webhook_missing_signature
PASSED tests/test_gitea_webhook.py::test_webhook_invalid_signature
PASSED tests/test_gitea_webhook.py::test_webhook_valid_signature
PASSED tests/test_gitea_webhook.py::test_webhook_repo_not_in_whitelist
PASSED tests/test_gitea_webhook.py::test_webhook_ping_event
PASSED tests/test_gitea_webhook.py::test_webhook_push_event
PASSED tests/test_gitea_webhook.py::test_webhook_push_non_default_branch
PASSED tests/test_gitea_webhook.py::test_webhook_unsupported_event
PASSED tests/test_gitea_webhook.py::test_webhook_pr_unsupported_action
```
- [ ] **Step 6: Commit**
```bash
git add apps/api/tests/test_gitea_webhook.py apps/api/tests/conftest.py
git commit -m "test: test_gitea_webhook.py — 8 個測試覆蓋 HMAC/whitelist/events (ADR-059)"
```
---
## Task 6: 清除舊 GitHub 檔案
**Files:**
- Delete: `apps/api/src/api/v1/github_webhook.py`
- Delete: `apps/api/src/services/github_webhook_service.py`
- Delete: `apps/api/tests/test_github_webhook.py`
- [ ] **Step 1: 確認 github_webhook 已無 import**
```bash
grep -rn "github_webhook" apps/api/src/ apps/api/tests/ --include="*.py" | grep -v "__pycache__"
```
Expected: 無輸出(所有 reference 都已在前面步驟移除)
- [ ] **Step 2: 刪除舊檔案**
```bash
git rm apps/api/src/api/v1/github_webhook.py \
apps/api/src/services/github_webhook_service.py \
apps/api/tests/test_github_webhook.py
```
- [ ] **Step 3: 確認無孤兒 import**
```bash
cd apps/api && python3.11 -c "from src.main import app; print('✅ main import OK')"
```
Expected: `✅ main import OK`
- [ ] **Step 4: 執行完整測試確認無 regression**
```bash
cd apps/api && python3.11 -m pytest tests/ -v --tb=short -x \
--ignore=tests/integration \
--ignore=tests/test_anomaly_counter.py \
--ignore=tests/test_global_repair_cooldown.py \
--ignore=tests/test_redis_multisig.py \
--ignore=tests/test_model_regression.py \
--ignore=tests/test_prompt_validation.py \
2>&1 | tail -30
```
Expected: 全部 PASSED無 FAILED
- [ ] **Step 5: Commit**
```bash
git commit -m "chore: 移除舊 GitHub Webhook 檔案 (github_webhook.py / service / test) — ADR-059"
```
---
## Task 7: K8s Secrets + CD Pipeline
**Files:**
- Modify: `k8s/awoooi-prod/03-secrets.yaml`
- Modify: `.gitea/workflows/cd.yaml`
- [ ] **Step 1: 更新 03-secrets.yaml**
在 `k8s/awoooi-prod/03-secrets.yaml` 找到 `GITHUB_WEBHOOK_SECRET`(若存在),改為 `GITEA_WEBHOOK_SECRET: "CHANGE_ME"`。
若不存在,在適當位置新增:
```yaml
GITEA_WEBHOOK_SECRET: "CHANGE_ME" # 2026-04-05 Claude Code (ADR-059): CD 自動注入
```
確認:
```bash
grep -n "GITEA_WEBHOOK_SECRET\|GITHUB_WEBHOOK_SECRET" k8s/awoooi-prod/03-secrets.yaml
```
Expected: 只看到 `GITEA_WEBHOOK_SECRET`,無 `GITHUB_WEBHOOK_SECRET`。
- [ ] **Step 2: 更新 cd.yaml Inject K8s Secrets step**
在 `.gitea/workflows/cd.yaml` 的 `Inject K8s Secrets` step`SECRETS` heredoc 內),在現有注入區塊末尾(`echo "✅ 所有 Secrets 注入完成"` 之前)新增:
```bash
# ADR-059: Gitea Webhook Secret (PR/Push → OpenClaw 代碼審查)
if [ -n "${GITEA_WEBHOOK_SECRET}" ]; then
sudo kubectl patch secret awoooi-secrets -n awoooi-prod --type='json' -p='[
{"op":"add","path":"/data/GITEA_WEBHOOK_SECRET","value":"'"$(echo -n "${GITEA_WEBHOOK_SECRET}" | base64 -w 0)"'"}
]' && echo "✅ GITEA_WEBHOOK_SECRET 已注入" || echo "⚠️ GITEA_WEBHOOK_SECRET patch 失敗"
else
echo "⚠️ GITEA_WEBHOOK_SECRET 未設定Gitea webhook 簽章驗證將跳過"
fi
```
同時在 step 的 `env:` 區塊新增:
```yaml
GITEA_WEBHOOK_SECRET: ${{ secrets.GITEA_WEBHOOK_SECRET }}
```
- [ ] **Step 3: 驗證 cd.yaml YAML 語法**
```bash
python3 -c "import yaml; yaml.safe_load(open('.gitea/workflows/cd.yaml')); print('✅ YAML OK')"
```
Expected: `✅ YAML OK`
- [ ] **Step 4: Commit**
```bash
git add k8s/awoooi-prod/03-secrets.yaml .gitea/workflows/cd.yaml
git commit -m "ops(k8s+cd): 新增 GITEA_WEBHOOK_SECRET 注入 (ADR-059)"
```
---
## Task 8: ADR 文件
**Files:**
- Create: `docs/adr/ADR-059-gitea-webhook-integration.md`
- [ ] **Step 1: 建立 ADR-059**
```bash
cat > docs/adr/ADR-059-gitea-webhook-integration.md << 'EOF'
# ADR-059: Gitea Webhook 整合 (GitHub → Gitea 遷移)
**日期**: 2026-04-05 (台北時區)
**狀態**: 已採用 (Adopted)
**決策者**: 統帥 + Claude Code
**關聯**: ADR-039 (Gitea CI/CD 遷移), ADR-029 (CI/CD AI 整合架構)
---
## 背景
Phase 13.1 (ADR-029) 實作了 GitHub Webhook → OpenClaw AI 代碼審查整合。
但 ADR-039 已將 CI/CD 全面遷移至 GiteaGitHub 只剩唯讀備份。
`GITHUB_WEBHOOK_SECRET` 從未注入 K8s SecretsGitHub Webhook 功能處於死路狀態。
## 決策
將 GitHub Webhook 整合遷移至 Gitea Webhook。
廢棄 `workflow_run` CI 診斷功能CD pipeline 已有 Telegram 通知覆蓋,重複無必要)。
## 遷移策略
**最少改動策略 (Minimal Viable Change)**
Gitea 與 GitHub 的 webhook payload 欄位幾乎相同,差異只在 HTTP header 名稱:
| 項目 | GitHub (廢棄) | Gitea (新) |
|------|-------------|-----------|
| 事件 Header | `X-GitHub-Event` | `X-Gitea-Event` |
| 簽章 Header | `X-Hub-Signature-256` | `X-Gitea-Signature` |
| 簽章格式 | `sha256=<hex>` | `sha256=<hex>` (相同) |
| Payload 欄位 | — | 幾乎相同 |
業務邏輯層OpenClaw 呼叫、Redis 儲存、Telegram 通知、Approval 建立)完全不動。
## 支援事件
| 事件 | 狀態 |
|------|------|
| `pull_request` (opened/synchronize/reopened) | ✅ 遷移 |
| `push` (main/master) | ✅ 遷移 |
| `workflow_run` | ❌ 廢棄 |
| `ping` | ✅ 遷移 |
## 端點變更
```
舊: POST /api/v1/webhooks/github
新: POST /api/v1/webhooks/gitea
```
## 實作護欄
1. **防禦性 payload 解析**: 所有欄位存取使用 `.get()` chain
2. **Gitea UI Content-Type**: 必須選 `application/json`(非 `application/x-www-form-urlencoded`
## 後果
- Gitea PR/Push 事件可觸發 OpenClaw AI 代碼審查
- 移除 `workflow_run` CI 診斷(由 CD pipeline TG 通知替代)
- `GITHUB_WEBHOOK_SECRET` K8s Secret 廢棄,改用 `GITEA_WEBHOOK_SECRET`
EOF
```
- [ ] **Step 2: Commit**
```bash
git add docs/adr/ADR-059-gitea-webhook-integration.md
git commit -m "docs(adr): ADR-059 Gitea Webhook 整合 (GitHub → Gitea 遷移)"
```
---
## Task 9: Push 到 Gitea + 部署後手動設定
- [ ] **Step 1: Push**
```bash
git push gitea main
```
- [ ] **Step 2: 確認 CI 通過**
觀察 Gitea Actions。`Run API Tests` step 應包含 `test_gitea_webhook.py` 的 9 個測試全部 PASSED。
- [ ] **Step 3: 在 Gitea UI 設定 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 Actions Secret `GITEA_WEBHOOK_SECRET` 相同值
6. **Trigger On**: Pull Request events ✅ + Push events ✅
7. **Active**: ✅ 勾選
8. 點擊 "Test Delivery" 確認 ping 事件收到 202 回應
- [ ] **Step 4: 在 Gitea Actions 新增 Secret**
`http://192.168.0.110:3001/wooo/awoooi` → Settings → Actions → Secrets → 新增:
- Name: `GITEA_WEBHOOK_SECRET`
- Value: (統帥決定,與 Gitea UI Webhook Secret 相同)
---
## Self-Review
**Spec coverage check:**
| Spec 要求 | 對應 Task |
|-----------|-----------|
| `github_webhook.py` → `gitea_webhook.py` | Task 3 |
| `github_webhook_service.py` → `gitea_webhook_service.py` | Task 2 |
| `main.py` 更新 import + 路徑 | Task 4 |
| `config.py` GITHUB_ → GITEA_ | Task 1 |
| `03-secrets.yaml` 更新 | Task 7 |
| `cd.yaml` 注入 GITEA_WEBHOOK_SECRET | Task 7 |
| `conftest.py` env var 更新 | Task 5 |
| `test_gitea_webhook.py` | Task 5 |
| 舊 GitHub 檔案刪除 | Task 6 |
| ADR-059 | Task 8 |
| 防禦性 `.get()` 解析 | Task 2 Step 6 |
| Content-Type 警告文件 | Task 9 Step 3 |
| Gitea UI 設定步驟 | Task 9 Step 3 |
**Placeholder scan:** 無 TBD/TODO所有步驟含完整程式碼或指令。
**Type consistency:** `GiteaWebhookService`、`get_gitea_webhook_service`、`gitea_webhook_router` 在 Task 2/3/4/5 命名一致。