272 lines
26 KiB
Markdown
272 lines
26 KiB
Markdown
---
|
||
work_item: ORCH-104
|
||
stage: analysis
|
||
author_agent: analyst
|
||
status: ready-for-review
|
||
created_at: 2026-06-11
|
||
model_used: claude-opus-4-8
|
||
---
|
||
|
||
# 02 — ТЗ (TRZ): ORCH-104 — Установочный скрипт Lite-тиража (интерактивный installer)
|
||
|
||
Work Item: **ORCH-104** · Repo: **orchestrator** · Стадия: analysis
|
||
|
||
> ТЗ описывает **что** должно измениться и **где** (модули/контракты/артефакты). **Как**
|
||
> (имя/режимы скрипта, эвристики discovery, степень автоматизации отдельных шагов) — решает
|
||
> архитектор в `06-adr/`. Тип изменения — **scripts + docs + tests** (паттерн ORCH-009/103):
|
||
> рантайм `src/**` байт-в-байт.
|
||
|
||
---
|
||
|
||
## 1. Сводка изменения
|
||
|
||
Ввести **единый операторский установочный скрипт Lite-тиража**: один файл в `scripts/`,
|
||
который интерактивно проводит внешнего оператора по маршруту `docs/deployment/LITE_SETUP.md`
|
||
§2–§12 — сканирует предусловия хоста и предлагает доустановить недостающее, обнаруживает
|
||
инсталляции Plane/Gitea (при нескольких — даёт выбрать), запрашивает обязательные ключи в
|
||
момент установки с немедленной верификацией, автодетектит хост-параметры, собирает
|
||
`.env`/`.env.watchdog` от канонов, поднимает Lite-контур, регистрирует проект заказчика
|
||
кирпичом `onboard_project.py` и выдаёт итоговый вердикт PASS/MANUAL/FAIL с exit-кодами
|
||
`0/2/1`. Паттерн — `bootstrap_bundle.py` (ORCH-103): step-движок check→ensure, stdlib-only,
|
||
no-delete, manual-checkpoint с верификацией, идемпотентный повтор. Канон LITE_SETUP.md не
|
||
форкается — скрипт становится в нём рекомендованным быстрым путём, ручной маршрут сохраняется.
|
||
|
||
---
|
||
|
||
## 2. Задействованные модули / пути
|
||
|
||
| Путь | Действие | Роль в задаче |
|
||
|------|----------|---------------|
|
||
| `scripts/setup_lite.py` *(имя-кандидат; финал — архитектор)* | **создать** | единый установочный CLI: step-движок, скан, discovery, интерактивный сбор, сборка конфигов, запуск, отчёт |
|
||
| `scripts/gen_secrets.py` | переиспользовать, **не менять** | кирпич выпуска webhook-секретов (субпроцесс — паттерн AC-7 ORCH-103: канон-знания только субпроцессами кирпичей) |
|
||
| `scripts/onboard_project.py` | переиспользовать, **не менять** | кирпич регистрации проекта: Plane-проект + 22 статуса + лейблы, Gitea-репо + webhook, merged-`ORCH_PROJECTS_JSON` |
|
||
| `docs/deployment/LITE_SETUP.md` | **обновить** | скрипт = рекомендованный быстрый путь; ручной маршрут остаётся каноном-fallback; размещение не ломает пиннинг «13 разделов в порядке» ЛИБО тест обновляется синхронно |
|
||
| `tests/test_setup_lite_script.py` *(имя-кандидат)* | **создать** | анти-дрейф + unit чистых функций (по образцу `tests/test_bootstrap_script.py`) |
|
||
| `tests/test_lite_setup_doc.py` | обновить **при необходимости** | если меняется пиннингуемая структура LITE_SETUP.md (разделы/кирпичи) |
|
||
| `CHANGELOG.md` | обновить | запись `feat:` |
|
||
| `docs/overview/` | обновить **при необходимости** | если витрина описывает маршрут Lite-тиража (таблица соответствия — в индексе витрины, правило агентов №2/ORCH-011) |
|
||
| `src/**`, `docker-compose.yml`, `Dockerfile`, `.env.example`, `.env.watchdog.example` | **НЕ менять** | каноны-источники: скрипт их только читает (шаблоны env, состав сервисов) |
|
||
|
||
---
|
||
|
||
## 3. Функциональные требования
|
||
|
||
### FR-1 — Единая точка входа, режимы, идемпотентность (BR-1, BR-5)
|
||
- Один файл; запуск `python3 scripts/setup_lite.py …` из корня чекаута репо `orchestrator`
|
||
на голом python3 (до venv и до `docker compose up`).
|
||
- Обязательны как минимум: **read-only режим диагностики** (ноль мутаций: скан предусловий +
|
||
discovery + план шагов — аналог `plan` ORCH-103) и **установочный интерактивный режим**
|
||
(аналог `apply`); желателен `verify` (read-only пост-проверка). Дефолтный режим и имена —
|
||
решение архитектора (OQ-1) с учётом паттерна D5 ORCH-009/103 («plan — дефолт») и
|
||
бизнес-цели «одна команда».
|
||
- Step-движок **check→ensure**: каждый шаг сперва проверяет «уже сделано?» и при PASS
|
||
пропускается → повторный запуск идемпотентен; «resume» после manual-step = просто повторный
|
||
запуск (паттерн `bootstrap_bundle.run_apply`).
|
||
- **Exit-коды (контракт):** `0` — все шаги PASS; `2` — остановка на manual-step /
|
||
незавершённое предусловие; `1` — ошибка.
|
||
- Каждая мутация хоста — с **явного согласия** пользователя (per-action consent); отказ от
|
||
согласия → честный MANUAL-шаг с печатью эквивалентной команды, не молчаливый пропуск.
|
||
|
||
### FR-2 — Скан предусловий с офером установки (BR-2; LITE_SETUP §2, §7)
|
||
- Проверяемый перечень (каждый пункт — отдельный вердикт `OK | MISSING | WARN | MANUAL`):
|
||
1. ОС/арх: `uname -sm` = Linux x86_64 (иное → WARN «вне контура Lite»);
|
||
2. `docker --version`, `docker compose version` (v2), `git --version`, `python3 --version`,
|
||
`node --version`;
|
||
3. дистрибутив claude-code (`npm root -g` → каталог `@anthropic-ai/claude-code`) и
|
||
аутентификация CLI (читаемость `~/.claude/.credentials.json` uid'ом из п.5);
|
||
4. группа docker (`getent group docker` → gid для `ORCH_DOCKER_GID`);
|
||
5. uid/gid пользователя-владельца и каталога репозиториев (`ORCH_HOST_REPOS_DIR`,
|
||
инвариант ORCH-040: владелец = `ORCH_RUN_UID:ORCH_RUN_GID`);
|
||
6. каталог ssh-ключей (`ORCH_HOST_SSH_DIR`): существование/ключи;
|
||
7. свободность портов (прод/staging, дефолты 8500/8501).
|
||
- Для каждого `MISSING`: определить пакетный менеджер хоста (например apt/dnf/yum/zypper —
|
||
точный набор фиксирует архитектор) → предложить **конкретную команду установки** → выполнить
|
||
её ТОЛЬКО с явного согласия; для node+claude-code — офер `npm install -g
|
||
@anthropic-ai/claude-code`; для ssh-ключей — офер `ssh-keygen` (+ печать pubkey как
|
||
manual-step «добавить в Gitea», §2.4/§6.2).
|
||
- Неопределимый дистрибутив/менеджер, отказ пользователя, sudo-недоступность → честный
|
||
**MANUAL** с готовыми командами и ссылкой на § LITE_SETUP; молчаливый пропуск запрещён.
|
||
- Интерактивный OAuth-логин claude CLI скрипт **не выполняет** — только верифицирует
|
||
результат (§7.2) и выдаёт manual-step.
|
||
|
||
### FR-3 — Discovery инсталляций Plane/Gitea (BR-4)
|
||
- Источник — **локальный Docker**: перечисление контейнеров, группировка в «инсталляции» по
|
||
метке compose-проекта (`com.docker.compose.project`), опознание по именам образов
|
||
(Plane: `makeplane/*`-семейство; Gitea: `gitea/*`) и published-портам; точные
|
||
эвристики/паттерны — ADR. Из кандидата выводятся **предлагаемые** URL
|
||
(`ORCH_PLANE_API_URL`/`ORCH_PLANE_WEB_URL`; `ORCH_GITEA_URL`/`ORCH_GITEA_PUBLIC_URL`).
|
||
- Поведение по числу найденных: **0** → ручной ввод URL + честная подсказка «Lite не
|
||
устанавливает Plane/Gitea; нет инфраструктуры — см. Bundled (BUNDLED_SETUP.md)»; **1** →
|
||
префилл по умолчанию (с подтверждением); **≥2** → нумерованный список (проект, образы,
|
||
порты) + выбор. Пункт **«ввести вручную»** доступен всегда.
|
||
- **Best-effort, never-block:** docker недоступен / ошибка перечисления / не-docker
|
||
инсталляция (native/k8s) → ручной ввод, не FAIL.
|
||
- Discovery заполняет только кандидаты URL; **токены всегда вводит пользователь** (FR-4);
|
||
выбранный кандидат всё равно проходит верификацию FR-4.
|
||
|
||
### FR-4 — Интерактивный сбор обязательных ключей с немедленной верификацией (BR-3)
|
||
- Покрывается карта обязательных ключей нового хоста — **§4.2 LITE_SETUP** (группы: Plane,
|
||
Gitea, webhook-секреты, Telegram, `ORCH_PROJECTS_JSON`, хост-параметры, порты).
|
||
- Секретные значения вводятся **скрыто** (`getpass`-класс ввода) и **никогда не печатаются**
|
||
(ни в stdout, ни в лог; только имена ключей) — паттерн NFR-3 ORCH-103.
|
||
- Немедленная верификация каждого введённого значения фактическим вызовом:
|
||
- Plane: `GET $ORCH_PLANE_API_URL/api/v1/workspaces/<slug>/projects/` c `X-API-Key` → 200;
|
||
- Gitea: `GET $ORCH_GITEA_URL/api/v1/user` c токеном → 200 (+ владелец → `ORCH_GITEA_OWNER`);
|
||
- Telegram: `getMe` → `"ok":true`; helper определения chat-id через `getUpdates`;
|
||
- публичный URL оркестратора (для webhook'ов) — синтаксическая валидация + предупреждение,
|
||
что достижимость со стороны Plane/Gitea проверится на smoke.
|
||
- Неуспех верификации → re-prompt с диагнозом (ограниченное число попыток — паттерн
|
||
`manual_checkpoint(max_tries=3)`), затем честный MANUAL/остановка exit 2 — не бесконечный
|
||
цикл и не молчаливое принятие.
|
||
- **non-TTY** (нет интерактива): честный отказ с подсказкой ЛИБО неинтерактивная альтернатива
|
||
(флаги/answers-file — механизм решает архитектор, OQ-2); зависание недопустимо.
|
||
|
||
### FR-5 — Автодетект хост-параметров и когерентность портов (BR-2, BR-6)
|
||
- Автоматически определяются и НЕ спрашиваются у пользователя (только подтверждение):
|
||
`ORCH_RUN_UID`/`ORCH_RUN_GID` (uid/gid владельца `ORCH_HOST_REPOS_DIR` / текущего
|
||
пользователя), `ORCH_DOCKER_GID` (`getent group docker`), `ORCH_HOST_NODE_BIN`
|
||
(`which node`), `ORCH_HOST_CLAUDE_CODE_DIR` (`npm root -g`), `ORCH_AGENT_HOME_DIR` /
|
||
`ORCH_HOST_CLAUDE_DIR` / `ORCH_HOST_CLAUDE_JSON` (HOME пользователя из §2.2),
|
||
`ORCH_DEPLOY_HOST_REPO_PATH` (корень чекаута).
|
||
- Порты: busy-check (`ss`/socket) прод- и staging-портов; занят → предложить альтернативу.
|
||
Смена прод-порта → **синхронно** согласовать тройку `ORCH_DEPLOY_PROD_TARGET_PORT` ⇄
|
||
`WATCHDOG_METRICS_URL` ⇄ `ORCH_POST_DEPLOY_BASE_URL` (§2.5/§4.2). `ORCH_STAGING_PORT` ==
|
||
прод-порт → **отказ fail-closed** (инвариант ORCH-058, усилен ORCH-101).
|
||
|
||
### FR-6 — Сборка `.env` / `.env.watchdog` (BR-5; LITE_SETUP §4)
|
||
- `.env` собирается **от канона `.env.example`** (принцип ORCH-101: дефолт = боевое значение;
|
||
записываются только собранные отличия нового хоста). `.env.watchdog` — от
|
||
`.env.watchdog.example` (ключи `WATCHDOG_TG_BOT_TOKEN`/`WATCHDOG_TG_CHAT_ID` кладутся
|
||
**только туда** — ловушка файла-носителя §4.3).
|
||
- Webhook-секреты — **кирпичом `gen_secrets.py`** (свежий выпуск; боевые секреты не
|
||
используются — stateless §12 / REPLICATION §3).
|
||
- **Существующий `.env`/`.env.watchdog` → отказ** (exit 2, внятное сообщение); перезапись —
|
||
только явный force-флаг (паттерн `gen_secrets --force` / `--force-secrets` ORCH-103).
|
||
Подсказки-дефолты промптов — из `.env.example`/автодетекта, **никогда** из боевых значений.
|
||
- После сборки — резолв-проверка `docker compose config` (PASS/FAIL, §4 «Проверка»).
|
||
|
||
### FR-7 — Подключения и машинная охрана нормативов (BR-3, BR-6)
|
||
- **Plane (§5):** наличие workspace/доступность API верифицируются (FR-4); статусы/лейблы
|
||
вручную НЕ создаются (только кирпич onboarding, §5.3). **Webhook (§5.4, каверза Plane CE):**
|
||
Path A (UI) = manual-checkpoint с инструкцией и верификацией; Path Б (SQL в Postgres Plane)
|
||
= только с **явного согласия** + подтверждённый пользователем контейнер БД Plane +
|
||
пост-верификация (`SELECT url, is_active FROM webhooks`); выбор степени автоматизации — ADR
|
||
(OQ-3). Молчаливый пропуск запрещён.
|
||
- **Gitea (§6):** токен верифицирован (`/api/v1/user`); **branch protection на `main`:**
|
||
непустой `branch_protections` → **FAIL шага с лечением** (норматив §6.4 / ADR D10 ORCH-009 /
|
||
INV-4); скрипт правила **сам не удаляет** (no-delete) — только инструкция. Per-repo webhook
|
||
и репо создаёт кирпич onboarding (FR-9), не сам скрипт.
|
||
- **Telegram (§8):** два независимых бота; **идентичные токены орка и watchdog → отказ шага**
|
||
(C-1 ORCH-100 «ЗАПРЕЩЕНО» — машинная проверка, не примечание).
|
||
- **LLM (§7):** верификация дистрибутива/нод-бинаря/читаемости кредов uid'ом контейнера;
|
||
наличие `ORCH_AGENT_MODEL_DEFAULT`/`ORCH_AGENT_EFFORT_DEFAULT` в собранном `.env` (§7.3).
|
||
|
||
### FR-8 — Запуск Lite-контура и health (BR-6; LITE_SETUP §9, §12)
|
||
- С согласия: `docker compose up -d --build`; проверка состава: запущены **ровно**
|
||
`orchestrator` + `orchestrator-watchdog`, `orchestrator-staging` НЕ поднят (строго за
|
||
`profiles: [staging]`).
|
||
- Health-чек контрактов: `/health` → 200 `"status":"ok"`; `/queue` → штатный JSON;
|
||
`/metrics` → `"schema_version": 1` (порт — фактический из конфига).
|
||
- Stateless-проверка чистоты (§12): счётчики jobs нулевые, ни одной задачи чужих проектов;
|
||
нарушение → FAIL с лечением «пересобрать `data/` с нуля».
|
||
|
||
### FR-9 — Регистрация проекта заказчика кирпичом onboarding (BR-6, BR-7; LITE_SETUP §10)
|
||
- Скрипт собирает параметры проекта (имя/описание/repo/prefix/stack/test-cmd/порты/
|
||
webhook-url из публичного URL оркестратора) и строит аргументы вызова
|
||
`onboard_project.py` **детерминированной чистой функцией** (тестируемо без сети).
|
||
- Последовательность: `plan` → показать пользователю → согласие → `apply` → `verify`
|
||
(субпроцессы; exit 2 кирпича = остались ручные шаги → транслировать как MANUAL).
|
||
Степень автоматизации vs «печать готовой команды» — ADR (OQ-6; прецедент драйва —
|
||
`bootstrap_bundle.step_onboard`).
|
||
- `ORCH_PROJECTS_JSON` из отчёта `apply` вписывается в `.env` (с согласия); затем управляемый
|
||
рестарт **только собственного свежеподнятого** контура по процедуре §10: проверка тихого
|
||
окна (`/queue` без running-job) → `docker compose up -d --force-recreate orchestrator` →
|
||
пост-проверка `/health`+`/queue`.
|
||
|
||
### FR-10 — Итоговый отчёт (BR-1, BR-6)
|
||
- Финальная сводная таблица всех шагов: PASS/FAIL/MANUAL; для каждого MANUAL — что сделать и
|
||
ссылка на § LITE_SETUP; для FAIL — диагноз и лечение (зеркало §13 траблшутинга).
|
||
- Smoke первой задачи (§11: issue → «To Analyse» → артефакты `01–04`) выдаётся как
|
||
завершающая manual-инструкция (вердикт «тираж PASS» — за оператором).
|
||
|
||
### FR-11 — Документация: канон не форкается (BR-7)
|
||
- `docs/deployment/LITE_SETUP.md`: скрипт вводится как **рекомендованный быстрый путь**
|
||
(врезка/подраздел в начале маршрута); ручной маршрут §2–§13 сохраняется как канон и
|
||
fallback для MANUAL-шагов. Размещение либо не ломает пиннинг
|
||
`test_doc_exists_with_all_13_sections_in_order`, либо тест обновляется в том же PR.
|
||
- Норматив сопровождения (NFR-5 ORCH-102) расширяется симметрично: «меняешь шаги тиража →
|
||
обнови LITE_SETUP.md **и установочный скрипт** в том же PR».
|
||
|
||
---
|
||
|
||
## 4. Изменения API
|
||
**Нет.** Эндпоинты оркестратора не добавляются и не меняются; скрипт — потребитель
|
||
существующих read-only контрактов (`/health`, `/queue`, `/metrics`) и внешних API
|
||
(Plane/Gitea/Telegram).
|
||
|
||
## 5. Изменения схемы БД
|
||
**Нет.** БД оркестратора не затрагивается (создаётся пустой при первом старте — штатно);
|
||
БД Plane заказчика — только опциональная webhook-вставка Path Б §5.4 (FR-7, с согласия).
|
||
|
||
## 6. Требования к новым/изменённым QG checks
|
||
**Нет.** Скрипт — операторский инструмент вне рантайма и вне конвейера;
|
||
`STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / machine-verdict ключи — байт-в-байт.
|
||
|
||
## 7. Совместимость / регресс
|
||
- **Kill-switch не нужен:** активация — только явный запуск CLI человеком (паттерн
|
||
ORCH-009/102/103); рантайм не несёт ни одной новой кодовой ветки.
|
||
- Полный существующий регресс `pytest tests/ -q` остаётся зелёным; `test_lite_setup_doc.py` —
|
||
зелёный (с синхронным обновлением при правке структуры дока).
|
||
- Обратимость: задача добавляет файлы `scripts/` + `tests/` + правку дока; откат = revert PR,
|
||
поведение платформы не меняется в обе стороны.
|
||
- Скрипт не вносит хост-литералов в `src/**` (вне скана `test_no_host_hardcodes.py` по
|
||
определению, т.к. `src/**` не трогается).
|
||
|
||
## 8. Конфигурация
|
||
Новых ключей `Settings`/`.env.example` **не вводится**. Скрипт читает каноны
|
||
`.env.example`/`.env.watchdog.example` как шаблоны и пишет целевые `.env`/`.env.watchdog`
|
||
(FR-6). Платформенные константы (`SELF_HOSTING_REPO="orchestrator"`, имена сервисов, layout
|
||
контейнера) не параметризуются (норматив REPLICATION §1).
|
||
|
||
## 9. Наблюдаемость
|
||
- Структурный stdout-лог прогона (шаг → вердикт → диагноз) + итоговая таблица (FR-10).
|
||
- Exit-код = машинный итог прогона (`0/2/1`) — пригоден для обвязки/CI заказчика.
|
||
- Опциональный файл-отчёт прогона — на усмотрение архитектора (ADR).
|
||
|
||
## 10. Артефакты pipeline (создать/обновить в ТОМ ЖЕ PR)
|
||
- `docs/work-items/ORCH-104/06-adr/ADR-001-<slug>.md` — решения: имя/режимы скрипта,
|
||
эвристики discovery, политика оферов установки, автоматизация webhook Path Б и onboarding,
|
||
механизм non-TTY (архитектор).
|
||
- `docs/deployment/LITE_SETUP.md` — быстрый путь (FR-11).
|
||
- `tests/test_setup_lite_script.py` (+ правка `tests/test_lite_setup_doc.py` при
|
||
необходимости).
|
||
- `CHANGELOG.md` — запись `feat:`; витрина `docs/overview/` — если затронуто описание тиража.
|
||
|
||
## 11. Инварианты (не нарушать)
|
||
- `src/**`, корневой `docker-compose.yml`, `Dockerfile`, `STAGE_TRANSITIONS`, `QG_CHECKS`,
|
||
`check_*`, machine-verdict ключи, схема БД — **байт-в-байт**.
|
||
- **stdlib-only**; модули платформы не импортируются; канон-знания — только субпроцессами
|
||
кирпичей (`gen_secrets.py`, `onboard_project.py`).
|
||
- **No-delete:** ни одной удаляющей операции (файлы, контейнеры, ветки, правила) — лечение
|
||
всегда инструкцией.
|
||
- **Никогда** не push/force-push/иные операции в `main` (INV-4); не рестартить чужие/боевые
|
||
контейнеры; мутации — только с явного согласия.
|
||
- Секреты: свежие, скрытый ввод, не печатаются; существующие `.env*` молча не перетираются.
|
||
- Stateless: данные/задачи/секреты исходного хоста не переносятся ни одним шагом.
|
||
|
||
## 12. Открытые вопросы для архитектора (не блокируют анализ)
|
||
- **OQ-1:** имя скрипта и набор/дефолт режимов: строгий паттерн D5 (`plan` — дефолт,
|
||
мутации только в `apply`) vs «запуск без аргументов = wizard» (бизнес-цель «одна команда»).
|
||
Возможный компромисс: wizard-дефолт, где фаза скана всегда read-only, а мутации — за
|
||
per-action consent.
|
||
- **OQ-2:** механизм неинтерактивного прогона (флаги / answers-file / env-переменные) и
|
||
точное поведение в non-TTY.
|
||
- **OQ-3:** webhook Plane: автоматизировать ли Path Б (SQL) или строго manual-checkpoint;
|
||
критерий выбора/подтверждения контейнера Postgres Plane.
|
||
- **OQ-4:** политика оферов установки: исполнять команды пакетного менеджера из скрипта
|
||
(с согласия) vs только печатать (безопаснее); набор поддерживаемых менеджеров.
|
||
- **OQ-5:** размещение быстрого пути в LITE_SETUP.md (врезка в §1 vs новый раздел с правкой
|
||
пиннинга «13 разделов») и судьба нумерации.
|
||
- **OQ-6:** степень драйва onboarding: субпроцесс plan→apply→verify изнутри скрипта
|
||
(прецедент `bootstrap_bundle.step_onboard`) vs печать готовой команды.
|