# Релизы

Tuist автоматически публикует новые версии по мере слияния изменений в основную ветку. CLI, сервер и кэш-сервер Kura поставляются через каналы релизов (canary, кандидат в релиз, стабильный), чтобы рекомендуемая установка оставалась стабильной и медленно меняющейся; выпуск кандидата в релиз и его продвижение в стабильный — это осознанные еженедельные шаги. Приложение выпускается непрерывно (iOS-приложение развёртывается только в TestFlight, подробнее [здесь](#app-store-release)).

## Обзор

Мы выпускаем следующие основные компоненты:
- **Tuist CLI** — инструмент командной строки, поставляемый через каналы canary, кандидата в релиз и стабильный [каналы](#release-channels)
- **Tuist Server** — бэкенд-сервисы, поставляемые по той же канальной модели, что и CLI
- **Kura** — кэш-сервер, поставляемый по той же канальной модели, что и CLI
- **Tuist App** — приложения для macOS и iOS, выпускаемые непрерывно (iOS-приложение развёртывается только в TestFlight, подробнее [здесь](#app-store-release))

Каждый пуш в `main` публикует canary-пререлиз для CLI, сервера и Kura. Стабильный релиз для каждого из этих компонентов выпускается только тогда, когда мейнтейнер продвигает проверенный кандидат в релиз, а еженедельное расписание запускает все три потока синхронно (см. [Каналы релизов](#release-channels)). Приложение публикует стабильную версию при каждом пуше в `main`.

## Как это работает

### 1. Соглашения о коммитах

Мы используем [Conventional Commits](https://www.conventionalcommits.org/) для структурирования сообщений коммитов. Это позволяет нашим инструментам понимать характер изменений, определять повышение версий и генерировать соответствующие списки изменений.

Формат: `type(scope): description`

#### Типы коммитов и их влияние

| Тип | Описание | Влияние на версию | Пример |
|------|-------------|----------------|---------|
| `feat` | Новая функция или возможность | Повышение минорной версии (x.Y.z) | `feat(cli): add support for Swift 6` |
| `fix` | Исправление ошибки | Повышение патч-версии (x.y.Z) | `fix(app): resolve crash when opening projects` |
| `docs` | Изменения в документации | Без релиза | `docs: update installation guide` |
| `style` | Изменения стиля кода | Без релиза | `style: format code with swiftformat` |
| `refactor` | Рефакторинг кода | Без релиза | `refactor(server): simplify auth logic` |
| `perf` | Улучшения производительности | Повышение патч-версии | `perf(cli): optimize dependency resolution` |
| `test` | Добавление/изменение тестов | Без релиза | `test: add unit tests for cache` |
| `chore` | Задачи по обслуживанию | Без релиза | `chore: update dependencies` |
| `ci` | Изменения CI/CD | Без релиза | `ci: add workflow for releases` |

Для CLI эти повышения определяют версию canary и группировку списка изменений. Стабильные версии CLI — это минорные линии (`X.Y.0`), выпускаемые вручную, а стабильные патчи (`X.Y.Z`) поступают только из бэкпортов, поэтому `feat` или `fix` достигают пользователей CLI через приведённый ниже поток каналов, а не как немедленный стабильный релиз.

#### Критические изменения

Критические изменения вызывают повышение мажорной версии (X.0.0) и должны быть указаны в теле коммита:

```
feat(cli): change default cache location

BREAKING CHANGE: The cache is now stored in ~/.tuist/cache instead of .tuist-cache.
Users will need to clear their old cache directory.
```

### 2. Обнаружение изменений

Каждый компонент использует [git cliff](https://git-cliff.org/) для:
- Анализа коммитов с момента последнего релиза
- Фильтрации коммитов по области (cli, app, server)
- Определения наличия изменений, готовых к релизу
- Автоматической генерации списков изменений

### 3. Пайплайн релиза

Когда обнаружены изменения, готовые к релизу:

1. **Расчёт версии**: пайплайн определяет номер следующей версии
2. **Генерация списка изменений**: git cliff создаёт список изменений на основе сообщений коммитов
3. **Процесс сборки**: компонент собирается и тестируется
4. **Генерация артефактов**: создаются специфичные для релиза ресурсы, такие как архивы CLI, контрольные суммы и спецификация CLI `tuist.spec.json`, генерируемая из `tuist --experimental-dump-help`
5. **Создание релиза**: в GitHub создаётся релиз с артефактами
6. **Дистрибуция**: обновления публикуются в менеджерах пакетов (например, Homebrew для CLI)

Для приложения это приводит к стабильному релизу при каждом слиянии. Для CLI, сервера и Kura тот же автоматический пайплайн выпускает только предварительный релиз **canary**; релиз-кандидаты и стабильные релизы выпускаются еженедельно (см. [Каналы релизов](#release-channels)).

### 4. Фильтрация по области

Каждый компонент выпускается только при наличии соответствующих изменений:

- **CLI**: коммиты с областью `(cli)` или без указания области
- **App**: коммиты с областью `(app)`
- **Server**: коммиты с областью `(server)`

## Каналы релизов

CLI, сервер и Kura не переводятся в стабильный релиз при каждом пуше в `main`. Каждый из них поставляется через три канала, чтобы рекомендуемая установка оставалась стабильной и медленно меняющейся, в то время как ранние пользователи получают сборки для каждого коммита. Пользовательская документация находится по адресам <.localized_link href="/cli/release-channels">Каналы релизов CLI</.localized_link> и <.localized_link href="/guides/server/self-host/release-channels">Каналы релизов сервера</.localized_link>; этот раздел предназначен для сопровождающих.

Каждый компонент использует одни и те же три канала:

| Канал | Версия | Выпускается |
|---------|---------|--------|
| Canary | `X.Y.0-canary.N` | Автоматически, при каждом пуше в `main`, затрагивающем компонент (`<component>-release.yml`; для Kura вызывается из `server-production-deployment.yml` при изменении `kura/`) |
| Релиз-кандидат | `X.Y.0-rc.N` | Каждый понедельник или вручную, в ветке `releases/<component>-<major>.<minor>.x` (`<component>-rc.yml`) |
| Стабильный | `X.Y.Z` | Каждый понедельник или вручную, путём перевода проверенного RC в стабильный статус (`<component>-promote.yml`); патчи через `<component>-backport.yml` |

Сборки Canary и RC публикуются как предварительные релизы GitHub (никогда не помечаются как «Latest», никогда не перемещают тег `:latest` в GHCR, никогда не отправляются в Homebrew для CLI), поэтому менеджеры пакетов и резолверы образов подбирают их только при явном согласии. Canary всегда нацелен на следующую невыпущенную минорную версию: как только выпущена линия RC, canary в `main` переходит к следующей минорной версии.

Hosted Tuist не ждёт стабильного релиза: сервер развёртывается для каждого коммита, а hosted canary и production используют новейший canary Kura. Каскад развёртывания публикует этот canary перед его развёртыванием, поэтому у `kura-release.yml` нет собственного триггера. Изменения только в инструментах релиза Kura (`mise/tasks/kura/**`, рабочие процессы `kura-*.yml`) не приводят к выпуску canary.

Вы никогда не выбираете номера версий вручную. Следующая версия каждого канала выводится из существующих git-тегов скриптом `mise/tasks/<component>/release/channel-version.sh`, который вызывают приведённые ниже рабочие процессы.

Ветки релизов изолированы по компонентам (`releases/server-X.Y.x`, `releases/kura-X.Y.x`, `releases/X.Y.x` для CLI), чтобы линии трёх компонентов никогда не пересекались на одной минорной версии.

### Еженедельное расписание

Каждый понедельник в 06:00 UTC задачи продвижения трёх компонентов выполняются синхронно: **CLI Promote to Stable**, **Server Promote to Stable** и **Kura Promote to Stable**. Каждая задача продвигает новейшую линию RC, которая ещё не стала стабильной, обычно это линия, выпущенная в предыдущий понедельник. Если такой линии нет, задача ничего не публикует.

По завершении продвижения, независимо от результата, запускается соответствующий рабочий процесс **Release Candidate** (`cli-rc.yml`, `server-rc.yml`, `kura-rc.yml`) через `workflow_run` и выпускает следующую линию из `main`.

Если запланированный запуск завершается ошибкой или отменяется, в Slack отправляется сообщение. Продвижение завершается ошибкой, если в ветке релиза есть коммиты после её последнего RC. Чтобы выпустить эту линию, создайте на ней новый RC и переведите его в стабильный статус вручную. В противном случае в следующий понедельник будет продвинута более новая линия.

### Выпуск релиз-кандидата

Чтобы выпустить новую линию вне еженедельного расписания (подставьте ниже имя рабочего процесса компонента):

1. Запустите рабочий процесс **\<Component\> Release Candidate** (например, `server-rc.yml`) с пустым входным параметром `branch`.
2. Он публикует `X.Y.0-rc.1` и после успешного завершения создаёт защищённую ветку `releases/<component>-X.Y.x` на собранном коммите.

Чтобы добавить критическое исправление или устранить регрессию в линии, проходящей тестирование, сделайте cherry-pick в ветку релиза через PR (тот же поток, что и для бэкпортов; CI никогда не делает cherry-pick самостоятельно), затем обновите RC:

1. Создайте ветку от `releases/<component>-X.Y.x`, сделайте cherry-pick исправления, откройте PR обратно в ветку релиза, разрешите возможные конфликты и выполните слияние.
2. Снова запустите рабочий процесс **Release Candidate** с параметром `branch=releases/<component>-X.Y.x`. Он опубликует `X.Y.0-rc.(N+1)` из HEAD ветки.

Линия, проходящая тестирование, заморожена по функциональности: в неё попадают только критические исправления и устранение регрессий.

### Продвижение в стабильный статус

Чтобы продвинуть линию вне еженедельного расписания после чистого прохождения тестирования её RC:

1. Запустите рабочий процесс **\<Component\> Promote to Stable** (например, `server-promote.yml`) с параметром `branch=releases/<component>-X.Y.x`.
2. Он публикует образ с семантическим тегом, устанавливая `make_latest=true`, что перемещает тег GHCR `:latest` и указатель «Latest» на GitHub. CLI дополнительно обновляет формулу Homebrew.

Продвижение не запускается, если HEAD ветки не совпадает точно с коммитом, на который указывает последний RC. Поэтому, если после последнего RC в линию было внесено какое-либо исправление, необходимо создать новый RC и дать ему пройти тестирование, прежде чем он сможет быть выпущен как стабильный.

### Бэкпорт исправлений в стабильную линию

Когда линия становится стабильной, выпускайте патчи с помощью рабочего процесса **Backport Release**, доступного только для CLI (`cli-backport.yml`): сделайте cherry-pick исправления в `releases/X.Y.x` через PR, затем запустите рабочий процесс для этой ветки, чтобы выпустить `X.Y.(Z+1)`. Бэкпорты никогда не перемещают указатель «Latest» и не обновляют формулу Homebrew. Одновременно поддерживаются две линии: текущая линия получает исправления регрессий и уязвимостей безопасности, предыдущая — только критические исправления и исправления безопасности. Другие исправления ошибок не бэкпортятся и выходят со следующим минорным релизом.

Для сервера и Kura используйте **Server Backport Release** (`server-backport.yml`) или **Kura Backport Release** (`kura-backport.yml`). Продвижение публикует только `X.Y.0` версии RC; оно не может применять патчи к уже стабильной линии.

1. Начните с последнего опубликованного стабильного тега компонента на выбранной линии `X.Y` и откройте точечный PR с исправлением в существующую ветку `releases/server-X.Y.x` или `releases/kura-X.Y.x`. Более новый минорный или мажорный релиз не препятствует применению патчей к поддерживаемой линии. Не включайте изменения из main, не относящиеся к делу.
2. Проверьте и слейте исходный PR. Получите удалённую ветку релиза и подтвердите её **финальный слитый коммит**, включая поддерживаемые платформы компонента и положительные/отрицательные контрольные точки TLS. Успешное прохождение проверок до слияния не означает квалификацию другого squash-коммита или коммита слияния.
3. После отдельного утверждения публикации запустите соответствующий рабочий процесс бэкпорта **из main**, установив `source_sha` в тот самый квалифицированный слитый коммит, а `base_tag` — в текущий стабильный тег, например `server@X.Y.Z` или `kura@X.Y.Z`. Рабочий процесс выводит `X.Y.(Z+1)`; операторы не выбирают повышение минорной версии и не продвигают содержимое canary.

Планирование обновляет точную удалённую ветку и базовый тег, требует, чтобы выбранный источник совпадал с концом ветки, отвергает неизменённый источник или изменения вне компонента, его входные данные политики доверия и валидацию, и фиксирует разрешённый базовый коммит. Контроли, основанные на main, повторяют эти проверки непосредственно перед публикацией версионированного образа и на GitHub. Если исходная ветка, стабильная база или доступная версия изменяются, остановитесь и повторно проведите квалификацию, вместо замены источника.

Бэкпорты используют существующие мультиплатформенные издатели без перемещения указателя «Latest» на GitHub или тега GHCR `:latest`. Публикация отделена от развёртывания на хостинге: эти рабочие процессы не продвигают размещённый сервер или флот Kura, не обходят приёмку canary и не квалифицируют установленную среду выполнения. Вспомогательные скрипты сборки и Dockerfile по-прежнему берутся из выбранного стабильного источника. Контроли являются шлюзами публикации, а не задачами сборки без учётных данных; сборки могут загружать нетегированные дайджесты образов до финального шлюза. Публикация образа и на GitHub не является атомарной: если позже шлюз завершается ошибкой после публикации тега образа, изучите частичный релиз и сохраните его исходный источник и артефакты. Никогда не заменяйте существующий релиз и не используйте другие байты под его тегом.

## Написание хороших сообщений коммитов

Поскольку сообщения коммитов напрямую влияют на примечания к релизу, важно писать понятные и описательные сообщения:

### Рекомендуется:
- Используйте настоящее время: «add feature», а не «added feature»
- Будьте лаконичны, но описательны
- Указывайте область изменений, если они относятся к конкретному компоненту
- Ссылайтесь на задачи, если применимо: `fix(cli): resolve build cache issue (#1234)`

### Не рекомендуется:
- Использовать расплывчатые сообщения, такие как «fix bug» или «update code»
- Объединять несколько несвязанных изменений в одном коммите
- Забывать включать информацию о критических изменениях

### Критические изменения

Для критических изменений включайте `BREAKING CHANGE:` в тело коммита:

```
feat(cli): change cache directory structure

BREAKING CHANGE: Cache files are now stored in a new directory structure.
Users need to clear their cache after updating.
```

## Рабочие процессы релизов

CLI, сервер и Kura имеют собственный набор рабочих процессов каналов, следующих одной структуре:

- `<component>-release.yml` — публикует canary при каждом пуше в `main`, затрагивающем компонент (для Kura это переиспользуемый рабочий процесс, который вызывает `server-production-deployment.yml` при изменении `kura/`)
- `<component>-rc.yml` — выпускает релиз-кандидат каждый понедельник (через `workflow_run` после продвижения) или вручную выпускает либо обновляет его
- `<component>-promote.yml` — продвигает ожидающий RC в стабильный статус каждый понедельник в 06:00 UTC или вручную продвигает прошедший тестирование RC
- `<component>-build-publish.yml` — общий конвейер сборки и публикации, вызываемый тремя вышеуказанными процессами

CLI, сервер и Kura также имеют `<component>-backport.yml` для выпуска патчей на стабильной линии. Все рабочие процессы компонента сериализуются через одну группу параллелизма `<component>-publish`, чтобы разрешение версий и тегирование никогда не конкурировали. Исключение составляет Kura: её canary использует `kura-canary-publish`, а RC, продвижение и бэкпорт используют `kura-stable-publish`, поэтому длительная стабильная сборка никогда не задерживает каскад развёртывания. Пространства имён тегов не пересекаются, поэтому две группы не могут конфликтовать.

Диаграмма Helm и образы инфраструктуры (оператор CAPI, контроллер раннеров, образ обработчика xcresult, образ linux-раннера и так далее) по-прежнему выпускаются через `.github/workflows/server-production-deployment.yml`. Процесс запускается при пушах в `main` и выполняет каскад canary → приёмочные тесты → продакшен для хостингового развёртывания, публикуя эти инфраструктурные компоненты по мере их изменения. Приложение, кэш, плагин Gradle, навыки, Noora и отдельные контроллеры инфраструктуры имеют выделенный рабочий процесс `*-release.yml`. Все они используют общий механизм обнаружения изменений: `mise/tasks/release/components.json` объявляет префикс тега и пути включения для каждого компонента, а git cliff превращает соответствующие коммиты в примечания к релизу.

Сборка CLI (`cli-build-publish.yml`) также создаёт и публикует артефакт `tuist.spec.json` (генерируемый из `tuist --experimental-dump-help`), чтобы сторонние инструменты могли использовать интерфейс командной строки.

## Мониторинг релизов

Вы можете отслеживать релизы через:
- [Страницу релизов на GitHub](https://github.com/tuist/tuist/releases)
- Вкладку GitHub Actions для запусков рабочих процессов
- Файлы changelog в директории каждого компонента

## Преимущества

Этот подход непрерывных релизов обеспечивает:

- **Быструю доставку**: Изменения попадают к пользователям сразу после слияния
- **Снижение узких мест**: Не нужно ждать ручных релизов
- **Чёткую коммуникацию**: Автоматизированные списки изменений на основе сообщений коммитов
- **Единый процесс**: Одинаковый поток релиза для всех компонентов
- **Гарантию качества**: Выпускаются только протестированные изменения

## Устранение неполадок

Если релиз не удался:

1. Проверьте логи GitHub Actions для неудачного рабочего процесса
2. Убедитесь, что сообщения ваших коммитов соответствуют общепринятому формату
3. Проверьте, что все тесты проходят
4. Убедитесь, что компонент успешно собирается

Для срочных исправлений, требующих немедленного выпуска:
1. Убедитесь, что ваш коммит имеет чёткую область действия
2. После слияния отслеживайте рабочий процесс релиза
3. Для сервера или приложения исправление выйдет со следующим пушем; при необходимости запустите ручной релиз
4. Для CLI исправление автоматически попадает в canary. Чтобы доставить его пользователям стабильной версии, выполните бэкпорт в поддерживаемую стабильную линию (линии) (см. [Бэкпорт исправлений в стабильную линию](#backporting-fixes-to-a-stable-line))

## Релиз в App Store

Хотя CLI и сервер следуют описанному выше процессу непрерывных релизов, **iOS-приложение** является исключением из-за процесса проверки Apple App Store:

- **Ручные релизы**: Релизы iOS-приложения требуют ручной отправки в App Store
- **Задержки проверки**: Каждый релиз должен пройти проверку Apple, которая может занять 1–7 дней
- **Пакетные изменения**: Несколько изменений обычно объединяются в каждый релиз iOS
- **TestFlight**: Бета-версии могут распространяться через TestFlight перед релизом в App Store
- **Примечания к релизу**: Должны быть написаны специально в соответствии с требованиями App Store

iOS-приложение всё ещё следует тем же соглашениям о коммитах и использует git cliff для генерации списка изменений, но фактический релиз для пользователей происходит по менее частому, ручному графику.
