194 lines
18 KiB
Markdown
194 lines
18 KiB
Markdown
---
|
||
work_item: ORCH-104
|
||
stage: analysis
|
||
author_agent: analyst
|
||
status: ready-for-review
|
||
created_at: 2026-06-12
|
||
model_used: claude-opus-4-8
|
||
---
|
||
|
||
# 02 — ТЗ (TRZ): ORCH-104 — Установочный скрипт для Lite
|
||
|
||
Work Item: **ORCH-104** · Repo: **orchestrator** · Стадия: analysis
|
||
|
||
> ТЗ описывает **конкретные требования к реализации**, выведенные из BRD и фактического кода.
|
||
> Архитектурное обоснование (структура скрипта, точные эвристики детекта, общий код с
|
||
> `bootstrap_bundle.py`, финальное имя файла) — задача архитектора (`06-adr/`). Открытые вопросы —
|
||
> §12.
|
||
|
||
## 1. Сводка изменения
|
||
Добавляется **один интерактивный установщик** `scripts/install_lite.py`, автоматизирующий ручной
|
||
маршрут `docs/deployment/LITE_SETUP.md` §2–§11 для Lite-тиража (Type A). Скрипт сканирует хост,
|
||
детектит/предлагает доустановить зависимости, обнаруживает существующие Plane/Gitea (выбор при
|
||
нескольких), интерактивно собирает и **живо верифицирует** токены/URL, выпускает секреты кирпичом
|
||
`gen_secrets.py`, собирает `.env`/`.env.watchdog` из канон-`.example`, поднимает `orchestrator` +
|
||
`orchestrator-watchdog`, регистрирует проект кирпичом `onboard_project.py` и проверяет health.
|
||
Изменение **аддитивно** и **вне рантайма**: `src/**`/`STAGE_TRANSITIONS`/`QG_CHECKS`/схема БД — не
|
||
трогаются; kill-switch не нужен (активация — только явный запуск). Каноны не форкаются.
|
||
|
||
## 2. Задействованные модули / пути
|
||
|
||
| Путь | Действие |
|
||
|------|----------|
|
||
| `scripts/install_lite.py` | **создать** — интерактивный установщик Lite (entry-point; имя — OQ-1) |
|
||
| `scripts/gen_secrets.py` | **переиспользовать** (subprocess-кирпич), без изменений |
|
||
| `scripts/onboard_project.py` | **переиспользовать** (subprocess-кирпич, `apply`/`verify`), без изменений |
|
||
| `.env.example`, `.env.watchdog.example` | **переиспользовать** как канон-источник рендера, без изменений |
|
||
| `docker-compose.yml` | **переиспользовать** (`docker compose up -d --build`), без изменений |
|
||
| `scripts/bootstrap_bundle.py` | **эталон-паттерн** (parse_env/render_env/preflight_verdict/manual_checkpoint/exit-коды) — не изменяется; общий код — OQ-4 |
|
||
| `docs/deployment/LITE_SETUP.md` | **обновить** — указатель на установщик как рекомендованный путь + синхронизация шагов |
|
||
| `tests/test_install_lite_script.py` | **создать** — unit (чистые функции) + структурные анти-дрейф тесты |
|
||
| `tests/test_lite_setup_doc.py` | **обновить** — ассерт ссылки на установщик; существующие ассерты зелёные |
|
||
| `CLAUDE.md`, `CHANGELOG.md`, `docs/architecture/README.md`, `docs/overview/` | **обновить** (docs golden-source, правило агентов №2) |
|
||
|
||
## 3. Функциональные требования
|
||
|
||
### FR-1 — Entry-point и режимы
|
||
Скрипт `scripts/install_lite.py`, запускаемый из корня чекаута: `argparse`, позиционный `mode ∈
|
||
{plan, apply, verify}` (дефолт `plan`). Exit-коды (контракт): **0** — успех; **2** — остановка на
|
||
manual-step / незавершённое предусловие / нет TTY; **1** — ошибка. `plan` — ноль мутаций (печать
|
||
плана + read-only preflight-диагностика). `apply` — полный прогон step-движком `check→ensure`
|
||
(повтор = каскад skip; «resume» = повторный запуск). `verify` — read-only пост-проверка
|
||
(health-контракты + `onboard_project.py verify`). Привязка: BR-1, BR-7.
|
||
|
||
### FR-2 — Скан предусловий хоста (preflight)
|
||
Read-only снимок хоста (по образцу `bootstrap_bundle.collect_facts`/`preflight_verdict`): наличие
|
||
`docker`, `docker compose` v2, `git`, `python3`, `node`, `claude` CLI + читаемость кред; свободность
|
||
портов (прод-порт `ORCH_DEPLOY_PROD_TARGET_PORT` дефолт 8500, при self-hosting-вилке staging 8501);
|
||
`uid`/`gid`/`docker-gid` и владелец каталога репозиториев; наличие ssh-каталога. Чистая функция-
|
||
вердикт возвращает `(blockers, warnings)`; человекочитаемый список «есть/нет». Привязка: BR-2.
|
||
|
||
### FR-3 — Управляемая установка зависимостей (D-1)
|
||
Для каждого блокера-зависимости: детект пакетного менеджера (`apt`/`dnf` по наличию бинаря /
|
||
`/etc/os-release`) → **точная команда** установки (чистая функция «дистрибутив+пакет → команда»);
|
||
печать команды; для безопасных — предложение выполнить с согласия (`y/N`, `input()`); отказ/нет
|
||
TTY/неизвестный дистрибутив → печать инструкции и `exit 2` (никакой молчаливой root-мутации).
|
||
Привязка: BR-3, AC-3.
|
||
|
||
### FR-4 — Детект существующих Plane/Gitea + выбор (D-2)
|
||
Best-effort обнаружение (never-raise, NFR-3): кандидаты из `docker ps` (имена/образы, похожие на
|
||
Plane: `plane-*`/`makeplane`/`proxy`; Gitea: `gitea/gitea`/`gitea-*`) и из слушающих портов
|
||
(типовые Plane 80/8080/443, Gitea 3000). По кандидату — проба живости (Plane: `GET /api/instances/`;
|
||
Gitea: `GET /api/v1/version`). Чистая функция формирует ранжированный список кандидатов. Поведение:
|
||
0 кандидатов → запрос ручного URL; ≥2 → нумерованный список + выбор (`input()` индекс), вне
|
||
диапазона → ручной ввод; 1 → предложить с подтверждением. Выбор наполняет `ORCH_PLANE_*` /
|
||
`ORCH_GITEA_*`. Привязка: BR-5, AC-4.
|
||
|
||
### FR-5 — Интерактивный сбор данных + живая верификация
|
||
Honest-checkpoint контракт (как `bootstrap_bundle.manual_checkpoint`): для каждого требуемого
|
||
секрета/параметра — печать откуда взять (ссылка на LITE_SETUP §5–§8), скрытый ввод секрета
|
||
(`getpass`), **верификация ДО записи**: Plane — `GET /api/v1/workspaces/<slug>/projects/` с
|
||
`X-API-Key`; Gitea — `GET /api/v1/user` с `Authorization: token`; Telegram — `GET /bot<token>/getMe`.
|
||
Провал → повтор (до N) или `exit 2` с подсказкой, значение **не пишется**. Авто-детект и
|
||
пред-заполнение всего детектируемого (uid/gid/docker-gid/порты/пути/node/claude/выбранные URL) —
|
||
оператор только подтверждает. Привязка: BR-4, AC-5.
|
||
|
||
### FR-6 — Выпуск webhook-секретов кирпичом `gen_secrets.py`
|
||
`ORCH_PLANE_WEBHOOK_SECRET` / `ORCH_GITEA_WEBHOOK_SECRET` выпускаются **строго** субпроцессом
|
||
`gen_secrets.py` (никакого собственного `secrets.token_hex` в установщике — анти-форк, AC-7); если
|
||
уже присутствуют в `.env` и валидны — пропуск (не перетирать без `--force`). Привязка: BR-6, NFR-2.
|
||
|
||
### FR-7 — Сборка `.env` / `.env.watchdog`
|
||
Идемпотентный рендер из канона (`render_env`-паттерн): существующий файл — обновить ключи-
|
||
override, отсутствующий — отрендерить из `.env.example` / `.env.watchdog.example`; комментарии
|
||
канона сохранены; неизвестные ключи — управляемым блоком в конец; запись правами **600**; значения
|
||
секретов в stdout/лог **не попадают**; молчаливой перезаписи нет. Watchdog-ключи (`WATCHDOG_TG_*`)
|
||
кладутся **только** в `.env.watchdog` (файл-носитель, LITE_SETUP §4.3). Привязка: BR-6, NFR-2.
|
||
|
||
### FR-8 — Подъём стека + готовность
|
||
`docker compose up -d --build` ровно `orchestrator` + `orchestrator-watchdog` (staging НЕ
|
||
поднимается — за профилем). Ожидание готовности поллингом `GET /health` (таймаут). Перед записью
|
||
`ORCH_PROJECTS_JSON` стек уже жив. Привязка: BR-1.
|
||
|
||
### FR-9 — Регистрация проекта кирпичом `onboard_project.py`
|
||
Сбор параметров проекта (имя/repo/prefix/стек/тест-команда/порты/webhook-URL — флаги или интерактивно),
|
||
вызов `onboard_project.py apply` затем `verify` субпроцессом; парс merged-`ORCH_PROJECTS_JSON` из
|
||
отчёта и запись в `.env`; ручные пункты отчёта (manual-step) пробрасываются оператору. **Никакого
|
||
собственного создания статусов/лейблов/репо** (анти-форк, 22 статуса — только онбординг-кирпич).
|
||
Привязка: BR-6, AC-7.
|
||
|
||
### FR-10 — Health-верификация + сводка
|
||
После `apply` (и в `verify`): `GET /health` → 200, `GET /queue` / `GET /metrics` → валидный JSON.
|
||
Итоговая сводка по шагам (`ok`/`skipped`/`manual-step`) + общий вердикт; любой FAIL → `exit 1` с
|
||
диагностикой (хвост `docker logs` / снапшот). Привязка: BR-7, AC-11.
|
||
|
||
### FR-11 — Синхронизация документации
|
||
`LITE_SETUP.md` дополняется указателем «рекомендованный путь — `install_lite.py`; ручной маршрut
|
||
ниже как фолбэк/референс». Обновляются `CLAUDE.md` (раздел тиража), `docs/architecture/README.md`
|
||
(Type A), `docs/overview/` (если затронута витрина), `CHANGELOG.md`. Привязка: BR-9, AC-12.
|
||
|
||
## 4. Изменения API
|
||
**Нет.** Установщик — вне процесса орка; обращается только к существующим read-only эндпоинтам
|
||
(`/health`, `/queue`, `/metrics`) как HTTP-клиент и к собственным Plane/Gitea/Telegram заказчика.
|
||
Новых/изменённых эндпоинтов оркестратора не вводится.
|
||
|
||
## 5. Изменения схемы БД
|
||
**Нет.** Установщик не касается БД оркестратора (её создаёт сам орк пустой при первом старте,
|
||
stateless-инвариант LITE_SETUP §12).
|
||
|
||
## 6. Требования к новым/изменённым QG checks
|
||
**Нет.** `QG_CHECKS` / `check_*` / `STAGE_TRANSITIONS` / machine-verdict ключи — байт-в-байт не
|
||
трогаются (INV-1). Установщик не участвует в решении ни одного гейта.
|
||
|
||
## 7. Конфигурация
|
||
Новых **рантайм**-ключей `config.py` / kill-switch — **нет** (NFR-1; активация = явный запуск).
|
||
Установщик читает/пишет только `.env` / `.env.watchdog` (канон ключей — `.env.example` /
|
||
`.env.watchdog.example`, ORCH-101). CLI-флаги установщика (имена — OQ-7): режим + параметры проекта
|
||
для `onboard_project.py` (`--repo`/`--prefix`/`--stack`/…), возможный `--force` (перевыпуск
|
||
секретов), возможный `--non-interactive`/значения из env для CI.
|
||
|
||
## 8. Наблюдаемость
|
||
- Прогресс-лог по шагам (`ok`/`skipped`/`manual-step`/`error`) — **без значений секретов** (только
|
||
имена ключей/пути файлов, NFR-2).
|
||
- Итоговая сводка PASS/FAIL + код выхода `0/2/1`.
|
||
- `manual_checkpoint` печатает точную инструкцию и верифицирует результат (молчаливый пропуск
|
||
запрещён); без TTY → `exit 2` с той же инструкцией.
|
||
|
||
## 9. Артефакты pipeline (создаются/обновляются)
|
||
- `scripts/install_lite.py` (новый исполняемый артефакт).
|
||
- `tests/test_install_lite_script.py` (новый), `tests/test_lite_setup_doc.py` (обновление).
|
||
- `docs/work-items/ORCH-104/06-adr/ADR-001-<slug>.md` (архитектор) + опц. сквозной
|
||
`docs/architecture/adr/adr-NNNN-*.md`.
|
||
- `docs/deployment/LITE_SETUP.md`, `CLAUDE.md`, `docs/architecture/README.md`, `docs/overview/`,
|
||
`CHANGELOG.md` — обновления (BR-9).
|
||
|
||
## 10. Совместимость / регресс
|
||
Аддитивно: новый файл-скрипт + новый тест + правки docs. Существующие кирпичи (`gen_secrets.py`,
|
||
`onboard_project.py`) и compose — байт-в-байт. Полный регресс `pytest tests/ -q` остаётся зелёным.
|
||
Обратимость — тривиальная (удаление нового файла/теста). Область раската — только хосты заказчиков
|
||
Lite; **наш прод не затронут** (установщик исполняется на чужом хосте, говорит только с локальным
|
||
хостом и инфраструктурой заказчика).
|
||
|
||
## 11. Инварианты (не нарушать)
|
||
- **INV-1** — `src/**` / `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / machine-verdict ключи /
|
||
схема БД — байт-в-байт.
|
||
- **INV-2** — Каноны не форкаются: секреты — `gen_secrets.py`; статусы/лейблы/репо/webhook —
|
||
`onboard_project.py`; env — из `.env.example`/`.env.watchdog.example`; стек — из `docker-compose.yml`.
|
||
- **INV-3** — Нет delete-операций (никаких `docker … rm`, `rm -rf`, удаления веток, force-push).
|
||
- **INV-4** — Секреты не печатаются; `.env`/`.env.watchdog` — права 600; без молчаливой перезаписи.
|
||
- **INV-5** — Никогда не трогает наш прод / `main` / force-push; говорит только с локальным хостом и
|
||
собственными Plane/Gitea/Telegram заказчика.
|
||
- **INV-6** — Платформенные конвенции: репо `orchestrator`; имена compose-сервисов/профиля —
|
||
константы; staging за профилем; `ORCH_STAGING_PORT` ≠ прод-порт (guard ORCH-058) — установщик
|
||
уважает, не форкает.
|
||
- **INV-7** — stdlib-only (NFR-6).
|
||
|
||
## 12. Открытые вопросы для архитектора (OQ — не блокируют анализ)
|
||
- **OQ-1** — Финальное имя/путь: `scripts/install_lite.py` (понятно конечному оператору) vs
|
||
`scripts/bootstrap_lite.py` (симметрия с `bootstrap_bundle.py`). Рекомендация анализа —
|
||
`install_lite.py`.
|
||
- **OQ-2** — Точные эвристики детекта Plane/Gitea (паттерны имён/образов контейнеров, набор
|
||
портов/URL-проб, ранжирование уверенности).
|
||
- **OQ-3** — Какие зависимости считать «безопасными для авто-выполнения с согласия» (напр.
|
||
`pip install -r requirements.txt` в venv — да; `apt install docker` под sudo — только consent;
|
||
claude CLI через npm); владелец distro-команд-карты.
|
||
- **OQ-4** — Общий код с `bootstrap_bundle.py` (вынести `parse_env`/`render_env`/`manual_checkpoint`
|
||
в общий stdlib-модуль) vs самодостаточный один файл (ради «1 установочный файл» и stdlib-only).
|
||
Trade-off DRY ↔ простота/одно-файловость.
|
||
- **OQ-5** — Драйвить ли путь Б Plane-webhook (raw-SQL, LITE_SETUP §5.4) автоматически (как
|
||
`bootstrap_bundle.step_plane_webhook`) или всегда оставлять верифицируемым manual-step.
|
||
- **OQ-6** — Подтвердить: интерактивный логин claude CLI остаётся manual-step с верификацией
|
||
(`claude --version` + читаемость кред), не автоматизируется.
|
||
- **OQ-7** — Набор CLI-флагов/env для не-интерактивного (CI) прогона: какие входы принимают
|
||
флаги/env vs только prompt.
|