Перейти к содержимому

Релизы

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

Обзор#

Мы выпускаем следующие основные компоненты:

  • Tuist CLI — инструмент командной строки, поставляемый через каналы canary, кандидата в релиз и стабильный каналы
  • Tuist Server — бэкенд-сервисы, поставляемые по той же канальной модели, что и CLI
  • Kura — кэш-сервер, поставляемый по той же канальной модели, что и CLI
  • Tuist App — приложения для macOS и iOS, выпускаемые непрерывно (iOS-приложение развёртывается только в TestFlight, подробнее здесь)

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

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

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

Мы используем Conventional Commits для структурирования сообщений коммитов. Это позволяет нашим инструментам понимать характер изменений, определять повышение версий и генерировать соответствующие списки изменений.

Формат: 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) и должны быть указаны в теле коммита:

plaintext
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 для:

  • Анализа коммитов с момента последнего релиза
  • Фильтрации коммитов по области (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; релиз-кандидаты и стабильные релизы выпускаются еженедельно (см. Каналы релизов).

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

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

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

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

CLI, сервер и Kura не переводятся в стабильный релиз при каждом пуше в main. Каждый из них поставляется через три канала, чтобы рекомендуемая установка оставалась стабильной и медленно меняющейся, в то время как ранние пользователи получают сборки для каждого коммита. Пользовательская документация находится по адресам Каналы релизов CLI и Каналы релизов сервера; этот раздел предназначен для сопровождающих.

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

КаналВерсияВыпускается
CanaryX.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 — в текущий стабильный тег, например [email protected] или [email protected]. Рабочий процесс выводит 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: в тело коммита:

plaintext
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), чтобы сторонние инструменты могли использовать интерфейс командной строки.

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

Вы можете отслеживать релизы через:

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

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

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

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

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

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

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

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

Релиз в App Store#

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

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

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