127 lines
11 KiB
Markdown
127 lines
11 KiB
Markdown
# ТЗ — ORCH-058: провенанс staging-образа перед BUILD-ONCE retag в прод
|
||
|
||
Work Item ID: ORCH-058
|
||
|
||
> Примечание: ТЗ фиксирует ТРЕБУЕМЫЕ изменения и точки в коде. **Выбор стратегии**
|
||
> (пересборка из HEAD `main` ПЕРЕД валидацией vs. fail-fast по провенансу образа, либо их
|
||
> комбинация) — решение **архитектора** (ADR в `06-adr/`). Ниже перечислены точки
|
||
> касания для обеих стратегий; архитектор выбирает и при необходимости сужает.
|
||
|
||
## 1. Инвариант, который нужно обеспечить
|
||
|
||
`INV-FRESH`: образ, передаваемый хуку как `SOURCE_IMAGE` для BUILD-ONCE retag в прод,
|
||
собран из ТОГО ЖЕ git-коммита, что прошёл `deploy-staging` для этой задачи. Если это
|
||
недоказуемо — деплой fail-fast (`deploy_status: FAILED` → откат на `development`, БАГ-8),
|
||
прод не трогается.
|
||
|
||
Якорь «провалидированного коммита» (architect фиксирует точно в ADR): SHA HEAD ветки задачи
|
||
после merge-gate rebase на `origin/main` (то, что валидировал `deploy-staging` + merge-gate).
|
||
|
||
## 2. Текущее поведение (что чинить)
|
||
|
||
| Место | Сейчас | Проблема |
|
||
|---|---|---|
|
||
| `scripts/orchestrator-deploy-hook.sh` шаг 2b | `docker tag $SOURCE_IMAGE → $TARGET_IMAGE` без проверки происхождения образа | промоутит любой образ под именем `orchestrator-orchestrator-staging`, даже устаревший |
|
||
| Стадия `deploy-staging` (`.openclaw/agents/deployer.md` + `staging_check.py`) | гоняет e2e против уже запущенного 8501, не пересобирая образ | валидирует не тот артефакт, что уедет в прод |
|
||
| `src/self_deploy.py::build_deploy_command` | передаёт `SOURCE_IMAGE`, `TARGET_*`, `COMPOSE_PROFILE`, `PREV_IMAGE_FILE`; провенанс/SHA не передаёт | хук не знает, какой коммит ожидать |
|
||
| `Dockerfile` | без OCI-лейбла `revision`/git-SHA | у образа нет машиночитаемого происхождения для проверки |
|
||
|
||
## 3. Задействованные модули `src/` и файлы
|
||
|
||
- `src/self_deploy.py` — основной (provenance-helpers + проброс ожидаемого SHA в команду хука).
|
||
- `src/config.py` — новые настройки (`ORCH_`-префикс обязателен, урок ORCH-36 п.2).
|
||
- `scripts/orchestrator-deploy-hook.sh` — fail-fast по провенансу и/или пересборка перед retag.
|
||
- `Dockerfile` — лейбл происхождения образа (для стратегии «провенанс по labels/sha»).
|
||
- `src/qg/checks.py` — опц. новый детерминированный под-чек свежести (если стратегия «гейт»).
|
||
- `src/stage_engine.py` — опц. точка вызова под-чека на ребре `deploy-staging → deploy`
|
||
(рядом с merge-gate, строки ~262–288). **Реестр `STAGE_TRANSITIONS` не меняется.**
|
||
- `.openclaw/agents/deployer.md` — шаги стадии `deploy-staging` (если выбран rebuild-перед-валидацией).
|
||
- `docker-compose.yml` — опц. build-args/labels для staging-сервиса (если стратегия rebuild).
|
||
|
||
## 4. Требуемые изменения — стратегия A (пересборка из HEAD main перед валидацией)
|
||
|
||
A1. Перед прогоном `staging_check.py` стадия `deploy-staging` для `orchestrator` пересобирает
|
||
образ `orchestrator-orchestrator-staging` из провалидированного коммита (worktree ветки
|
||
после merge-gate rebase) и пересоздаёт контейнер 8501 на свежем образе.
|
||
A2. `staging_check.py` гоняется против свежего контейнера; на `SUCCESS` ровно ЭТОТ образ
|
||
становится `SOURCE_IMAGE` для прод-retag (loop closed).
|
||
A3. Детерминированно (без LLM в критическом пути): сборку/recreate выполняет код стадии или
|
||
host-хук в staging-режиме, не агент-деплойер «руками».
|
||
A4. Безопасность: операция трогает ТОЛЬКО staging (8501), НИКОГДА прод (8500).
|
||
|
||
## 5. Требуемые изменения — стратегия B (fail-fast по провенансу образа)
|
||
|
||
B1. `Dockerfile`: добавить лейбл происхождения, напр.
|
||
`LABEL org.opencontainers.image.revision=$GIT_SHA` через `ARG GIT_SHA` (build-arg).
|
||
B2. Сборка staging-образа (ручная или из стратегии A) проставляет `GIT_SHA` = коммит сборки.
|
||
B3. `src/self_deploy.py::build_deploy_command`: вычислить ожидаемый SHA провалидированного
|
||
коммита и пробросить в команду хука новым env (напр. `EXPECTED_REVISION=<sha>`).
|
||
Новый pure-helper, напр. `expected_revision(repo, branch) -> str` (never-raise).
|
||
B4. `scripts/orchestrator-deploy-hook.sh` шаг 2b: ПЕРЕД `docker tag` прочитать лейбл
|
||
`$SOURCE_IMAGE` (`docker image inspect --format '{{ index .Config.Labels "org.opencontainers.image.revision" }}'`)
|
||
и сравнить с `$EXPECTED_REVISION`. Несовпадение / пустой лейбл / пустой ожидаемый SHA →
|
||
`log` + `exit 1` (fail-fast). Поведение обратносовместимо: при незаданном
|
||
`EXPECTED_REVISION` — текущее поведение (без проверки), чтобы не сломать не-self репо.
|
||
B5. exit 1 хука уже маппится `map_exit_code_to_status → FAILED` (контракт не меняется),
|
||
Phase C пишет `14-deploy-log.md` `deploy_status: FAILED` → откат на `development` (БАГ-8).
|
||
|
||
## 6. Требуемые изменения — опц. под-гейт (если архитектор выберет gate-side для B)
|
||
|
||
- Новый детерминированный (без LLM) под-чек, напр. `check_staging_image_fresh`, по образцу
|
||
`check_branch_mergeable` (ORCH-043): pure verdict-logic + условность (`self_deploy_applies`
|
||
/ `is_self_hosting_repo`), never-raise, для прочих репо → `(True, "N/A")`.
|
||
- Вызов на ребре `deploy-staging → deploy` ПЕРЕД Phase A (рядом с merge-gate, `stage_engine`
|
||
~268–288). FAIL → откат на `development` (как merge-gate). Реестр стадий неизменен —
|
||
это под-гейт ребра, не новая стадия.
|
||
- Если выбран чисто хуковый fail-fast (раздел 5) — под-гейт не нужен.
|
||
|
||
## 7. Изменения API
|
||
|
||
Нет. Эндпоинты (`/health`, `/status`, `/queue`, `/webhook/*`) не меняются. Опц.: в снимок
|
||
`GET /queue` можно добавить диагностическое поле о свежести образа — НЕ обязательно.
|
||
|
||
## 8. Изменения схемы БД
|
||
|
||
Нет. Состояние deploy — sentinel-файлы (`.deploy-state-<repo>/<wi>/`, ORCH-36). Миграции
|
||
запрещены (как ORCH-36/43/53).
|
||
|
||
## 9. Конфигурация (`src/config.py`, ВСЕ с префиксом `ORCH_`)
|
||
|
||
Кандидаты (architect финализирует имена и дефолты):
|
||
- `image_freshness_enabled: bool = True` — kill-switch проверки (поэтапный раскат).
|
||
- `image_freshness_repos: str = ""` — CSV; пусто → только self-hosting (как `self_deploy_repos`).
|
||
- (для стратегии B) проброс `EXPECTED_REVISION` строится в `build_deploy_command`, отдельной
|
||
настройки может не требоваться.
|
||
- (для стратегии A) при необходимости — имя/тег staging-образа уже есть
|
||
(`deploy_prod_source_image`).
|
||
|
||
Урок ORCH-36 п.2: любая настройка, читаемая pydantic Settings, ОБЯЗАНА иметь префикс `ORCH_`.
|
||
|
||
## 10. Новые QG checks (если применимо)
|
||
|
||
- Опц. `check_staging_image_fresh` (см. §6) — добавить в реестр `QG_CHECKS` и в
|
||
snapshot-тест реестра (`tests/test_qg_registry_snapshot.py`). Только если выбран gate-side.
|
||
|
||
## 11. Артефакты pipeline (создать/обновить В ТОМ ЖЕ PR)
|
||
|
||
- `06-adr/ADR-001-<slug>.md` — выбор стратегии (A / B / A+B), якорь «провалидированного
|
||
коммита», точки fail-fast, условность, never-raise, отсутствие deadlock (BR-5).
|
||
- `docs/operations/DEPLOY_HOOK.md` — описание провенанс-проверки / пересборки и новых env.
|
||
- `docs/operations/STAGING.md` — как и когда пересобирается staging-образ в конвейере.
|
||
- `docs/operations/INFRA.md` — обновить топологию/риск self-deploy (закрыт п.4 каскада).
|
||
- `docs/architecture/README.md` — секция ORCH-36/58 (свежесть артефакта в BUILD-ONCE).
|
||
- `CHANGELOG.md` — запись ORCH-058.
|
||
- При выборе стратегии A: bootstrap-чеклист (урок ORCH-36 «сквозной»: реальный staging-прогон
|
||
до мержа).
|
||
|
||
## 12. Инварианты / ограничения (self-hosting safety)
|
||
|
||
- Никогда не рестартовать/ронять прод 8500 в рамках задачи (CLAUDE.md). Любая сборка/recreate —
|
||
только staging 8501.
|
||
- Никогда не пушить/форс-пушить `main` (как merge-gate).
|
||
- Контракты НЕ меняются: exit-code хука (0/1/2), `map_exit_code_to_status`,
|
||
`check_deploy_status`/`_parse_deploy_status`, БАГ-8 rollback, terminal-sync, merge-gate.
|
||
- Fail-closed: на любом сомнении (нет лейбла, нет ожидаемого SHA, ошибка inspect) —
|
||
трактовать как несоответствие → FAILED, никогда не промоутить «на авось».
|
||
- never-raise: helpers и под-чек не должны пробрасывать исключение в stage_engine.
|