Skip to main content
В выпуске ничто не решается вручную. Версия берётся из сообщений коммитов, журнал изменений — оттуда же, а артефакты собирает, сканирует, подписывает и заверяет рабочий процесс, который запускает тег. Задача сопровождающего — влить правильное и проверить результат, а не выбирать номер и не писать заметки о выпуске задним числом. Эта страница — взгляд сопровождающего. Оператору, читающему опубликованный выпуск, нужны матрица версий, способ проверить подпись и допустимое отставание установки — это на странице Совместимость и обновления.

Что означает версия

Выпуски следуют семантическому версионированию, и начиная с 1.0.0 обещание обычное: мажорная версия может сломать публичную поверхность, минорная добавляет возможности совместимо, патч исправляет поведение. Что именно считается публичным, перечислено в Политике расширения: /v1/admin, контракт OpenAPI, маршруты вебхуков и каталога, пробы, переменные окружения и опубликованные образы. /v1/panel и /v1/account в этот список не входят и меняются вместе с панелями. Первый общедоступный выпуск — 1.0.0. Всё до него было разработкой в main без опубликованных тегов; поэтому release-please-config.json называет 1.0.0 начальной версией, а не позволяет инструменту начать линию 0.x, которая сразу оказалась бы неверной.

Входные данные — Conventional Commits

Версия выводится из коммитов, влитых с предыдущего выпуска, поэтому сообщение коммита — это решение о выпуске, а не заметка. Область указывается в скобках и называет участок: feat(wallet):, fix(bot):, docs(ops):. Задание conventional-pr в CI проверяет заголовок пул-реквеста, потому что при squash-мердже заголовок становится сообщением коммита. Никогда не добавляйте футеры generated-by или co-author, что бы ни предлагал шаблон инструмента по умолчанию. AGENTS.md формулирует это правило, и оно важнее настроек инструмента.
Ломающему изменению нужен ! или футер, даже если правка кода выглядит небольшой. Удаление переменной окружения, переименование поля в ответе /v1/admin или смена поведения маршрута вебхука — это мажорная версия, а также устаревание, объявленное минимум на один минорный выпуск раньше, согласно политике устаревания на странице Совместимость и обновления.

До того как что-то можно выпускать

На каждом пул-реквесте выполняется полный рабочий процесс CI, и всё в нём должно быть зелёным до вливания. Задания и то, что доказывает каждое, описаны в Разработке; для выпуска важно, что нужные ему шлюзы уже отработали к моменту появления тега:
  • задание миграций применило все поддерживаемые пути обновления к настоящему PostgreSQL, поэтому выпущенная схема — та, до которой установка действительно может дойти;
  • задание compose проверило поставляемый стек и оба примера прокси против закоммиченного .env.example, поэтому задокументированная отправная точка по-прежнему запускается;
  • задание docs проверило оба языковых дерева и их ссылки.

Как выпускается версия

1

Влейте в main

Release Please выполняется при каждом push в main. Он читает коммиты с последнего выпуска и открывает — или обновляет — пул-реквест выпуска.
2

Проверьте пул-реквест выпуска

В нём ровно два вида изменений: новая версия в .release-please-manifest.json и сгенерированные записи CHANGELOG.md. Прочитайте журнал изменений так, как прочитал бы пользователь. feat:, из описания которого оператор не может ничего извлечь, стоит поправить следующей записью в истории коммитов, а не правкой сгенерированного файла.
3

Влейте его

Вливание пул-реквеста выпуска создаёт тег omniflow-vX.Y.Z и релиз на GitHub. Имя пакета в теге — потому что release-please настроен с именем пакета. Ничто другое тег не создаёт: тег, отправленный руками, опубликовал бы образы из коммита, который не описан ни одним журналом изменений.
4

Проследите за рабочим процессом выпуска

Release Please в том же запуске вызывает Release images, который проверяет, собирает, публикует и подписывает без дальнейшего участия — ниже описано, что может его остановить.
Он именно вызывается, а не запускается тегом, и в этой разнице причина того, что 1.0.0 вышел без образов. release-please отправляет тег дефолтным GITHUB_TOKEN, а GitHub намеренно не порождает запуски от событий, вызванных этим токеном. У Release images по-прежнему есть триггер по тегу — для тега, отправленного человеком, — и workflow_dispatch, принимающий тег, чтобы собрать уже существующий выпуск, не выпуская новый. Но для обычного выпуска работает не они.
5

Проверьте опубликованное

Заберите один образ по digest и проверьте его подпись командой cosign verify со страницы Совместимость и обновления. Проверять артефакт вместо того, чтобы доверять рабочему процессу, — это и есть смысл подписи.

Что делает рабочий процесс выпуска

Два шлюза выполняются до того, как что-либо собрано, и находка в любом из них останавливает выпуск, а не помечает его:
  • Сканирование исходников. govulncheck для уязвимостей зависимостей Go, Gitleaks для секретов по всей истории и Trivy для уязвимостей файловой системы и лицензий уровней CRITICAL и HIGH, игнорируя находки без исправлений.
  • CodeQL. Семантический анализ исходников Go и TypeScript — это другой вопрос, чем наличие известной рекомендации по безопасности у зависимости.
Только после этого четыре образа — api, bot, worker, web — собираются под linux/amd64 и linux/arm64, публикуются в GitHub Container Registry с приложенными SBOM и provenance сборки и подписываются cosign без ключа, с использованием OIDC-идентичности самого рабочего процесса GitHub Actions. Управлять, ротировать и терять нечего, а подпись записывается в публичный журнал прозрачности Rekor. Затем опубликованный образ сканируется ещё раз. Этот шаг — обнаружение, а не шлюз: мультиархитектурный образ нельзя просканировать до того, как build-push-action его отправит, без гораздо более инвазивной двухэтапной сборки, поэтому находка там роняет рабочий процесс уже после того, как образ оказался в реестре. Считайте такой отказ поводом выпустить патч, а не основанием считать, что ничего не опубликовалось.

Плановое сканирование — не шлюз выпуска

Security выполняется на пул-реквестах и еженедельно и выгружает SARIF во вкладку безопасности. Он информационный: сообщает, но не блокирует. Рабочий процесс выпуска запускает те же инструменты с exit-code: 1. Оба существуют потому, что находка, появившаяся между выпусками, должна быть видна, не дожидаясь следующего.

Исправление выпущенной версии

Поддерживающих веток нет. Исправление попадает в main коммитом fix:, Release Please выпускает следующий патч, а установка обновляется вперёд. Миграции только вперёд, поэтому откат — это восстановление из резервной копии, снятой непосредственно перед обновлением; процедура описана в Резервных копиях, восстановлении и аварийном восстановлении. Если выпуск нужно отозвать, удалите релиз и его тег до того, как хоть одна установка обновится, и выпустите следующий патч с исправлением. Не перевешивайте тег: подпись и provenance привязаны к digest, и перенос тега заставит обе описывать нечто другое.

Документация выходит вместе с изменением

Документация не является шагом выпуска, потому что она шаг вливания. Изменение поведения несёт свою английскую страницу, её русскую пару и запись в навигации в том же коммите, а задание docs в CI следит, чтобы деревья не расходились. К моменту появления тега документация на то, что в нём содержится, уже опубликована — см. Документацию.

Смежное

Разработка

Настройка рабочего окружения, генераторы и каждая проверка, которую выполняет CI.

Совместимость и обновления

Матрица версий, проверка артефактов, окно обновления и устаревания.

Стратегия тестирования

Что выполняется по умолчанию, чему нужен Docker и что должно сопровождать финансовое изменение.

Политика расширения

Какие поверхности на самом деле покрыты обещанием версии.