developer(ET): auto-commit from developer run_id=627
Some checks failed
CI / test (push) Failing after 57s
CI / test (pull_request) Failing after 56s

This commit is contained in:
2026-06-11 01:53:21 +03:00
parent eea3553d64
commit ffe02df484
6 changed files with 1244 additions and 3 deletions

View File

@@ -210,9 +210,12 @@ sidecar читает только `.env.watchdog`; C-1 ORCH-100 — отдель
(docs+tests). Подробнее: [adr-0037](adr/adr-0037-lite-replication-canon.md), детально —
`docs/work-items/ORCH-102/06-adr/ADR-001-lite-setup-doc-canon.md`.
**Type B — Bundled (ORCH-103 — design).** Закрывает эпик ORCH-10: весь стек одним комплектом
**Type B — Bundled (ORCH-103).** Закрывает эпик ORCH-10: весь стек одним комплектом
(орк + watchdog + Gitea + Plane CE ≈1314 контейнеров) для заказчика без собственной
инфраструктуры. Новый top-level каталог **`deploy/`** (исполняемые дистрибутивы; дополняет
инфраструктуры. Состав Plane — зеркало официального selfhost-référence v0.23.1
(upstream-имена сервисов web/space/admin/api/worker/beat-worker/migrator/live +
plane-db/plane-redis/plane-mq/plane-minio/proxy); Gitea — `gitea/gitea:1.22.6`
(не rootless, ssh выключен). Новый top-level каталог **`deploy/`** (исполняемые дистрибутивы; дополняет
`docs/deployment/` — инструкции): `deploy/bundled/docker-compose.yml` — один самодостаточный
compose с `name: orchestrator-bundle` (узнаваемый префикс томов/контейнеров; `container_name`
не пиннится — нет коллизий с корневым compose на одном хосте), пиннинг сторонних образов

View File

@@ -0,0 +1,436 @@
# BUNDLED_SETUP — Bundled-тираж: весь стек одним комплектом (ORCH-103)
> **Golden source Bundled-тиража (Type B эпика ORCH-10).** Маршрут «чистый хост →
> работающий конвейер» для заказчика **без собственной инфраструктуры**: один
> compose-комплект (`deploy/bundled/docker-compose.yml`) поднимает оркестратор +
> watchdog + Gitea + Plane CE, один запуск `scripts/bootstrap_bundle.py apply`
> доводит стек до рабочего состояния. Каждый шаг — исполняемая команда + явная
> проверка результата (**Проверка:** / PASS / FAIL). Хост-специфика — только
> плейсхолдеры `<...>` и `$ENV_VAR`. Тираж **stateless**: данные/задачи/секреты
> боевого (исходного) хоста **НЕ переносятся** ни на одном шаге (§12).
> Границы слоёв тиража (10-common vs Lite vs Bundled) — `docs/operations/REPLICATION.md` §1;
> канон Lite (своя инфраструктура Plane/Gitea) — `docs/deployment/LITE_SETUP.md`.
---
## 1. Рамка Bundled
**Что входит в комплект** (compose-проект `orchestrator-bundle`, одна bridge-сеть):
- `orchestrator` (конвейер, образ собирается из этого чекаута) и
`orchestrator-watchdog` (независимый sidecar-мониторинг);
- **Gitea** (git-хостинг, пиннованный официальный образ);
- **Plane CE — ≈ 14 контейнеров** (зеркало официального selfhost-комплекта:
web/space/admin/api/worker/beat-worker/migrator/live + postgres/redis/
rabbitmq/minio/proxy) — это **ресурсоёмко**, см. §2.
**Что НЕ входит** (внешние предусловия заказчика):
- **Claude CLI / LLM-доступ** — дистрибутив claude-code, node и аутентификация
живут на хосте и пробрасываются маунтами (§8); без них стек поднимется, но
конвейер не поедет;
- **Telegram-боты** — опциональны (§9): пусто = деградация только нотификаций;
- **HTTPS/домены/reverse-proxy** — вне bundle: наружу публикуются три http-порта
(§2), терминирование TLS — средствами заказчика.
**Bundled vs Lite:** Lite (`LITE_SETUP.md`) подключает оркестратор к **вашим**
Plane/Gitea; Bundled везёт их **с собой** на чистых томах. Staging-контур орка в
bundle отсутствует вовсе: заказчик Type B эксплуатирует платформу для своих
проектов, а не развивает её self-hosting'ом (нужен self-hosting — маршрут Lite,
`LITE_SETUP.md` §9.3). Репо `orchestrator` в bundle-инсталляции **не
регистрируется** как проект.
**Осознанный компромисс (TR-7):** git-доступ агентов — HTTP token-remote
(токен бот-юзера в конфиге локальных чекаутов, права 600); ssh-контур
сознательно не вводится; порты БД/брокера/minio наружу не публикуются.
---
## 2. Требования к хосту
Linux x86_64, один хост. Минимумы проверяет preflight bootstrap **до любых
мутаций** (пороги — константы `scripts/bootstrap_bundle.py`, ниже — те же цифры;
подтверждаются замером приёмочного развёртывания):
| Ресурс | Минимум | Почему |
|--------|---------|--------|
| RAM | **8 GB** | Plane CE — ≈ 14 контейнеров (миграции и брокер прожорливы) |
| Диск | **40 GB** свободно | образы стека + тома postgres/minio/gitea + данные орка |
| CPU | **4 vCPU** (рекомендация) | меньше — стек поднимется, но будет медленным |
**Карта публикуемых портов** (дефолты; конфигурируемы в
`deploy/bundled/.env`, ключи `BUNDLE_*`):
| Порт | Ключ | Сервис |
|------|------|--------|
| 8500 | `BUNDLE_ORCH_PORT` | API оркестратора (`/health`, `/queue`, `/metrics`, вебхуки) |
| 8080 | `BUNDLE_PLANE_PORT` | Plane UI (proxy) |
| 3000 | `BUNDLE_GITEA_HTTP_PORT` | Gitea web/API |
Postgres/redis/rabbitmq/minio наружу **не публикуются** (машинный трафик —
внутрисетевой сервис-DNS).
```bash
free -g # RAM ≥ 8 GB
df -h . # свободно ≥ 40 GB
nproc # ≥ 4
ss -ltn | grep -E ':(8500|8080|3000)\b' || echo "ports free"
```
**Проверка:** ресурсы не ниже минимумов и `ports free` — PASS. Порт занят →
смените соответствующий `BUNDLE_*`-ключ в §5 (или освободите порт) — иначе
preflight откажет (FAIL до мутаций, это штатно).
---
## 3. Предусловия
Софт хоста: Docker Engine + Compose v2, git, python3 (+venv), sudo у оператора.
```bash
uname -sm # Linux x86_64
docker --version && docker compose version
git --version && python3 --version
python3 -m venv --help >/dev/null && echo "venv: ok"
getent group docker # третье поле — gid, понадобится в §5 (ORCH_DOCKER_GID)
id -u && id -g # uid/gid оператора (ORCH_RUN_UID / ORCH_RUN_GID)
```
**Проверка:** все команды отвечают без ошибок, gid группы docker известен —
PASS; что-то отсутствует — FAIL (доставьте пакет средствами дистрибутива).
---
## 4. Получение кода
Переносится **только код** — чекаут репо `orchestrator` (норматив §12).
```bash
git clone <ORCHESTRATOR_GIT_URL> <путь-чекаута>
cd <путь-чекаута>
ls deploy/bundled/docker-compose.yml deploy/bundled/.env.example \
scripts/bootstrap_bundle.py scripts/gen_secrets.py scripts/onboard_project.py
```
**Проверка:** все пять файлов на месте — PASS. Канал дистрибуции
(`<ORCHESTRATOR_GIT_URL>`) согласуйте с поставщиком платформы (как в
`LITE_SETUP.md` §3).
---
## 5. Секреты
Все секреты инсталляции выпускаются **заново на месте** (§12): webhook-секреты —
`scripts/gen_secrets.py`, внутренние креды Plane/Gitea-стека — генерирует
bootstrap (в репо — только пустые плейсхолдеры, ни одного дефолтного пароля).
**5.1. Конфиг bundle-инфры.**
```bash
cd <путь-чекаута>
cp deploy/bundled/.env.example deploy/bundled/.env
chmod 600 deploy/bundled/.env
# заполнить НЕсекретные ключи: BUNDLE_PUBLIC_HOST (IP/имя хоста для браузера),
# карту портов BUNDLE_* (§2), ORCH_RUN_UID/ORCH_RUN_GID (из §3),
# ORCH_DOCKER_GID (getent group docker, §3), пути Claude CLI (§8).
```
**Проверка:**
```bash
docker compose -f deploy/bundled/docker-compose.yml config --quiet && echo "config: PASS"
```
`config: PASS` — интерполяция согласована; ошибка — FAIL (опечатка в
`deploy/bundled/.env`).
**5.2. Секрет-значения** (пустые ключи `deploy/bundled/.env` и корневого `.env`)
заполнит `bootstrap_bundle.py apply` (§7): webhook-секреты — субпроцессом
`gen_secrets.py`, креды postgres/rabbitmq/minio/`SECRET_KEY` Plane и пароль
админ-бота Gitea — stdlib-генератором. Значения **не печатаются** (только имена
ключей); повторный запуск **не перетирает** существующие секреты (явная
регенерация — флаг `--force-secrets`, допустим только ДО первого запуска стека).
```bash
grep -cE '^(POSTGRES_PASSWORD|SECRET_KEY|RABBITMQ_DEFAULT_PASS|MINIO_ROOT_PASSWORD|GITEA_ADMIN_PASSWORD)=$' \
deploy/bundled/.env
```
**Проверка:** до §7 счётчик `5` (пустые плейсхолдеры) — PASS; после §7 — `0`.
---
## 6. Запуск bundle-compose
Одна команда поднимает весь стек (≈ 16 контейнеров; первый запуск тянет образы
и гоняет миграции Plane — это минуты, не секунды).
```bash
docker compose -f deploy/bundled/docker-compose.yml up -d
docker compose -f deploy/bundled/docker-compose.yml ps
```
**Проверка:** все сервисы в состоянии `Up`/`Up (healthy)`; `migrator`
`Exited (0)` (одноразовая миграция) — PASS. Контейнер в рестарт-цикле — FAIL
(§14). Шаг идемпотентен; можно пропустить — `bootstrap_bundle.py apply` выполнит
`up -d` сам (§7).
---
## 7. Bootstrap
Доводка «одним запуском»: preflight → секреты → up/готовность → init Gitea
(полностью автоматом: админ-бот + API-токен) → init Plane → онбординг
sandbox-проекта **строго** кирпичом `onboard_project.py` (22 канонических
статуса, включая fail-closed **`Confirm Deploy`** и **`STOP`**, лейблы,
репо+webhook — golden source `docs/operations/ONBOARDING.md` §1) → git-доступ
агентов → сборка `.env`/`.env.watchdog` → health.
```bash
python3 scripts/bootstrap_bundle.py # план + preflight-диагностика (ноль мутаций)
python3 scripts/bootstrap_bundle.py apply # полный прогон
```
**Manual-step чекпоинты Plane CE** (API первичной инициализации в CE нет;
каждый чекпоинт: точная инструкция → подтверждение → верификация результата
API-пробой, молчаливый пропуск запрещён):
1. **instance setup** — открыть Plane UI, зарегистрировать первого
пользователя (станет администратором инстанса);
2. **workspace** — создать workspace, ввести его slug в bootstrap;
3. **API-токен** — Workspace Settings → API tokens, вставить значение в
bootstrap (ввод скрыт; уходит в `ORCH_PLANE_API_TOKEN`);
4. **workspace-webhook** — bootstrap регистрирует сам (запись в Postgres
инсталляции, путь Б канона `LITE_SETUP.md` §5.4) и проверяет; при отказе —
честный ручной шаг с той же проверкой;
5. **порядок статусов на доске** — drag-and-drop по отчёту onboard
(`docs/operations/ONBOARDING.md`).
Exit-коды: `0` — успех; `2` — остановка на manual-step/предусловии (выполните
шаг и перезапустите `apply` — завершённые шаги пропускаются, повторный запуск
безопасен); `1` — ошибка. Пароль админ-бота Gitea — ключ `GITEA_ADMIN_PASSWORD`
в `deploy/bundled/.env` (права 600; вход в UI Gitea под
`GITEA_ADMIN_USERNAME`).
**Проверка:**
```bash
python3 scripts/bootstrap_bundle.py verify && echo "bootstrap: PASS"
```
`verify` зелёный (health/queue/metrics + onboard verify) — PASS; exit 2 —
остались ручные пункты отчёта; exit 1 — FAIL (§14).
---
## 8. LLM (claude CLI)
Канон — `LITE_SETUP.md` §7 (полностью применим; не дублируется). Кратко: на
хост ставятся claude-code + node, выполняется интерактивный логин CLI; пути
прописываются в `deploy/bundled/.env` (это источники маунтов контейнера орка):
`ORCH_HOST_CLAUDE_CODE_DIR`, `ORCH_HOST_NODE_BIN`, `ORCH_HOST_CLAUDE_DIR`,
`ORCH_HOST_CLAUDE_JSON`.
```bash
claude --version
docker compose -f deploy/bundled/docker-compose.yml exec orchestrator /usr/bin/claude --version
```
**Проверка:** обе команды печатают версию — PASS; вторая падает — пути в
`deploy/bundled/.env` не указывают на фактические каталоги хоста (§14.4).
---
## 9. Telegram
Канон — `LITE_SETUP.md` §8 (два независимых бота, C-1: токен орка для watchdog
переиспользовать запрещено). Ключи орка (`ORCH_TELEGRAM_BOT_TOKEN`,
`ORCH_TELEGRAM_CHAT_ID`) — в корневой `.env`; ключи watchdog
(`WATCHDOG_TG_BOT_TOKEN`, `WATCHDOG_TG_CHAT_ID`) — **только** в `.env.watchdog`
(файл-носитель, `LITE_SETUP.md` §4.3). Шаг опционален: пустые токены =
деградация только нотификаций.
```bash
grep -E '^ORCH_TELEGRAM_(BOT_TOKEN|CHAT_ID)=' .env
grep -E '^WATCHDOG_TG_(BOT_TOKEN|CHAT_ID)=' .env.watchdog
docker compose -f deploy/bundled/docker-compose.yml up -d orchestrator orchestrator-watchdog
```
**Проверка:** ключи заполнены и контейнеры пересозданы → тестовое сообщение от
обоих ботов (`getMe` — команды в `LITE_SETUP.md` §8) — PASS; пусто — осознанный
PASS без нотификаций.
---
## 10. Онбординг следующих проектов
Sandbox-проект создал bootstrap (§7). Каждый следующий проект заказчика —
штатный runbook `docs/operations/ONBOARDING.md` поверх bundle-инсталляции; для
команд из чекаута: Plane/Gitea доступны на `localhost`-портах §2, webhook-URL —
in-network `http://orchestrator:8500/webhook/gitea`.
```bash
. .venv/bin/activate # venv создан bootstrap'ом (§7)
python3 scripts/onboard_project.py plan \
--name "<имя проекта>" --repo <repo> --prefix <PREFIX> \
--stack "<стек>" --test-cmd "<команда тестов>" \
--prod-port <порт-прода> --staging-port <порт-staging> \
--webhook-url http://orchestrator:8500/webhook/gitea
# план устроил → apply → verify (как в LITE_SETUP.md §10), затем:
# строку ORCH_PROJECTS_JSON из отчёта — в .env и пересоздать орк:
docker compose -f deploy/bundled/docker-compose.yml up -d --force-recreate orchestrator
```
**Проверка:** `verify` зелёный; `GET /queue` отвечает после пересоздания — PASS.
---
## 11. Smoke
Процедура — чек-лист `docs/operations/REPLICATION.md` §4 (шаги 05; шаг 6 «до
`done`» — опционально) поверх bundle-инсталляции, без форка. Минимальный сигнал
«конвейер доехал»: issue в sandbox-проекте Plane → статус **To Analyse**
артефакты `01``04` в ветке задачи.
```bash
curl -fsS http://127.0.0.1:8500/health
curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool | head -30
curl -fsS http://127.0.0.1:8500/metrics | python3 -m json.tool | head -10
# создать issue в Plane (порт 8080) → перевести в «To Analyse», затем:
curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool | head -40 # job появился
git -C deploy/bundled/repos/sandbox fetch origin
git -C deploy/bundled/repos/sandbox ls-tree --name-only origin/<ветка-задачи> "docs/work-items/<id>/"
```
**Проверка:** оба направления связности живы — job в `/queue` (Plane→орк
доехал), `ls-tree` показывает `01-brd.md``04-test-plan.yaml` (орк→Gitea
пишет; Gitea→орк события идут) — PASS. Любой шаг FAIL → тираж FAIL: соберите
`docker compose -f deploy/bundled/docker-compose.yml logs --tail 100 orchestrator`
и снапшот `GET /queue`, разбор — §14. (Порты замените, если меняли `BUNDLE_*`.)
---
## 12. Stateless-проверка
**Нормативно: данные/задачи/секреты/БД боевого (исходного) хоста НЕ
переносятся** (зеркало `docs/operations/REPLICATION.md` §5). Все тома bundle
созданы заново при первом `up`; секреты — только свежевыпущенные (§5); в
Plane/Gitea инсталляции нет чужих задач/репо/пользователей.
```bash
docker volume ls --format '{{.Name}}' | grep '^orchestrator-bundle' # только тома этой инсталляции
curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool # счётчики нулевые
```
**Проверка:** в `/queue` нулевые счётчики и ни одной чужой задачи (никаких
work-item исходного хоста) — PASS. Чужая задача/перенесённый файл БД — FAIL:
инсталляция собрана не stateless, выполните полный сброс (§13) и повторите.
---
## 13. Остановка и полный сброс
Teardown — **только эта документированная процедура** (в bootstrap delete-режима
сознательно нет, ADR-001 D9).
**Остановка (обратимая):**
```bash
docker compose -f deploy/bundled/docker-compose.yml down
```
**Проверка:** `docker compose -f deploy/bundled/docker-compose.yml ps` пуст;
тома целы (`docker volume ls | grep orchestrator-bundle`) — PASS.
**Полный сброс (НЕОБРАТИМО — удаляет все данные Plane/Gitea/орка):**
```bash
docker compose -f deploy/bundled/docker-compose.yml down -v
rm -rf deploy/bundled/data deploy/bundled/repos
rm -f deploy/bundled/.env .env .env.watchdog
```
**Проверка:** `docker volume ls --format '{{.Name}}' | grep -c '^orchestrator-bundle'`
`0`; live-конфигов нет — PASS (хост чист, можно разворачивать заново с §5).
---
## 14. Траблшутинг
Формат: симптом → диагностика → лечение.
**14.1. Webhook не доходит (issue в Plane есть, job в `/queue` нет).**
```bash
docker compose -f deploy/bundled/docker-compose.yml logs --tail 50 orchestrator | grep -i "webhook\|signature"
docker compose -f deploy/bundled/docker-compose.yml exec -T plane-db \
psql -U plane -d plane -c "SELECT url, is_active FROM webhooks;"
```
Лечение: (а) нет строки webhook → §7 чекпоинт 4; (б) URL не
`http://orchestrator:8500/webhook/plane` → исправьте на in-network URL;
(в) 401/HMAC → секрет в Plane обязан байт-в-байт совпадать с
`ORCH_PLANE_WEBHOOK_SECRET` корневого `.env`. Для Gitea-направления проверьте
Recent Deliveries в настройках hook'а репо; помните про
`GITEA__webhook__ALLOWED_HOST_LIST=orchestrator` в bundle-compose (без него
Gitea молча режет вебхуки в приватные адреса).
**14.2. Не хватает RAM / OOM (контейнеры Plane в рестарт-цикле).**
```bash
free -g && docker stats --no-stream | head -20
docker compose -f deploy/bundled/docker-compose.yml ps
```
Лечение: минимум §2 (8 GB; Plane ≈ 14 контейнеров). Меньше — добавьте RAM/swap;
preflight bootstrap отказывает заранее именно поэтому.
**14.3. Порт занят (`up` падает с bind error).**
```bash
ss -ltnp | grep -E ':(8500|8080|3000)\b'
```
Лечение: смените `BUNDLE_ORCH_PORT`/`BUNDLE_PLANE_PORT`/`BUNDLE_GITEA_HTTP_PORT`
в `deploy/bundled/.env` и повторите `up`/bootstrap.
**14.4. claude не найден / агент падает на старте.**
```bash
docker compose -f deploy/bundled/docker-compose.yml exec orchestrator /usr/bin/claude --version
ls "$(grep '^ORCH_HOST_CLAUDE_CODE_DIR=' deploy/bundled/.env | cut -d= -f2)"
```
Лечение: пути `ORCH_HOST_*` в `deploy/bundled/.env` обязаны указывать на
фактические каталоги хоста; креды CLI читаемы uid'ом `ORCH_RUN_UID` (канон —
`LITE_SETUP.md` §7/§13.3); после правки — `up -d --force-recreate orchestrator`.
**14.5. Миграции Plane не завершились (bootstrap падает на ожидании).**
```bash
docker compose -f deploy/bundled/docker-compose.yml logs --tail 50 migrator plane-db
docker compose -f deploy/bundled/docker-compose.yml ps plane-db plane-mq plane-redis
```
Лечение: чаще всего — нехватка RAM/диска (§14.2) или невыпущенные секреты
(пустой `POSTGRES_PASSWORD` → postgres не стартует; прогоните §7, который
заполняет креды ДО `up`). После лечения — повторный `apply` (идемпотентен).
**14.6. PR задачи не мержится / HOLD.** Branch protection на `main` в Gitea
**НЕ включать** — норматив `LITE_SETUP.md` §6.4 (ломает PR-merge API
merge-актора); bundle-Gitea конфигурируется тем же правилом.
```bash
curl -fsS -H "Authorization: token $ORCH_GITEA_TOKEN" \
"http://127.0.0.1:3000/api/v1/repos/<owner>/<repo>/branch_protections" | python3 -m json.tool
```
Лечение: непустой список правил → удалить (канон `LITE_SETUP.md` §6.4/§13.7).
---
*Golden source Bundled-тиража (ORCH-103, ADR-001 D10). **Норматив сопровождения
(NFR-5):** меняешь шаги Bundled-тиража (состав bundle-compose, ключи
`deploy/bundled/.env.example`, шаги bootstrap, smoke) → обнови этот док В ТОМ ЖЕ
PR. Полноту и гигиену держит `tests/test_bundled_setup_doc.py`; кирпичи-каноны:
`LITE_SETUP.md` (§5§8 — подключения), `docs/operations/ONBOARDING.md` (статусы
§1, онбординг), `docs/operations/REPLICATION.md` (карта env §2, секреты §3,
smoke §4), `deploy/bundled/.env.example` + `.env.example` /
`.env.watchdog.example` (каноны ключей).*

View File

@@ -13,7 +13,7 @@
|------|---------|--------|
| **10-common** (этот док) | фундамент: все хост-значения параметризованы (env/конфиг), секреты выпускаются заново, smoke-процедура с PASS/FAIL | ✅ ORCH-101 |
| **Type A — Lite** | инструкция «поставь Plane+Gitea сам, подключи оркестратор» поверх 10-common | ✅ ORCH-102 — [`docs/deployment/LITE_SETUP.md`](../deployment/LITE_SETUP.md) |
| **Type B — Bundled** | комплект «всё в одном» (Plane+Gitea+оркестратор) поверх 10-common | отдельная задача эпика |
| **Type B — Bundled** | комплект «всё в одном» (Plane+Gitea+оркестратор) поверх 10-common | ✅ ORCH-103 — [`docs/deployment/BUNDLED_SETUP.md`](../deployment/BUNDLED_SETUP.md) |
Этот док НЕ описывает установку Plane/Gitea — только параметризацию, секреты и
smoke самого оркестратора (анти-скоуп-крип Р-5).