В заявке на выпуск написано 2024.07.0. Рядом лежат четыре похожих подписи: commit synthetic-commit-7f4a0c1, artifact с digest sha256:synthetic-2024-07-0-a1, migration plan 2024.07.0 и rollout card с тем же номером. Это конкретная учебная ситуация, а не журнал поставки: все записи заранее вшиты в fixture. Цена понятна и без реальной среды: если хотя бы одна подпись относится к другой версии, команда может обсуждать ошибку не того кода, ожидать не ту схему и готовить возврат к несуществующей точке.
Чаще всего симптом выглядит безобидно: на каждой стадии есть знакомый tag, но нельзя показать одну непрерывную связь от commit до запрошенного artifact. Причина — версия используется как украшение, а не как ключ сравнения; migration и rollout появляются отдельными карточками. Проверка короткая: сравнить parent-ссылку artifact на commit, target migration, digest в rollout и return point. Действие ещё короче: при первом несовпадении остановить review и исправить запись до любого реального deploy. Так рассуждают о контракте релиза, не о любимой CI-системе.
Что именно должно совпасть
Минимальная цепочка не требует огромной платформы. Достаточно назвать четыре объекта и запретить им молча расходиться. Commit фиксирует исходный revision. Artifact хранит собственную версию, digest и declared source commit. Migration plan хранит target release и отдельный compatibility statement. Rollout card повторяет версию, digest и migration version, но остаётся намерением: она ещё не доказывает, что кто-либо получил доступ к среде или что операция была выполнена. Отдельно хранится rollback candidate с прошлой версией и digest, но без обещания вернуть данные назад.
Слово «доказательство» здесь практическое. Оно не означает криптографическое свойство любого поля и не подменяет attestation. Оно означает, что следующий человек может сравнить одну пару значений с другой и получить конкретное «совпало» либо «стоп». GitHub в июне 2024 уже описывал artifact attestations как отдельный объект, который создаётся для build artifact и проверяется позже. Это полезное разделение ролей: label artifact, statement о происхождении и approval на deploy не должны сливаться в одну строку release=green.
Рабочая таблица связей
| Переход | Нужное равенство | Что проверить | Что делать при расхождении |
|---|---|---|---|
| commit → artifact | artifact.sourceCommitId = commit.id | Не только tag, но exact declared commit id. | Остановить: artifact нельзя связать с исходным revision. |
| artifact → migration | migration.targetReleaseVersion = releaseVersion | Версия и declared compatibility относятся к одному release. | Остановить: не угадывать, для какого кода написана migration. |
| artifact → rollout card | requestedArtifactDigest = artifact.digest | Card называет именно записанный immutable digest. | Остановить: версия может совпасть, а содержимое — нет. |
| release → rollback draft | return version + return digest известны | Есть ли точка возврата и граница для migration. | Не обещать data undo; вынести его в отдельный review. |
Таблица полезна тем, что не требует доверять одному центральному полю. Например, tag 2024.07.0 может стоять и у source, и у artifact, но source commit всё равно обязан совпасть. Аналогично, rollout card не становится надёжной от того, что её version выглядит красиво: digest должен быть тем же digest, который зафиксирован рядом с artifact. Каждая строка даёт проверяемый ответ и отдельный stop condition. Такой stop condition дешевле позднего расследования, потому что меняет только запись, а не среду.
Минимальная fixture вместо ложной автоматизации
Ниже — исполнимая fixture sidecar-пакета. В ней четыре versioned synthetic JS objects заранее записаны в модуле. Функция не принимает URL, имя registry, путь к manifest, credentials, журнал CI или target deploy. Она возвращает teaching report: aligned case ведёт лишь к human deploy review, а mismatch case — к stop. Команда --verify-fixture проверяет invariants этих объектов, но не создаёт artifact, не вызывает runner и не читает ни одного внешнего состояния.
import { createFixedSyntheticReleaseInput, inspectSyntheticReleaseEvidence } from './upgrade-2024-07.mjs';
const report = inspectSyntheticReleaseEvidence(createFixedSyntheticReleaseInput('fixed-aligned-release'));
if (report.decision !== 'continue-to-human-deploy-review') {
throw new Error('fixed synthetic chain is not aligned');
}
console.log(report.record.commit.id);
console.log(report.record.artifact.digest);
console.log(report.record.rollout.state); // proposed-not-executed
// The fixture reads only embedded objects. It is not CI, a registry, or a rollout.
node web/scripts/upgrade-2024-07.mjs --verify-fixture
Именно ограничение input делает пример проверяемым. Если передать похожее поле ciLog или deployTarget, fixture отвергнет input как extra field. Это не защита настоящей системы и не проверка CI. Это защита смысла статьи: учебный объект не должен незаметно начать потреблять реальное evidence, а затем выдавать его за подтверждённый rollout. Даже положительный результат fixture означает лишь равенство fixed records, которые автор уже поместил в память.
Маршрут перед настоящим выпуском
- Назовите release version и owner проверки. Version без ответственного за сравнение остаётся label. Owner не обязан делать deploy, но обязан знать, какая запись считается исходной.
- Свяжите artifact с commit exact идентификатором. Не заменяйте id названием ветки, последним merge или человеческим описанием. Сначала проверьте declared link, затем решайте, достаточно ли он надёжен для вашей среды.
- Опишите migration отдельно. Укажите target version, compatibility с предыдущей версией и то, что data effect требует отдельного review. Не прячьте это в комментарии к artifact.
- Соберите rollout intent без действия. Card должна повторить release, digest и migration version. Её задача — сделать будущую проверку однозначной, не запустить операцию.
- Сравните четыре перехода и остановитесь на первом mismatch. Не лечите расхождение новой попыткой. Исправьте record, повторите сравнение и только затем передавайте вопрос авторизованному reviewer.
- Запишите return point до approval. Версия и digest для возврата нужны до реального deploy; для migration отдельно зафиксируйте, что code rollback не означает data rollback.
Почему migration нельзя приклеить в самом конце
Migration обычно ломает простую картину «вернули прошлый artifact — всё вернулось». В Kubernetes Deployment revision связана с Pod template: rollback возвращает прежний template, а не отменяет любые изменения за его пределами. Это не недостаток Kubernetes; это важная граница, которую стоит назвать в любом delivery design. Если migration уже изменила данные или contract, прежний code может потребовать compatibility window, отдельную reverse procedure либо сознательный отказ от автоматического возврата. Поэтому migration version и её declared compatibility — часть evidence chain, а не последний пункт runbook.
Практика не требует однажды выбрать универсальный способ миграций. Она требует отделить два вопроса. Первый: какая версия artifact допустима для target data contract? Второй: какое действие по данным разрешено и как проверяется его обратимость? Пока второй вопрос не описан, rollback draft должен говорить ровно правду: «return artifact указан; data undo не обещан». Такая формулировка неприятнее зелёной галочки, зато она не заставляет инженера угадывать после первого симптома.
Ограничения и следующий проверяемый шаг
Эта схема не проверяет реальный Git history, build provenance, signature, runtime configuration, secret, доступ к cluster, состояние database, качество monitoring или факт доставки. SLSA v1.0 даёт vocabulary для provenance и verification, но не отменяет local policy и не превращает произвольный JSON в доказательство. GitHub Artifact Attestations и Kubernetes Deployment documentation описывают конкретные возможности своих систем; из них нельзя вывести, что каждая организация должна применять идентичные поля или один и тот же rollback.
Следующий проверяемый шаг — взять один разрешённый к анализу release record и, не выполняя deploy, заполнить ту же таблицу четырьмя значениями: exact commit id, artifact digest, migration target/compatibility и return version/digest. Затем второй reviewer должен найти либо подтвердить одно равенство в каждой строке. Если у строки нет owner-а, источника или stop action, не расширяйте pipeline: сначала исправьте contract. Это маленький тест, который оставляет понятный результат независимо от выбранного CI/CD инструмента.
Историческая граница июля 2024
Материал использует только первичные официальные источники, реально доступные к июлю 2024: SLSA v1.0 был объявлен stable в апреле 2023, GitHub объявил Artifact Attestations generally available 25 июня 2024, а Kubernetes release-1.30 documentation уже содержала versioned Deployment guidance. Технические факты в тексте намеренно узкие: provenance нужно проверять относительно ожиданий, attestation не равна approval, а Deployment rollback не равен откату данных. Все ids, digest, versions и outcomes выше — fixed synthetic teaching records, не наблюдения о чьей-либо доставке.
Проверяемые источники
- 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-инструмент.