> ## Documentation Index
> Fetch the complete documentation index at: https://omniflow-vpn.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Процесс выпуска

> Как влитый коммит становится версией, что проверяет и публикует рабочий процесс выпуска и что после этого проверяют сопровождающий и оператор

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

Эта страница — взгляд сопровождающего. Оператору, читающему опубликованный
выпуск, нужны матрица версий, способ проверить подпись и допустимое отставание
установки — это на странице
[Совместимость и обновления](https://omniflow-vpn.mintlify.site/ru/operations/compatibility.md).

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

Выпуски следуют [семантическому версионированию](https://semver.org/), и начиная
с 1.0.0 обещание обычное: мажорная версия может сломать публичную поверхность,
минорная добавляет возможности совместимо, патч исправляет поведение. Что именно
считается публичным, перечислено в
[Политике расширения](https://omniflow-vpn.mintlify.site/ru/integrations/extending.md): `/v1/admin`, контракт OpenAPI,
маршруты вебхуков и каталога, пробы, переменные окружения и опубликованные
образы. `/v1/panel` и `/v1/account` в этот список не входят и меняются вместе с
панелями.

Первый общедоступный выпуск — `1.0.0`. Всё до него было разработкой в `main` без
опубликованных тегов; поэтому `release-please-config.json` называет `1.0.0`
начальной версией, а не позволяет инструменту начать линию `0.x`, которая сразу
оказалась бы неверной.

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

Версия выводится из коммитов, влитых с предыдущего выпуска, поэтому сообщение
коммита — это решение о выпуске, а не заметка.

| Префикс | Влияние на следующую версию |
| - | - |
| `fix:` | Патч |
| `feat:` | Минорная |
| `feat!:` или любой тип с футером `BREAKING CHANGE:` | Мажорная |
| `docs:`, `test:`, `chore:`, `refactor:`, `ci:` | Никакого, и в журнал изменений не попадает |

Область указывается в скобках и называет участок: `feat(wallet):`, `fix(bot):`,
`docs(ops):`. Задание `conventional-pr` в CI проверяет заголовок пул-реквеста,
потому что при squash-мердже заголовок становится сообщением коммита.

Никогда не добавляйте футеры generated-by или co-author, что бы ни предлагал
шаблон инструмента по умолчанию. `AGENTS.md` формулирует это правило, и оно
важнее настроек инструмента.

> [!WARNING]
>
> Ломающему изменению нужен `!` или футер, даже если правка кода выглядит
> небольшой. Удаление переменной окружения, переименование поля в ответе
> `/v1/admin` или смена поведения маршрута вебхука — это мажорная версия, а также
> устаревание, объявленное минимум на один минорный выпуск раньше, согласно
> политике устаревания на странице
> [Совместимость и обновления](https://omniflow-vpn.mintlify.site/ru/operations/compatibility.md).

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

На каждом пул-реквесте выполняется полный рабочий процесс `CI`, и всё в нём
должно быть зелёным до вливания. Задания и то, что доказывает каждое, описаны в
[Разработке](https://omniflow-vpn.mintlify.site/ru/contributing/development.md); для выпуска важно, что нужные ему
шлюзы уже отработали к моменту появления тега:

* задание миграций применило все поддерживаемые пути обновления к настоящему
  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`, который
проверяет, собирает, публикует и подписывает без дальнейшего участия — ниже
описано, что может его остановить.

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

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

Заберите один образ по digest и проверьте его подпись командой
`cosign verify` со страницы
[Совместимость и обновления](https://omniflow-vpn.mintlify.site/ru/operations/compatibility.md). Проверять артефакт
вместо того, чтобы доверять рабочему процессу, — это и есть смысл подписи.

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

Два шлюза выполняются до того, как что-либо собрано, и находка в любом из них
останавливает выпуск, а не помечает его:

* **Сканирование исходников.** `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 выпускает следующий патч, а установка обновляется вперёд. Миграции только
вперёд, поэтому откат — это восстановление из резервной копии, снятой
непосредственно перед обновлением; процедура описана в
[Резервных копиях, восстановлении и аварийном восстановлении](https://omniflow-vpn.mintlify.site/ru/operations/backup-restore.md).

Если выпуск нужно отозвать, удалите релиз и его тег до того, как хоть одна
установка обновится, и выпустите следующий патч с исправлением. Не перевешивайте
тег: подпись и provenance привязаны к digest, и перенос тега заставит обе
описывать нечто другое.

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

Документация не является шагом выпуска, потому что она шаг вливания. Изменение
поведения несёт свою английскую страницу, её русскую пару и запись в навигации в
том же коммите, а задание `docs` в CI следит, чтобы деревья не расходились. К
моменту появления тега документация на то, что в нём содержится, уже
опубликована — см. [Документацию](https://omniflow-vpn.mintlify.site/ru/contributing/documentation.md).

## Смежное

> **[Разработка](https://omniflow-vpn.mintlify.site/ru/contributing/development.md)**
>
> Настройка рабочего окружения, генераторы и каждая проверка, которую выполняет CI.

> **[Совместимость и обновления](https://omniflow-vpn.mintlify.site/ru/operations/compatibility.md)**
>
> Матрица версий, проверка артефактов, окно обновления и устаревания.

> **[Стратегия тестирования](https://omniflow-vpn.mintlify.site/ru/contributing/testing.md)**
>
> Что выполняется по умолчанию, чему нужен Docker и что должно сопровождать финансовое изменение.

> **[Политика расширения](https://omniflow-vpn.mintlify.site/ru/integrations/extending.md)**
>
> Какие поверхности на самом деле покрыты обещанием версии.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.