88 lines
7.0 KiB
Markdown
88 lines
7.0 KiB
Markdown
# BRD — ORCH-058: Self-deploy retag берёт устаревший staging-образ (риск тихого регресса)
|
||
|
||
Work Item ID: ORCH-058
|
||
Тип: bug / техдолг инфраструктуры self-deploy
|
||
Источник: `docs/history/LESSONS_ORCH-036-selfdeploy.md` п.4 (самый опасный из 4 багов bootstrap ORCH-36)
|
||
|
||
## 1. Контекст
|
||
|
||
ORCH-36 сделал стадию `deploy` исполняемой для self-hosting репозитория `orchestrator`:
|
||
- Phase B (`src/self_deploy.py::build_deploy_command`) запускает детачед host-хук
|
||
`scripts/orchestrator-deploy-hook.sh` с параметром `SOURCE_IMAGE=orchestrator-orchestrator-staging`.
|
||
- Хук (шаг **2b**, BUILD-ONCE, ORCH-36 BR-6) делает `docker tag $SOURCE_IMAGE → $TARGET_IMAGE`
|
||
**без `docker build`** — «прод получает ровно тот артефакт, что прошёл staging».
|
||
|
||
Дизайн-предпосылка BUILD-ONCE: **staging-образ свеж и провалидирован**. На практике этой
|
||
гарантии НЕТ.
|
||
|
||
## 2. Проблема (корень)
|
||
|
||
Конвейер **нигде не пересобирает** образ `orchestrator-orchestrator-staging` из текущего
|
||
кода (HEAD `main` / провалидированной ветки):
|
||
- Стадия `deploy-staging` запускает только `scripts/staging_check.py` (e2e-проверка)
|
||
против **уже работающего** контейнера `orchestrator-staging` (8501) — что бы в нём ни
|
||
крутилось. Сборка staging-образа — ручная операция (STAGING.md / ORCH-34), вне конвейера.
|
||
- Между «образ собран» и «retag в прод» нет провенанс-связи с провалидированным коммитом.
|
||
|
||
Следствие (инцидент ORCH-36): staging-образ не пересобрали из нового `main` →
|
||
`staging_check` прошёл против СТАРОГО кода → BUILD-ONCE retag промоутнул СТАРЫЙ образ в прод.
|
||
Деплой «зелёный» (`result=0`, health ok), но прод молча откатился на код 2-дневной давности:
|
||
пропал `deploy-finalizer` → задача не закрылась → бесконечная петля Phase B.
|
||
|
||
## 3. Почему это критично
|
||
|
||
> Это **самый опасный** из четырёх багов self-deploy: он **не падает**, а **тихо откатывает
|
||
> прод**. Зелёный гейт = ложный позитив. Орк обслуживает все проекты (enduro-trails) из одного
|
||
> прод-инстанса → тихий регресс инструмента = групповой инцидент для всех проектов.
|
||
|
||
Текущая защита (staging-гейт, merge-gate, health-check хука) НЕ ловит этот класс: все они
|
||
зелёные, потому что проверяют не тот артефакт, что уезжает в прод.
|
||
|
||
## 4. Бизнес-цель
|
||
|
||
Гарантировать инвариант: **в прод никогда не промоутится образ, не собранный из
|
||
провалидированного для данной задачи коммита; при невозможности это доказать — деплой
|
||
fail-fast (вердикт FAILED → откат на development), а не «тихо зелёный»**.
|
||
|
||
## 5. Объём (scope)
|
||
|
||
В объёме:
|
||
- Привязка артефакта (staging-образ → прод-retag) к провалидированному коммиту.
|
||
- Fail-fast при рассинхроне образа и кода (никаких тихих промоутов устаревшего).
|
||
- Условность как ORCH-35/36/43: реально только для `orchestrator`; прочие репо — no-op /
|
||
прежнее поведение.
|
||
- Контракт never-raise и fail-closed (на сомнении — не деплоить).
|
||
|
||
Вне объёма:
|
||
- Полный авто-approve прод-деплоя (ORCH-54).
|
||
- Изменение exit-code-контракта хука (0/1/2) и реестров `STAGE_TRANSITIONS` / `QG_CHECKS` как
|
||
набора стадий.
|
||
- Миграции схемы БД.
|
||
- Деплой/рестарт **прод**-контейнера `orchestrator` (8500) в рамках задачи.
|
||
|
||
## 6. Бизнес-требования (BR)
|
||
|
||
- **BR-1.** Образ, который BUILD-ONCE retag промоутит в прод, ДОЛЖЕН соответствовать коду,
|
||
провалидированному стадией `deploy-staging` для данной задачи (тот же git-коммит).
|
||
- **BR-2.** Если соответствие НЕ доказуемо (staging-образ собран не из провалидированного
|
||
коммита, либо провенанс невозможно прочесть) — деплой ОБЯЗАН fail-fast: вердикт `FAILED`,
|
||
штатный откат на `development` (контракт БАГ-8), без рестарта прода.
|
||
- **BR-3.** `staging_check.py` (e2e-валидация) ДОЛЖЕН прогоняться против артефакта,
|
||
соответствующего тому же провалидированному коммиту, что уедет в прод (нельзя валидировать
|
||
один образ, а катить другой).
|
||
- **BR-4.** Поведение условно: реально для `orchestrator`; для прочих репозиториев — no-op /
|
||
без регрессий прежнего синхронного деплоя.
|
||
- **BR-5.** Выбранное решение НЕ должно приводить к вечной блокировке деплоя (если механизм
|
||
свежести отсутствует — нужен путь, который доводит до зелёного, а не fail-fast'ит навсегда).
|
||
- **BR-6.** Контракт never-raise: сбой проверки свежести/провенанса не должен валить
|
||
stage_engine; на любом сомнении — fail-closed (трактуем как несоответствие).
|
||
- **BR-7.** Документация-голден-сорс: INFRA / DEPLOY_HOOK / STAGING / architecture README +
|
||
CHANGELOG обновляются в том же PR; решение оформляется ADR.
|
||
|
||
## 7. Связанные материалы
|
||
|
||
- `docs/history/LESSONS_ORCH-036-selfdeploy.md` (п.4 — корень)
|
||
- `docs/architecture/adr/adr-0007-executable-self-deploy.md`, `docs/work-items/ORCH-036/06-adr/ADR-001-executable-self-deploy.md`
|
||
- `src/self_deploy.py`, `scripts/orchestrator-deploy-hook.sh`, `src/config.py`
|
||
- `docs/operations/STAGING.md`, `docs/operations/DEPLOY_HOOK.md`, `docs/operations/INFRA.md`
|