auto-sync: 2026-05-21 16:00:01

This commit is contained in:
Stream
2026-05-21 16:00:01 +03:00
parent 1b2502b39b
commit 732a19e564
2 changed files with 302 additions and 15 deletions

View File

@@ -0,0 +1,284 @@
# DEV TASK: Настройка Webhooks (Plane + Gitea → Orchestrator)
**Статус:** Ready for dev
**Проект:** multi-agent
**Фаза:** 3
**Предыдущая задача:** DEV_TASK_ORCHESTRATOR_QG.md (выполнена)
---
## Цель
> Plane и Gitea автоматически отправляют webhook events в Orchestrator при действиях пользователя (создание задачи, комментарий, push, PR, CI status).
## Архитектура
Nginx proxy уже настроен: `https://openclaw.mva154.duckdns.org/orchestrator/``localhost:8500`.
Нужно:
1. Создать webhook в Gitea через API (events: push, pull_request, status)
2. Создать webhook в Plane через API или UI (events: work_item.created, comment.created)
3. Добавить HMAC-верификацию подписи в обоих handlers
4. Добавить webhook secret в .env Orchestrator'а
5. Проверить end-to-end: действие в UI → webhook → Orchestrator обрабатывает
## Стек / Зависимости
- httpx (уже есть)
- hmac + hashlib (stdlib)
- Gitea API v1
- Plane API v1
---
## Инфраструктура
| Параметр | Значение |
|----------|----------|
| Сервер | `slin@82.22.50.71` (mva154) |
| Orchestrator URL (external) | `https://openclaw.mva154.duckdns.org/orchestrator/` |
| Orchestrator URL (internal) | `http://127.0.0.1:8500/` |
| Gitea API | `http://localhost:3000/api/v1` |
| Gitea token | в .env `ORCH_GITEA_TOKEN` |
| Plane API | `http://localhost:8091/api/v1` |
| Plane token | в .env `ORCH_PLANE_API_TOKEN` |
| Plane workspace | `ag_proj` |
| Gitea repo owner | `admin` |
| Gitea repo | `enduro-trails` |
---
## Задачи
### Task 1: Создать webhook в Gitea через API
**Шаги:**
- [ ] **1.1** Создать webhook для репо `admin/enduro-trails`:
```bash
# Сгенерировать secret
GITEA_WEBHOOK_SECRET=$(openssl rand -hex 20)
echo "ORCH_GITEA_WEBHOOK_SECRET=${GITEA_WEBHOOK_SECRET}" >> /home/slin/repos/orchestrator/.env
# Создать webhook через API
curl -s -X POST "http://localhost:3000/api/v1/repos/admin/enduro-trails/hooks" \
-H "Authorization: token $(grep ORCH_GITEA_TOKEN /home/slin/repos/orchestrator/.env | cut -d= -f2)" \
-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": "*"
}'
```
- [ ] **1.2** Проверить что webhook создан:
```bash
curl -s "http://localhost:3000/api/v1/repos/admin/enduro-trails/hooks" \
-H "Authorization: token $(grep ORCH_GITEA_TOKEN /home/slin/repos/orchestrator/.env | cut -d= -f2)" | python3 -m json.tool
```
**Критерий готовности:** Webhook виден в Gitea UI (Settings → Webhooks), URL правильный.
---
### Task 2: Создать webhook в Plane
**Шаги:**
- [ ] **2.1** Проверить Plane API для webhooks:
```bash
# Plane webhooks API
curl -s "http://localhost:8091/api/v1/workspaces/ag_proj/webhooks/" \
-H "X-API-Key: $(grep ORCH_PLANE_API_TOKEN /home/slin/repos/orchestrator/.env | cut -d= -f2)" | python3 -m json.tool
```
- [ ] **2.2** Создать webhook:
```bash
PLANE_WEBHOOK_SECRET=$(openssl rand -hex 20)
echo "ORCH_PLANE_WEBHOOK_SECRET=${PLANE_WEBHOOK_SECRET}" >> /home/slin/repos/orchestrator/.env
curl -s -X POST "http://localhost:8091/api/v1/workspaces/ag_proj/webhooks/" \
-H "X-API-Key: $(grep ORCH_PLANE_API_TOKEN /home/slin/repos/orchestrator/.env | cut -d= -f2)" \
-H "Content-Type: application/json" \
-d '{
"url": "https://openclaw.mva154.duckdns.org/orchestrator/webhook/plane",
"is_active": true,
"secret_key": "'${PLANE_WEBHOOK_SECRET}'",
"project": null
}'
```
Примечание: Plane API для webhooks может отличаться от документации. Если API не работает — настроить через UI (`plane.mva154.duckdns.org` → Settings → Webhooks).
- [ ] **2.3** Если API не поддерживает webhooks — задокументировать и оставить TODO для ручной настройки через UI.
**Критерий готовности:** Webhook создан (через API или UI), URL указывает на Orchestrator.
---
### Task 3: HMAC-верификация подписи в webhook handlers
**Файлы:**
- Изменить: `src/webhooks/plane.py`
- Изменить: `src/webhooks/gitea.py`
- Изменить: `src/config.py`
**Шаги:**
- [ ] **3.1** Добавить верификацию в `src/webhooks/gitea.py`:
```python
import hmac
import hashlib
from ..config import settings
def verify_gitea_signature(body: bytes, signature: str) -> bool:
"""Verify Gitea webhook HMAC-SHA256 signature."""
if not settings.gitea_webhook_secret:
return True # Skip verification if no secret configured
expected = hmac.new(
settings.gitea_webhook_secret.encode(),
body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
@router.post("/gitea")
async def gitea_webhook(request: Request):
body = await request.body()
signature = request.headers.get("X-Gitea-Signature", "")
if not verify_gitea_signature(body, signature):
raise HTTPException(status_code=401, detail="Invalid signature")
# ... rest of handler
```
- [ ] **3.2** Добавить верификацию в `src/webhooks/plane.py`:
```python
# Plane использует свой формат подписи — проверить документацию
# Обычно: X-Plane-Signature header с HMAC-SHA256
def verify_plane_signature(body: bytes, signature: str) -> bool:
if not settings.plane_webhook_secret:
return True
expected = hmac.new(
settings.plane_webhook_secret.encode(),
body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
```
- [ ] **3.3** Добавить `from fastapi import HTTPException` в оба файла
**Критерий готовности:** Запрос без правильной подписи → 401. С правильной → 200.
---
### Task 4: Пересобрать и протестировать
**Шаги:**
- [ ] **4.1** Пересобрать Orchestrator:
```bash
cd /home/slin/repos/orchestrator && docker compose up -d --build
```
- [ ] **4.2** Проверить health:
```bash
curl -s http://localhost:8500/health
```
- [ ] **4.3** Тест Gitea webhook — сделать push в enduro-trails:
```bash
cd /home/slin/repos/enduro-trails
echo "# webhook test $(date)" >> .webhook-test
git add .webhook-test && git commit -m "test: webhook delivery check" && git push origin feature/ET-002-poi-toggle
```
- [ ] **4.4** Проверить что Orchestrator получил event:
```bash
docker logs orchestrator --tail 10 2>&1 | grep -i "gitea\|push"
```
- [ ] **4.5** Проверить в БД:
```bash
docker exec orchestrator python -c "
import sqlite3
conn = sqlite3.connect('/app/data/orchestrator.db')
rows = conn.execute('SELECT id, source, event_type, timestamp FROM events ORDER BY id DESC LIMIT 3').fetchall()
for r in rows: print(r)
"
```
- [ ] **4.6** Удалить тестовый файл:
```bash
cd /home/slin/repos/enduro-trails
git rm .webhook-test && git commit -m "test: cleanup webhook test" && git push origin feature/ET-002-poi-toggle
```
**Критерий готовности:** Push в Gitea → event появляется в Orchestrator БД. Логи показывают обработку.
---
### Task 5: Настроить Plane webhook через UI (если API не сработал)
Если Task 2 не удался через API:
- [ ] **5.1** Документировать инструкцию для ручной настройки:
```
1. Открыть https://plane.mva154.duckdns.org
2. Settings → Webhooks → Add Webhook
3. URL: https://openclaw.mva154.duckdns.org/orchestrator/webhook/plane
4. Secret: <значение из ORCH_PLANE_WEBHOOK_SECRET в .env>
5. Events: All (или work_item.created, comment.created)
6. Save
```
- [ ] **5.2** Сохранить инструкцию в `/home/slin/repos/orchestrator/docs/SETUP_WEBHOOKS.md`
**Критерий готовности:** Документация есть. Webhook либо настроен, либо есть чёткая инструкция.
---
## Проверка (Acceptance)
| # | Проверка | Команда / Действие | Ожидаемый результат |
|---|----------|-------------------|---------------------|
| 1 | Gitea webhook создан | GET /api/v1/repos/admin/enduro-trails/hooks | Webhook с URL orchestrator |
| 2 | Push → event в БД | git push + check DB | Новый event source=gitea |
| 3 | Signature verification | curl без подписи → 401 | 401 Unauthorized |
| 4 | Signature verification | curl с правильной подписью → 200 | 200 accepted |
| 5 | Plane webhook | Создать issue в Plane → check DB | Event source=plane |
---
## Ограничения и контекст
- ⚠️ Plane API для webhooks может не поддерживать программное создание — тогда через UI
- ⚠️ Gitea webhook secret формат: `X-Gitea-Signature: sha256=<hex>`
- ⚠️ НЕ удалять существующие данные в БД
- ⚠️ Если signature verification ломает существующие smoke tests — сделать skip если secret пустой
- 🚫 НЕ менять Nginx конфиг (уже настроен)
- 🚫 НЕ менять порт Orchestrator'а
---
## Деплой-чеклист
- [ ] Webhook secret сгенерирован и добавлен в .env
- [ ] Gitea webhook создан через API
- [ ] Plane webhook создан (API или UI)
- [ ] HMAC verification добавлена в handlers
- [ ] `docker compose up -d --build` успешен
- [ ] Push в Gitea → event в Orchestrator
- [ ] Логи чистые, нет ошибок
---
*Создано: 2026-05-21 | Автор ТЗ: Стрим | Исполнитель: Dev-агент*

View File

@@ -11,8 +11,8 @@
|----------------|--------|-------------|
| **Фаза 0: Инфраструктура** | ✅ Завершена | Всё установлено и работает |
| **Фаза 1: Ручной конвейер** | ✅ Завершена | ET-001 прошёл полный цикл |
| **Фаза 2: Orchestrator MVP** | 🟡 Частично | Каркас задеплоен, QG — заглушки, автозапуск не работает |
| **Фаза 3: Plane интеграция** | 🟡 Начата | Webhook receiver есть, логика обработки — заглушки |
| **Фаза 2: Orchestrator MVP** | ✅ Завершена (21.05) | QG реальные, автозапуск Claude CLI работает, 27 тестов green |
| **Фаза 3: Plane интеграция** | 🟡 В работе (21.05) | Webhook handlers готовы, настройка webhooks в Plane/Gitea UI — в процессе |
| **Фаза 4: Полный конвейер** | ❌ Не начата | — |
| **Фаза 5: Оптимизация** | ❌ Не начата | — |
@@ -90,22 +90,25 @@
- ✅ SQLite БД инициализирована (tables: events, tasks, agent_runs)
- ✅ AgentLauncher — класс написан, конфиги 5 агентов определены
### Что НЕ работает (заглушки)
### Что реализовано (21.05.2026 — DEV_TASK_ORCHESTRATOR_QG)
- `qg/checks.py`все 4 функции возвращают хардкод (True/False), реальных проверок нет
- `handle_work_item_created` — создаёт запись в tasks, но НЕ создаёт ветку и НЕ запускает агента
- `handle_comment``:approved:` детектится, но НЕ определяет задачу и НЕ двигает stage
- `handle_push` / `handle_pr` / `handle_ci_status` — пустые `pass`
- AgentLauncher — ни разу не запускался автоматически (agent_runs: 0)
- ❌ Nginx proxy_pass на `/orchestrator/`не найден (только localhost:8500)
- ❌ Plane webhooks → Orchestrator — не настроены в Plane UI
- ❌ Gitea webhooks → Orchestrator — не настроены в Gitea UI
- `qg/checks.py`5 реальных QG-проверок (filesystem + Gitea API)
- `handle_work_item_created` — создаёт task + ветку в Gitea + папку docs
- `handle_comment``:approved:` → QG check → advance stage → launch agent
- `handle_push` / `handle_pr` / `handle_ci_status` — полная обработка
- AgentLauncher — Claude CLI запускается из контейнера (binary mount)
- ✅ Stage machine (`src/stages.py`)конечный автомат 8 стадий
- ✅ Notifications (`src/notifications.py`) — structured logging
- ✅ 27 тестов — all green
- ✅ Nginx proxy_pass `/orchestrator/` → localhost:8500 (уже был настроен)
- 🟡 Plane webhooks → Orchestrator — настройка в процессе
- 🟡 Gitea webhooks → Orchestrator — настройка в процессе
### Данные в БД
### Данные в БД (на 21.05.2026)
- events: 4 (тестовые, от 19.05.2026 — 2 plane + 2 gitea)
- tasks: 2 (тестовые)
- agent_runs: 0
- events: 15 (4 старых + 11 от smoke tests)
- tasks: 6 (2 старых + 4 smoke tests: ET-001..ET-004)
- agent_runs: 3 (2x architect, 1x developer — реальные запуски Claude CLI)
---