feat: full pipeline fixes - CI status branch lookup, review webhook routing, auto-advance, plane sync
- handle_ci_status: fallback git branch -r --contains when branches[] empty - webhook router: handle pull_request_approved event type - handle_pr: map review.type to review.state for new Gitea format - launcher: auto-advance stage after agent completion (_try_advance_stage) - plane_sync: notify Plane on stage changes - stages.py: stage machine with QG definitions - notifications.py: stage change notifications - safe.directory fix for container git operations
This commit is contained in:
163
docs/SETUP_WEBHOOKS.md
Normal file
163
docs/SETUP_WEBHOOKS.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# Webhook Setup: Plane + Gitea → Orchestrator
|
||||
|
||||
## Архитектура
|
||||
|
||||
```
|
||||
Gitea (push/PR/CI) ──→ Nginx proxy ──→ Orchestrator /webhook/gitea
|
||||
Plane (work_item/comment) ──→ Nginx proxy ──→ Orchestrator /webhook/plane
|
||||
```
|
||||
|
||||
External URL: `https://openclaw.mva154.duckdns.org/orchestrator/`
|
||||
Internal URL: `http://127.0.0.1:8500/`
|
||||
|
||||
---
|
||||
|
||||
## Gitea Webhook
|
||||
|
||||
**Создан автоматически через API.**
|
||||
|
||||
- URL: `https://openclaw.mva154.duckdns.org/orchestrator/webhook/gitea`
|
||||
- Events: `push`, `pull_request`, `status`
|
||||
- Secret: значение `ORCH_GITEA_WEBHOOK_SECRET` в `.env`
|
||||
- Signature header: `X-Gitea-Signature` (HMAC-SHA256 hex digest)
|
||||
|
||||
### Проверка
|
||||
|
||||
```bash
|
||||
GITEA_TOKEN=$(grep ORCH_GITEA_TOKEN /home/slin/repos/orchestrator/.env | cut -d= -f2)
|
||||
curl -s "http://localhost:3000/api/v1/repos/admin/enduro-trails/hooks" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" | python3 -m json.tool
|
||||
```
|
||||
|
||||
### Пересоздание (если нужно)
|
||||
|
||||
```bash
|
||||
GITEA_WEBHOOK_SECRET=$(openssl rand -hex 20)
|
||||
# Обновить в .env: ORCH_GITEA_WEBHOOK_SECRET=<new_secret>
|
||||
|
||||
curl -X POST "http://localhost:3000/api/v1/repos/admin/enduro-trails/hooks" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"type": "gitea",
|
||||
"active": true,
|
||||
"config": {
|
||||
"url": "https://openclaw.mva154.duckdns.org/orchestrator/webhook/gitea",
|
||||
"content_type": "json",
|
||||
"secret": "'${GITEA_WEBHOOK_SECRET}'"
|
||||
},
|
||||
"events": ["push", "pull_request", "status"],
|
||||
"branch_filter": "*"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Plane Webhook
|
||||
|
||||
**Создан напрямую в PostgreSQL** (Plane CE не экспортирует webhook API через внешний /api/v1/).
|
||||
|
||||
- URL: `https://openclaw.mva154.duckdns.org/orchestrator/webhook/plane`
|
||||
- Events: `issue` (work_item.created), `issue_comment` (comment.created)
|
||||
- Secret: значение `ORCH_PLANE_WEBHOOK_SECRET` в `.env`
|
||||
- Signature header: `X-Plane-Signature` (HMAC-SHA256 hex digest)
|
||||
|
||||
### Проверка
|
||||
|
||||
```bash
|
||||
docker exec -e PGPASSWORD=plane plane-app-plane-db-1 psql -U plane -d plane -c \
|
||||
"SELECT id, url, is_active FROM webhooks;"
|
||||
```
|
||||
|
||||
### Ручная настройка через UI (альтернатива)
|
||||
|
||||
1. Открыть `https://plane.mva154.duckdns.org`
|
||||
2. Workspace Settings → Webhooks → Add Webhook
|
||||
3. URL: `https://openclaw.mva154.duckdns.org/orchestrator/webhook/plane`
|
||||
4. Secret: значение из `ORCH_PLANE_WEBHOOK_SECRET` в `.env`
|
||||
5. Events: Issue, Issue Comment
|
||||
6. Save
|
||||
|
||||
### Пересоздание через SQL
|
||||
|
||||
```bash
|
||||
PLANE_WEBHOOK_SECRET=$(openssl rand -hex 20)
|
||||
# Обновить в .env: ORCH_PLANE_WEBHOOK_SECRET=<new_secret>
|
||||
|
||||
WORKSPACE_ID=$(docker exec -e PGPASSWORD=plane plane-app-plane-db-1 psql -U plane -d plane -t -A -c \
|
||||
"SELECT id FROM workspaces WHERE slug='ag_proj'")
|
||||
|
||||
WEBHOOK_ID=$(cat /proc/sys/kernel/random/uuid)
|
||||
|
||||
docker exec -e PGPASSWORD=plane plane-app-plane-db-1 psql -U plane -d plane -c "
|
||||
INSERT INTO webhooks (id, created_at, updated_at, deleted_at, workspace_id, url, is_active, secret_key, project, issue, module, cycle, issue_comment, is_internal, version)
|
||||
VALUES ('${WEBHOOK_ID}', NOW(), NOW(), NULL, '${WORKSPACE_ID}',
|
||||
'https://openclaw.mva154.duckdns.org/orchestrator/webhook/plane',
|
||||
true, '${PLANE_WEBHOOK_SECRET}', true, true, false, false, true, false, 'v1');
|
||||
"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## HMAC Signature Verification
|
||||
|
||||
Оба handler'а проверяют подпись:
|
||||
- Если secret пустой в `.env` — верификация пропускается (для dev/debug)
|
||||
- Если secret задан — запрос без валидной подписи получает `401 Unauthorized`
|
||||
|
||||
### Формат подписи
|
||||
|
||||
| Source | Header | Algorithm | Format |
|
||||
|--------|--------|-----------|--------|
|
||||
| Gitea | `X-Gitea-Signature` | HMAC-SHA256 | hex digest (без префикса) |
|
||||
| Plane | `X-Plane-Signature` | HMAC-SHA256 | hex digest |
|
||||
|
||||
### Тест подписи вручную
|
||||
|
||||
```bash
|
||||
SECRET=$(grep ORCH_GITEA_WEBHOOK_SECRET /home/slin/repos/orchestrator/.env | cut -d= -f2)
|
||||
BODY='{"ref":"refs/heads/test","repository":{"name":"enduro-trails"},"commits":[]}'
|
||||
SIG=$(echo -n "${BODY}" | openssl dgst -sha256 -hmac "${SECRET}" | awk '{print $NF}')
|
||||
|
||||
curl -X POST http://localhost:8500/webhook/gitea \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Gitea-Event: push" \
|
||||
-H "X-Gitea-Signature: ${SIG}" \
|
||||
-d "${BODY}"
|
||||
# Expected: {"status":"accepted"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Переменные окружения (.env)
|
||||
|
||||
| Переменная | Описание |
|
||||
|-----------|----------|
|
||||
| `ORCH_GITEA_WEBHOOK_SECRET` | HMAC secret для Gitea webhook |
|
||||
| `ORCH_PLANE_WEBHOOK_SECRET` | HMAC secret для Plane webhook |
|
||||
| `ORCH_GITEA_TOKEN` | API token для Gitea |
|
||||
| `ORCH_PLANE_API_TOKEN` | API token для Plane |
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
```bash
|
||||
# Логи Orchestrator
|
||||
docker logs orchestrator --tail 50 2>&1 | grep -i "webhook\|signature\|401"
|
||||
|
||||
# События в БД
|
||||
docker exec orchestrator python3 -c "
|
||||
import sqlite3
|
||||
conn = sqlite3.connect('/app/data/orchestrator.db')
|
||||
for r in conn.execute('SELECT id, source, event_type, timestamp FROM events ORDER BY id DESC LIMIT 10').fetchall():
|
||||
print(r)
|
||||
"
|
||||
|
||||
# Gitea webhook delivery history
|
||||
# Gitea UI → Settings → Webhooks → click webhook → Recent Deliveries
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Создано: 2026-05-21 | Автор: Dev-агент*
|
||||
Reference in New Issue
Block a user