DarkRiDDeR11 мин

Инженерия релиза: механизм evidence gate и контролируемого rollback

РелизыDevOps

Четыре карточки могут честно носить версию 2024.07.0 и всё равно описывать разные вещи. В учебном case artifact говорит, что он получен из synthetic-commit-other-91, migration уже нацелена на 2024.07.1, а rollout card просит знакомый номер. Это не лог CI и не снимок кластера, а специально зафиксированная модель расхождения. Цена такого расхождения в настоящем процессе — потеря точки, где ещё можно безопасно остановиться: команда начинает спорить о rollout, хотя предметом спора должен быть parent artifact или contract migration.

Симптом — «везде написана одна версия». Причина — system считает tag достаточным идентификатором и допускает свободные поля между стадиями. Проверка — представить каждую связь как exact predicate, а не как описание: commit id у artifact равен commit id source; rollout digest равен artifact digest; migration target равен release version. Действие — закрыть gate: любой false predicate возвращает stop-and-reconcile-records, а не очередную попытку deploy. Так механизм делает несовпадение видимым до действия и не прячет его за успешным названием job.

Не четыре текста, а ориентированные связи

У цепочки есть направление. Commit не подтверждает artifact автоматически; artifact должен явно указать, на какой commit он ссылается. Migration не должна «следовать последнему main»; ей нужен target release и known compatibility statement. Rollout card не является artifact; она лишь говорит, какой immutable digest и какой migration version намерены рассматривать дальше. Rollback candidate не является кнопкой возврата; он описывает предыдущую versioned point и собственную data boundary. Если поменять направление, легко решить, что rollout status доказывает source, хотя он мог смотреть на совсем другой artifact.

Эта модель близка к тому, как SLSA отделяет artifact от provenance и verification от простого наличия metadata. Но она намеренно меньше спецификации: не пытается моделировать builder, signature, identity, dependency graph или policy engine. Внутри sidecar нужны только считываемые relations. Такая скромность важна: чем больше real-like полей проглатывает fixture, тем выше риск неявно превратить teaching example в плохую imitation pipeline. Поэтому input состоит из marker, scope, fixed-memory-only mode и case id; сами commit, artifact, migration и rollout нельзя передать извне.

Матрица controlled rollback: code and artifact могут иметь versioned return point, migration требует отдельной compatibility and data review, rollout card не становится операцией, а evidence record остаётся тем, что нужно сравнить до действия.
Таблица показывает границы решения: вернуть artifact можно планировать, но fixture не выполняет deploy и не объявляет отмену migration или данных.

Таблица predicate и stop condition

Проверки механизма и их смысл
PredicateКогда trueКогда gate закрытРазрешённое действие
artifact.sourceCommitId === commit.idArtifact declared the same source revision.Нельзя понять, чей source представляет artifact.Стоп и сравнение source record; не менять rollout.
migration.targetReleaseVersion === releaseVersionPlan написан именно для этого release contract.Migration относится к другой версии или неизвестной совместимости.Стоп и отдельный migration review.
rollout.requestedArtifactDigest === artifact.digestIntent называет exact recorded content.Card может указать другой immutable artifact.Стоп и правка intent record.
return version + digest известныЕсть названная code/artifact point для возврата.Rollback — лишь слово без target.Не давать approval; написать return draft.
data boundary названаНикто не обещает undo того, что не моделировалось.Code rollback маскируется как data recovery.Разделить review code и data.

В этой таблице нет строки «все jobs зелёные», потому что она недостаточна для исходной проблемы. Зелёный status может быть полезным evidence в реальном разрешённом процессе, но он не отвечает, какой artifact проверяли, для какого release была migration и что именно вернётся назад. Обратное тоже верно: совпадение полей не доказывает успешный deploy. Gate обязан различать semantic consistency records и execution result. Иначе положительный unit test или status check случайно получит смысл release approval, на который он не рассчитан.

Почему closed input — техническая, а не декоративная граница

Функция createFixedSyntheticReleaseInput() принимает case id, а inspectSyntheticReleaseEvidence() сверяет его с object, созданным в source file. Дополнительные ciLog, deployTarget, другой scope, режим чтения registry или unknown case отвергаются. Это звучит жёстко, но именно так видно, что пример не перепутал модель с интеграцией. В нём нельзя случайно положить реальный digest, URL или credentials, дождаться вывода и назвать его evidence. Все возвращаемые поля прямо говорят not-inspected, not-executed или no-system-change.

После inspection отдельная функция plan повторно строит canonical report из fixed case и сравнивает весь object через serialised representation. Если подменить decision на continue, заменить releaseVersion либо добавить поле, похожее на deployment id, plan откажет. Это не универсальная защита от подделки в production; в fixture нет identity, signature и storage. Это маленькая проверка model invariant: decision не должен зависеть от изменённого object, который caller собрал после inspection. Даже на уровне обучения это полезнее, чем функция, которая верит любому красивому report.

Исполнимый пример: plan и return draft

Этот пример можно запустить как модуль рядом с sidecar. Он сначала получает report только из embedded case, затем делает decision draft и запрашивает return point. Последняя проверка специально требует фразу о migration boundary. Если позже кто-то заменит её на обещание data undo или добавит действие над deploy, fixture должен изменить verdict и тест остановится. Код не является конфигурацией GitHub Actions, Kubernetes manifest или инструкцией registry; он не содержит client, filesystem access или network call.

import { createFixedSyntheticReleaseInput, inspectSyntheticReleaseEvidence, planSyntheticReleaseDecision, draftSyntheticControlledRollback } from './upgrade-2024-07.mjs';

const report = inspectSyntheticReleaseEvidence(createFixedSyntheticReleaseInput('fixed-aligned-release'));
const plan = planSyntheticReleaseDecision(report);
const rollback = draftSyntheticControlledRollback(plan);

if (rollback.drafted !== true || rollback.migrationBoundary !== 'does-not-reverse-data-and-requires-separate-human-review') {
  throw new Error('fixed synthetic rollback boundary changed');
}

console.log(rollback.toReleaseVersion); // 2024.06.3
console.log(rollback.deployment);       // not-created-or-run

// This creates no CI run, deploy, registry entry, cloud change, or production effect.

Контролируемый rollback начинается не с глагола «откатить», а с ответа на два разных вопроса. К какому artifact version можно вернуться? И какие effects migration это не покрывает? Для aligned case fixture возвращает 2024.06.3 и точный synthetic digest, а строка does-not-reverse-data-and-requires-separate-human-review остаётся частью output. Это ограничение избегает одного из самых дорогих ложных обещаний: что возврат template или binary автоматически превращает данные в прежнюю форму.

Маршрут проектирования evidence gate

  1. Запишите entity types. Commit, artifact, migration plan, rollout intent и rollback candidate должны быть различимы даже если используют одну release version.
  2. Выберите immutable relation для каждой стрелки. Для artifact это exact source commit и digest; для migration — target version и compatibility; для intent — повторение exact digest и migration version.
  3. Назовите отрицательные outcomes. «Неизвестный artifact», «migration другой версии» и «digest в card иной» — разные stop reasons. Не сливайте их в один retry.
  4. Разведите consistency и execution. Gate может разрешить human review, но не говорит, что deploy состоялся. Result реальной операции должен жить в другой, разрешённой модели evidence.
  5. Сделайте return point versioned. Нужны previous release и previous digest, а также понятная граница migration. Отсутствие одного поля — повод не выдавать approval.
  6. Проверьте contract отрицательными cases. Подмените commit, target migration, requested digest и добавьте real-like input. Каждая ветка обязана останавливаться без side effect.

Ограничения и граница реальных инструментов

GitHub Changelog от 25 июня 2024 важен здесь не как рекомендация внедрить конкретный action, а как исторически точный пример того, что artifact attestation и её verification — отдельные операции. SLSA v1.0 важна как stable terminology, где output artifact, provenance и проверка ожиданий не смешиваются. Kubernetes v1.30 важен как напоминание, что Deployment revision и её rollback имеют конкретную область: Pod template. Эти источники не диктуют schema полей sidecar и не дают обещаний о CI, registry, cloud или production данной работы.

Нельзя честно перенести этот fixture в real release path одной строкой. Для этого потребуются авторизация, threat model, источник истины для commit, правила подписей или digest, review migration, target-specific deploy policy, monitoring и заранее разрешённые данные. Ничего из перечисленного fixture не читает и не заменяет. Положительный его output означает только «в этой закрытой коллекции JS objects predicates совпали». Отрицательный означает «в этой коллекции named relation нарушена». Это хороший unit test мысли, но не change record.

Следующий проверяемый шаг

Выберите одну существующую release form и проведите paper review без доступа к deploy: найдите, где в ней должны жить exact commit id, artifact digest, migration target/compatibility, requested digest и return digest. Для каждого поля добавьте owner и question, который проверяющий способен закрыть. Затем специально составьте три ложные карточки: одна с другим commit, одна с migration следующей версии, одна с другим digest. Если process не умеет назвать три разных stop actions, сначала улучшите форму. Если умеет — только после отдельного разрешения исследуйте автоматизацию.

Историческая граница июля 2024

Все внешние ссылки в этой ревизии — первичные официальные материалы, доступные до конца июля 2024. Они подтверждают лишь узкие исторические свойства: SLSA v1.0 существовала как stable release, GitHub Artifact Attestations стали generally available в июне 2024, Kubernetes v1.30 описывала Deployment revisions и rollback Pod template. Внутренние record names, predicates, negative cases и controlled rollback draft — авторская synthetic модель. Она не измеряет delivery, не получает attestation, не вызывает CI и не утверждает состояние production.

Проверяемые источники

  • GitHub Changelog: Artifact Attestations is generally available, 25 июня 2024 — Первичный датированный источник GitHub: в GitHub Actions можно создать attestation для собранного artifact и затем проверить его. Здесь это основание различать artifact и проверяемое утверждение о нём; источник не является policy, журналом или доказательством чужого выпуска.
  • SLSA: SLSA v1.0 is now final!, 19 апреля 2023 — Первичная публикация о первом stable SLSA v1.0. Из неё взята узкая идея versioned provenance и проверки ожиданий, а не claim, что любой набор полей делает конкретную сборку безопасной или готовой к deploy.
  • Kubernetes documentation: Deployments, snapshot 14.03.2024 — Первичный неизменяемый снимок ветки release-1.30, доступный до июля 2024: revision Deployment связана с Pod template, а rollback возвращает прежнюю revision шаблона. Это не отменяет data migration и не переносится автоматически на другой deploy-инструмент.