auto-sync: 2026-05-21 16:00:01
This commit is contained in:
284
tasks/multi-agent/DEV_TASK_WEBHOOKS.md
Normal file
284
tasks/multi-agent/DEV_TASK_WEBHOOKS.md
Normal 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-агент*
|
||||
@@ -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)
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user