auto-sync: 2026-06-04 23:40:01
This commit is contained in:
@@ -9,3 +9,16 @@
|
|||||||
- **Записала в ORCH-30** (id `85da2b10-6dc4-4693-b611-e96c6117f565`, seq 30, Backlog): анализ + 3 варианта (A эфемерный CI-стенд / B постоянный staging / C гибрид — рекомендую) + правки методики (стадия deploy-staging ПЕРЕД deploy-prod, тестер на staging-эндпоинт, build-once-promote, seed-фикстуры) + 5 открытых вопросов Славе. Связь: блокирует ORCH-7, пересекается с ORCH-21.
|
- **Записала в ORCH-30** (id `85da2b10-6dc4-4693-b611-e96c6117f565`, seq 30, Backlog): анализ + 3 варианта (A эфемерный CI-стенд / B постоянный staging / C гибрид — рекомендую) + правки методики (стадия deploy-staging ПЕРЕД deploy-prod, тестер на staging-эндпоинт, build-once-promote, seed-фикстуры) + 5 открытых вопросов Славе. Связь: блокирует ORCH-7, пересекается с ORCH-21.
|
||||||
- **Грабли Plane API (запомнить):** env `ORCH_PLANE_API_URL=http://localhost:8091` БЕЗ `/api/v1` — суффикс добавляет код (`plane_sync.py:15 PLANE_BASE=f"{url}/api/v1"`). Если дёргать API вручную — добавлять `/api/v1` самой, иначе Plane вернёт HTML (200) вместо JSON. Заголовок `X-API-Key`. Скрипт лить в контейнер orchestrator через base64-stdin+docker cp (scp на воркстейшене НЕТ, прямого доступа к localhost:8091 из контейнера ассистента НЕТ).
|
- **Грабли Plane API (запомнить):** env `ORCH_PLANE_API_URL=http://localhost:8091` БЕЗ `/api/v1` — суффикс добавляет код (`plane_sync.py:15 PLANE_BASE=f"{url}/api/v1"`). Если дёргать API вручную — добавлять `/api/v1` самой, иначе Plane вернёт HTML (200) вместо JSON. Заголовок `X-API-Key`. Скрипт лить в контейнер orchestrator через base64-stdin+docker cp (scp на воркстейшене НЕТ, прямого доступа к localhost:8091 из контейнера ассистента НЕТ).
|
||||||
- enduro-deploy-hook УЖЕ умеет rollback (PREV_IMG) — пригодится для ORCH-21/staging.
|
- enduro-deploy-hook УЖЕ умеет rollback (PREV_IMG) — пригодится для ORCH-21/staging.
|
||||||
|
|
||||||
|
### 🏗️ Staging-среда orchestrator — дизайн согласован, 5 задач заведены (04.06)
|
||||||
|
- Решения Славы: (1) постоянный staging-контейнер, (2) полная изоляция БД, (3) вариант b (песочница: тестовый Plane-проект + Gitea-репо), (4) промоут — на усмотрение Стрим («автономно и надёжно»), (5) не только smoke — реальные прогоны/статусы/комменты Plane + проверка доступов + интеграционное тестирование. (6) агенты = гибрид C (заглушки по дефолту + full-real по флагу). (7) approve — ручной на старте.
|
||||||
|
- **Ключевой принцип (решение Стрим):** деплоем орка рулит ВНЕШНИЙ хост-хук, НЕ сам контейнер (иначе убивает себя на середине self-деплоя). Рубильник снаружи.
|
||||||
|
- **Дизайн:** `tasks/orchestrator/DESIGN_STAGING_ENV.md`
|
||||||
|
- **Задачи (все в Backlog, НЕ запускать — пайплайн стартует только при In Progress):**
|
||||||
|
- ORCH-31 (`5a68a13c-3f3b-4d9e-91f7-24b0794bed06`) — staging-инфра (контейнер 8501, изолир. БД)
|
||||||
|
- ORCH-32 (`3dadcf0d-f1b6-4302-8075-8d428ace01f6`) — песочница Plane+Gitea
|
||||||
|
- ORCH-33 (`a11a7c0f-a78a-4309-9aa5-1c43d4355d0f`) — тест-сьют (smoke+доступы+e2e)
|
||||||
|
- ORCH-34 (`47fe9e75-d7ff-4ecb-bb99-67af9b23ab67`) — хост-деплой-хук + promote + auto-rollback (ядро, пересекается с ORCH-21)
|
||||||
|
- ORCH-35 (`4ead9be7-e1bf-4a28-8c76-88bda1c1fc2c`) — встройка в методику (deploy-staging перед deploy-prod)
|
||||||
|
- **Backlog state id ORCH:** `2d5d42ff-e94d-4209-a664-8020c28c2a95`
|
||||||
|
- Слава: «делай все этапы, минимум вопросов, installer skill если нужно, следи чтобы задачи случайно не запустились».
|
||||||
|
|||||||
60
tasks/orchestrator/DEV_TASK_FIX_WEBHOOK_SECRET_ISOLATION.md
Normal file
60
tasks/orchestrator/DEV_TASK_FIX_WEBHOOK_SECRET_ISOLATION.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# DEV TASK — изолировать webhook-секрет между тестами (CI зелёный на полном прогоне)
|
||||||
|
|
||||||
|
**Проект:** orchestrator | **Сервер:** slin@82.22.50.71 (pw motoZ@yaz2010) | **Репо:** /home/slin/repos/orchestrator | **Контейнер:** orchestrator
|
||||||
|
**Ветка УЖЕ существует:** `fix/isolate-webhook-tests-from-plane` (коммиты 7bbab9c, e856e09). РАБОТАТЬ В НЕЙ, добавить новый коммит сверху. `git fetch origin && git checkout fix/isolate-webhook-tests-from-plane && git pull --ff-only`.
|
||||||
|
|
||||||
|
## ПРОБЛЕМА (диагностирована, причина железная)
|
||||||
|
- `test_webhooks.py` поодиночке = 16 passed. Но в ПОЛНОМ прогоне `pytest tests/` (как делает CI) 10 webhook-тестов падают с `assert 401 == 200`.
|
||||||
|
- Причина: `settings` (Pydantic, синглтон в src/config.py) читает webhook-секрет один раз. Разные тест-файлы по-разному управляют секретом:
|
||||||
|
- `test_webhooks.py:10-11` ставит `os.environ["ORCH_PLANE_WEBHOOK_SECRET"]=""` ЖЁСТКО на import-time.
|
||||||
|
- Остальные файлы (test_m6_sequence, test_plane_webhook и т.д.) — через `os.environ.setdefault(...,"")` (только если не задан).
|
||||||
|
- `test_webhook_dedup.py:268,278` временно ставит реальный секрет `"s3cr3t"` прямо в `settings` через `monkeypatch.setattr`.
|
||||||
|
- В зависимости от ПОРЯДКА сбора тестов pytest в `settings` протекает ненулевой секрет → HMAC-проверка включается → webhook-тесты получают 401 вместо 200.
|
||||||
|
|
||||||
|
## РЕШЕНИЕ — узкая autouse-фикстура в conftest.py (НЕ трогать HMAC-механизм!)
|
||||||
|
Добавить в `tests/conftest.py` autouse-фикстуру, которая ПЕРЕД КАЖДЫМ тестом сбрасывает webhook-секрет на module-level `settings` в `""` — РОВНО тем же проверенным паттерном, что УЖЕ применён в `test_webhook_dedup.py:80-81`:
|
||||||
|
```python
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def _reset_webhook_secrets(monkeypatch):
|
||||||
|
"""Изоляция: webhook-секрет протекает между тест-файлами через синглтон settings
|
||||||
|
(settings читается один раз). Сбрасываем его в "" перед каждым тестом, чтобы HMAC
|
||||||
|
по умолчанию выключен. Тесты, которым нужен реальный секрет (test_webhook_dedup),
|
||||||
|
переопределяют его СВОИМ monkeypatch ПОСЛЕ этой фикстуры — она им не мешает."""
|
||||||
|
import src.webhooks.gitea as gitea_mod
|
||||||
|
import src.webhooks.plane as plane_mod
|
||||||
|
monkeypatch.setattr(gitea_mod.settings, "gitea_webhook_secret", "", raising=False)
|
||||||
|
monkeypatch.setattr(plane_mod.settings, "plane_webhook_secret", "", raising=False)
|
||||||
|
```
|
||||||
|
(Проверь точные имена модулей/атрибутов settings по факту — в test_webhook_dedup.py образец: `gitea_mod.settings.gitea_webhook_secret`, `plane_mod.settings.plane_webhook_secret`. Импорты settings в webhooks могут быть `from ..config import settings` — фикстура должна патчить ТОТ объект settings, что реально используется в проверке подписи. Если в gitea.py/plane.py секрет читается не через `settings.X`, а иначе — СТОП, отчёт, не угадывай.)
|
||||||
|
|
||||||
|
## ⛔ OFF-LIMITS (НЕ ТРОГАТЬ — иначе СТОП и отчёт)
|
||||||
|
- НЕ менять сам HMAC-механизм / проверку подписи в src/webhooks/*.py.
|
||||||
|
- НЕ менять 9 HMAC/401 тестов (те, что проверяют ОТКЛОНЕНИЕ при неверной подписи). Фикстура сбрасывает секрет в "", но тесты, которые СПЕЦИАЛЬНО ставят секрет (test_webhook_dedup.py:268,278) и проверяют 401 — должны продолжать работать, т.к. они переопределяют секрет своим monkeypatch ПОСЛЕ autouse-фикстуры (порядок: autouse применяется первой, тестовый monkeypatch — внутри теста, перекрывает). ПРОВЕРЬ это явно прогоном.
|
||||||
|
- НЕ трогать src/config.py, логику settings.
|
||||||
|
- Если autouse-фикстура ломает хоть один из 401/HMAC/dedup тестов → СТОП, отчёт. Значит подход надо скорректировать — НЕ продавливать.
|
||||||
|
|
||||||
|
## ПРОВЕРКА (обязательный пруф в отчёт)
|
||||||
|
1. ПОЛНЫЙ прогон как в CI (чистое окружение, read-only, без .env):
|
||||||
|
```
|
||||||
|
docker run --rm -v /home/slin/repos/orchestrator:/code:ro -w /code -e PYTHONPATH=/code \
|
||||||
|
--entrypoint python3 $(docker inspect orchestrator --format '{{.Config.Image}}') \
|
||||||
|
-m pytest tests/ -q
|
||||||
|
```
|
||||||
|
— ДОЛЖНО быть **0 failed** (было 10 failed / 284 passed). Приложи итоговую строку.
|
||||||
|
2. Отдельно прогнать dedup/HMAC-тесты, убедиться что 401-проверки ЖИВЫ (не сломаны фикстурой):
|
||||||
|
```
|
||||||
|
... -m pytest tests/test_webhook_dedup.py -q
|
||||||
|
```
|
||||||
|
— все passed. Приложи.
|
||||||
|
3. test_webhooks.py отдельно — по-прежнему passed.
|
||||||
|
4. После push: workflow прогнан, job test → **success** (был failure). `/api/v1/repos/admin/orchestrator/actions/tasks`, токен `docker exec orchestrator printenv ORCH_GITEA_TOKEN`. ⚠️ ЭТО ГЛАВНЫЙ КРИТЕРИЙ — CI наконец зелёный.
|
||||||
|
5. `git log --oneline origin/main..origin/fix/isolate-webhook-tests-from-plane` — три коммита (7bbab9c, e856e09, твой новый).
|
||||||
|
|
||||||
|
## ПРАВИЛА
|
||||||
|
- Менять ТОЛЬКО `tests/conftest.py` (добавить одну фикстуру). Если для работы фикстуры строго необходимо что-то ещё в tests/ — обоснуй в отчёте, но src/ НЕ трогать.
|
||||||
|
- ⚠️ НЕ регистрировать раннеров, НЕ nohup. Раннер mva154-runner-orch уже есть.
|
||||||
|
- PR push в main ЗАПРЕЩЁН. НЕ мержить — ревьюер (Стрим).
|
||||||
|
- Conventional Commits. Если факт разошёлся с ТЗ — СТОП, отчёт с вопросом.
|
||||||
|
|
||||||
|
## РЕЗУЛЬТАТ
|
||||||
|
Новый коммит в ветке: `tests/conftest.py` с autouse-фикстурой сброса webhook-секрета. Полный `pytest tests/` = 0 failed в чистом окружении, HMAC/dedup-тесты живы, CI job test = success. PR #27 зелёный. Отчёт → `tasks/orchestrator/reports/dev-2026-06-04-fix-webhook-secret-isolation.md`. Коммит: `test: reset webhook secret per-test to fix cross-file isolation (CI green)`.
|
||||||
57
tasks/orchestrator/DEV_TASK_ORCH31_STAGING_INFRA.md
Normal file
57
tasks/orchestrator/DEV_TASK_ORCH31_STAGING_INFRA.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# DEV TASK — ORCH-31: Staging-инфраструктура orchestrator (контейнер 8501, изолированная БД)
|
||||||
|
|
||||||
|
**Проект:** orchestrator | **Сервер:** slin@82.22.50.71 (pw motoZ@yaz2010) | **Репо:** /home/slin/repos/orchestrator | **Контейнер прод:** orchestrator (8500)
|
||||||
|
**Дизайн:** tasks/orchestrator/DESIGN_STAGING_ENV.md (§1, §2) | **Plane:** ORCH-31 (id 5a68a13c-3f3b-4d9e-91f7-24b0794bed06)
|
||||||
|
**Ветка:** `feature/ORCH-31-staging-infra` из свежего origin/main.
|
||||||
|
|
||||||
|
## ЦЕЛЬ (Этап 1 из 5)
|
||||||
|
Добавить в репо возможность поднять ПОСТОЯННЫЙ staging-инстанс орка на порту 8501, с БД, ПОЛНОСТЬЮ изолированной от прод-очереди задач. На этом этапе — ТОЛЬКО инфра-обвязка (compose + конфиг + доки). Песочница Plane/Gitea, тест-сьют, деплой-хук — это ORCH-32/33/34, НЕ здесь.
|
||||||
|
|
||||||
|
## ФАКТЫ О ТЕКУЩЕМ УСТРОЙСТВЕ (проверено на проде, не угадывай)
|
||||||
|
- `docker-compose.yml`: один service `orchestrator`, `build: .`, `container_name: orchestrator`, `network_mode: host`, `restart: unless-stopped`, `init: true`, `env_file: .env`, `group_add: ["999"]`.
|
||||||
|
- Запуск (Cmd образа): `uvicorn src.main:app --host 0.0.0.0 --port 8500`.
|
||||||
|
- Volumes прод: `./data:/app/data`, `/home/slin/repos:/repos`, `/var/run/docker.sock`, claude-code (ro), node (ro), `/home/slin/.claude` (rw), `/home/slin/.claude.json` (ro), `/home/slin/.orchestrator-ssh:/root/.ssh` (ro).
|
||||||
|
- БД: `src/config.py:43` → `db_path: str = "/app/data/orchestrator.db"`, переопределяется env `ORCH_DB_PATH`. Подключение `src/db.py:6` `sqlite3.connect(settings.db_path)`. **ORCH_DB_PATH уже параметризован** — изоляция БД через него.
|
||||||
|
- `network_mode: host` → порт задаётся в команде uvicorn, мапить порты не нужно. Два контейнера на host-сети сосуществуют, пока порты разные (8500 vs 8501).
|
||||||
|
|
||||||
|
## ЧТО СДЕЛАТЬ
|
||||||
|
|
||||||
|
### 1. Новый compose-service `orchestrator-staging`
|
||||||
|
Добавить в `docker-compose.yml` второй service `orchestrator-staging` ПОД ПРОФИЛЕМ `staging` (чтобы обычный `docker compose up -d` НЕ поднимал его случайно — только `docker compose --profile staging up -d orchestrator-staging`):
|
||||||
|
- `profiles: ["staging"]`
|
||||||
|
- `build: .` (тот же Dockerfile) ИЛИ `image: orchestrator:candidate` — см. примечание ниже.
|
||||||
|
- `container_name: orchestrator-staging`
|
||||||
|
- `network_mode: host`, `init: true`, `restart: unless-stopped`, `group_add: ["999"]` — как у прода.
|
||||||
|
- **Команда/порт:** переопределить запуск на порт **8501**. Если в Dockerfile порт зашит в CMD — переопредели через `command:` в compose: `command: ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8501"]`.
|
||||||
|
- `env_file: .env.staging` (НЕ .env прода). Файл .env.staging НА ЭТОМ ЭТАПЕ НЕ КОММИТИТЬ (секреты); вместо него закоммитить `\.env.staging.example` с именами ключей и комментариями (включая ORCH_DB_PATH). Реальный .env.staging создаётся при выкате (отдельный шаг через installer, не в репо).
|
||||||
|
- **Volumes (изоляция БД!):** `./data/staging:/app/data` — отдельный подкаталог, чтобы staging-БД (`/app/data/orchestrator.db` внутри → физически `./data/staging/orchestrator.db` на хосте) НЕ делила файл с продом (`./data`). Остальные mount'ы как у прода (repos, docker.sock, claude-code, node, .claude, ssh) — staging должен уметь то же, что прод.
|
||||||
|
- `environment`: те же ORCH_REPOS_DIR=/repos, ORCH_HOST_REPOS_DIR, DEPLOY_* как у прода. ДОБАВИТЬ явно `ORCH_DB_PATH=/app/data/orchestrator.db` (внутри контейнера; на хосте это ./data/staging/ — изолировано через volume) — чтобы было очевидно и не зависело от дефолта.
|
||||||
|
|
||||||
|
> ⚠️ Примечание про образ: на Этапе 1 допустимо `build: .` (staging собирается из текущего кода). Тег `orchestrator:candidate` и promote — это ORCH-34. Здесь не усложняй.
|
||||||
|
|
||||||
|
### 2. `.env.staging.example`
|
||||||
|
Создать файл `\.env.staging.example` (в корне репо, коммитится) — копия структуры .env с именами всех ключей (ORCH_PLANE_*, ORCH_GITEA_*, ORCH_TELEGRAM_*, ORCH_DB_PATH, ORCH_CLAUDE_BIN, ORCH_REPOS_DIR и т.д.), значения — плейсхолдеры/комментарии. Пометить комментарием вверху: «STAGING env. Plane/Gitea токены и sandbox-проект — ORCH-32. На Этапе 1 можно скопировать с прод .env, поменяв только то, что относится к изоляции.»
|
||||||
|
|
||||||
|
### 3. `.gitignore`
|
||||||
|
Убедиться, что `.env.staging` (реальный) в `.gitignore` (как .env). Добавить если нет. `data/staging/` тоже в gitignore (как data/).
|
||||||
|
|
||||||
|
### 4. Документация
|
||||||
|
Создать `docs/STAGING.md` (или дополнить существующую доку): как поднять staging (`docker compose --profile staging up -d orchestrator-staging`), порт 8501, где БД (./data/staging/), что .env.staging создаётся отдельно. Кратко — на этапе 1 это инфра, песочница/тесты/деплой будут в следующих задачах.
|
||||||
|
|
||||||
|
## ПРОВЕРКА (приложить пруф в отчёт)
|
||||||
|
1. `docker compose config` (на хосте, в репо) — валиден, виден service orchestrator-staging под профилем staging.
|
||||||
|
2. ОБЫЧНЫЙ `docker compose config --profiles ""` или `docker compose up -d --dry-run` (без --profile staging) НЕ включает orchestrator-staging (доказать, что случайно не поднимется).
|
||||||
|
3. НЕ поднимать staging на этом этапе на проде (нет .env.staging — это шаг выката, делает Стрим). Достаточно валидации конфига.
|
||||||
|
4. `git log origin/main..origin/feature/ORCH-31-staging-infra` показывает коммит.
|
||||||
|
|
||||||
|
## ЗАПРЕТЫ / ПРАВИЛА
|
||||||
|
- ⚠️ НЕ трогать прод-service `orchestrator` в compose (только ДОБАВИТЬ новый). НЕ менять порт прода, volumes прода, .env прода.
|
||||||
|
- ⚠️ НЕ поднимать staging-контейнер на проде (нет секретов, это отдельный шаг). Только код+валидация конфига.
|
||||||
|
- ⚠️ НЕ регистрировать раннеров, НЕ nohup.
|
||||||
|
- ⚠️ НЕ коммитить реальные секреты/.env.staging — только .example.
|
||||||
|
- PR в Gitea, push в main ЗАПРЕЩЁН (pre-receive hook). НЕ мержить — мержит ревьюер (Стрим).
|
||||||
|
- docker-compose.yml — критический конфиг: правка через PR, выкат согласует Стрим.
|
||||||
|
- Conventional Commits. Если факт ТЗ разошёлся с реальностью — СТОП, отчёт с вопросом.
|
||||||
|
|
||||||
|
## РЕЗУЛЬТАТ
|
||||||
|
PR с изменениями: docker-compose.yml (+service staging под профилем), `.env.staging.example`, `.gitignore`, `docs/STAGING.md`. Конфиг валиден, staging НЕ поднимается без явного профиля, прод не затронут. Отчёт → `tasks/orchestrator/reports/dev-2026-06-04-orch31-staging-infra.md`. Коммит: `feat(staging): add isolated orchestrator-staging service (port 8501, separate DB)`.
|
||||||
Reference in New Issue
Block a user