Модель релизов
YY.M.patch.build-type, где YY — двузначный год, M — месяц релиза (без ведущего нуля), patch — номер патча в ветке, build — монотонно возрастающий номер сборки, а type — либо stable, либо lts.
Пример: 25.3.8.23-lts — LTS-релиз за март 2025 года, патч 8, сборка 23.
Существует два трека релизов:
- Стабильные релизы публикуются примерно раз в месяц. Патчи выпускаются для трёх последних стабильных релизов, что даёт каждому релизу около трёх месяцев активной поддержки.
- Релизы LTS (Long-Term Support) публикуются в марте и августе каждого года. Одновременно поддерживаются две версии LTS, каждая — не менее 12 месяцев.
Политика бэкпортов
- Исправления уязвимостей — бэкпортируются всегда.
- Исправления критических ошибок (исключения (логические ошибки), потеря данных, некорректные результаты, проблемы с RBAC) — автоматически отбираются для бэкпорта по общим правилам бэкпортирования; определяются по метке
pr-critical-bugfix, из-за которой автоматически добавляетсяpr-must-backport. - Исправления, связанные со стабильностью и регрессиями — бэкпортируются, если риск изменения невелик по сравнению с риском оставить ошибку без исправления; определяются по
pr-must-backport, которую мейнтейнеры добавляют вручную. - Незначительные исправления ошибок при наличии обходного пути — как правило, не бэкпортируются, чтобы не дестабилизировать ветки релиза.
- Новые возможности, улучшения, оптимизации производительности — не бэкпортируются.
pr-must-backport — это ручное переопределение, которое мейнтейнеры используют, чтобы пометить PR для бэкпортирования. Метка pr-critical-bugfix приводит к тому, что pr-must-backport автоматически добавляется хуком CI (см. pr_labels_and_category.py).
Эскалация конфликтов. Если при автоматическом бэкпортировании не удаётся разрешить конфликты слияния, PR cherry-pick всё равно должен быть создан и назначен автору, тому, кто выполнил слияние, и текущим назначенным исполнителям исходного PR, чтобы человек мог разрешить конфликты и завершить бэкпорт.
Инструмент бэкпорта
tests/ci/cherry_pick.py. Инструмент запускается как workflow GitHub Actions в инфраструктуре ClickHouse и охватывает все требования: обнаружение активных веток релиза, выбор PR, подходящих для бэкпорта, выполнение двухэтапной процедуры cherry-pick и бэкпорта, разрешение конфликтов, соблюдение политики задержки и поддержание синхронизации меток.
Долгосрочная цель — выделить эту реализацию в автономный open-source инструмент на Python, который смогут использовать и другие проекты. Целевая архитектура выглядит так:
- Настраиваемый — все параметры политики (подходящие метки, окно задержки, пороги устаревания PR, поведение во время раскатки и т. д.) задаются в виде файла конфигурации, чтобы инструмент можно было адаптировать под требования бэкпорта любого проекта без изменений в коде.
- Распространяемый — упакован как самодостаточный Python wheel, устанавливаемый из PyPI, без зависимости от CI-инфраструктуры ClickHouse.
- Программируемый — предоставляет понятную объектную модель для pull request, меток и веток релиза, чтобы пользователи могли строить собственные workflow поверх базового движка.
Тестирование
- настраиваемым набором веток, представляющих линии релизов,
- pull request’ами с различными комбинациями меток бэкпорта,
- релизными PR с меткой
release, указывающими на ветки релизов.
Активные ветки релиза
release) всё ещё открыт на GitHub. Автоматизация бэкпорта определяет такие ветки динамически при каждом запуске, поэтому при выходе нового релиза или завершении жизненного цикла старого никаких изменений в конфигурации не требуется.
Ветка релиза может находиться в состоянии раскатки (если её релизный PR помечен меткой rolling-out) в период развёртывания нового релиза. Обычные бэкпорты для веток в состоянии раскатки приостанавливаются, чтобы не усложнять этот процесс. Метки, привязанные к конкретной версии (например, v25.3-must-backport), переопределяют это поведение и принудительно запускают бэкпорт даже во время раскатки.
Реализация
Обзор
CherryPick в GitHub Actions (.github/workflows/cherry_pick.yml), реализованного в tests/ci/cherry_pick.py. Она работает через GitHub API и локальные операции git на самоуправляемом раннере style-checker-aarch64.
Процесс состоит из двух этапов для каждой пары (original PR, ветка релиза):
- Создается PR cherry-pick, чтобы отделить разрешение конфликтов от фактического слияния в целевую ветку. Если конфликтов нет, он сливается автоматически.
- Создается бэкпорт PR в реальную ветку релиза, при этом изменения после cherry-pick объединяются в один коммит.
Метки
Именование веток и PR
N и ветки релиза release/X.Y:
- Ветка для cherry-pick:
cherrypick/release/X.Y/N - Ветка бэкпорта:
backport/release/X.Y/N - Заголовок PR cherry-pick:
Cherry pick #N to release/X.Y: <original title> - Заголовок PR бэкпорта:
Backport #N to release/X.Y: <original title>
Пошаговая инструкция
1. Поиск активных релизов
BackportPRs.receive_release_prs запрашивает в GitHub все открытые PR с меткой release. Head ref этих PR — это имена веток релиза (например, release/25.3). На их основе формируется набор меток совместимости: v25.3-must-backport и т. д.
2. Найдите PR для бэкпорта
BackportPRs.receive_prs_for_backport использует API поиска GitHub, чтобы найти слитые PR, которые:
- имеют как минимум одну метку для бэкпорта (
pr-must-backport,pr-must-backport-force,pr-critical-bugfixили метку для конкретной версии), и - не имеют
pr-backports-created, и - были слиты после самой ранней даты коммита, найденной в любой release branch, и
- обновлялись в течение последних 90 дней (чтобы поисковый запрос оставался эффективным).
3. Обработка ветки в процессе раскатки
rolling-out, общие метки бэкпорта (pr-must-backport, pr-critical-bugfix) не применяются к этой ветке. Бот закрывает все ранее созданные PR с cherry-pick или бэкпортом для этой ветки, добавляя поясняющий комментарий. Метка для конкретной версии (например, v25.3-must-backport) всегда имеет приоритет над этим правилом. pr-must-backport-force отключает проверку rolling-out для всех веток.
4. Этап cherry-pick (ReleaseBranch.create_cherrypick)
- Переключитесь на ветку релиза и создайте от неё ветку бэкпорта (
backport/release/X.Y/N). - Выполните
git merge -s oursотносительно первого родителя коммита слияния, чтобы создать синтетическую базу слияния без изменений содержимого. - Принудительно создайте ветку cherry-pick (
cherrypick/release/X.Y/N), указывающую непосредственно на коммит слияния исходного PR. - Попробуйте выполнить
git merge --no-commit --no-ffветки cherry-pick в ветку бэкпорта:- Если ветка уже в актуальном состоянии, изменение уже присутствует в ветке релиза — пометьте как выполненное и пропустите.
- В противном случае (с конфликтами или без них) выполните reset и отправьте обе ветки.
- Создайте PR cherry-pick из
cherrypick/release/X.Y/Nвbackport/release/X.Y/N, добавив меткиpr-cherrypickиdo not test. - Перенесите
pr-bugfixилиpr-critical-bugfixиз исходного PR, если применимо. - Назначенные исполнители на этом этапе не задаются; их добавляют только при обнаружении конфликтов.
5. Автоматическое слияние PR cherry-pick без конфликтов
6. Этап бэкпорта (ReleaseBranch.create_backport)
- Переключитесь на ветку бэкпорта и выполните
pull. - Найдите merge-base между веткой релиза и веткой бэкпорта.
- Выполните
git reset --softдо merge-base, объединив все cherry-pick-коммиты в один. - Создайте коммит, используя в качестве сообщения заголовок PR бэкпорта.
- Выполните принудительный
pushветки бэкпорта и откройте PR бэкпорта в целевую ветку релиза. - Добавьте к PR метку
pr-backport(а такжеpr-bugfix/pr-critical-bugfix, если применимо). - Назначьте PR автору исходного PR, пользователю, выполнившему слияние, и уже назначенным исполнителям (исключая учётные записи роботов).
7. Завершение
pr-backports-created.
8. Предварительная проверка
ReleaseBranch.pre_check запускает git merge-base --is-ancestor, чтобы убедиться, что коммит слияния ещё не достижим из релизной ветки. Если достижим, PR считается уже бэкпортированным и пропускается.
Обработка устаревших PR cherry-pick
CherryPickPRs запускается в начале каждого ежечасного выполнения и обрабатывает два сценария:
- Осиротевшие PR cherry-pick: если для ветки релиза, связанной с PR cherry-pick, больше нет открытого релизного PR (то есть релиз закрыт), PR cherry-pick закрывается автоматически.
- Повторно открытые PR cherry-pick: если у исходного PR уже есть метка
pr-backports-created, но PR cherry-pick для него всё ещё открыт, меткаpr-backports-createdудаляется из исходного PR, чтобы его можно было обработать повторно.
- Через 3 дня без обновлений бот публикует комментарий с Ping и упоминанием назначенных исполнителей.
- Через 7 дней без обновлений бот публикует комментарий о закрытии и закрывает PR.
Разрешение конфликтов
- Удалите метку
pr-cherrypickиз PR cherry-pick. - Удалите ветку
cherrypick/.... - Удалите
pr-backports-createdиз исходного PR, если он есть.
CI для PR бэкпорта
BackportPR, определённый в ci/workflows/backport_branches.py), а не стандартный workflow для pull request. Этот workflow запускает репрезентативное подмножество CI: сборки ASan/UBSan и TSan, релизные сборки, сборки для macOS, функциональные тесты под ASan, стресс-тесты под TSan и интеграционные тесты. Он проверяет, что ветка бэкпорта содержит от 1 до 50 коммитов и как минимум один изменённый файл (это проверяется скриптом check_backport_branch.py).
Аутентификация
ROBOT_CLICKHOUSE_SSH_KEY). Вызовы GitHub API аутентифицируются через get_best_robot_token, который выбирает токен с наибольшей оставшейся квотой из пула, хранящегося в SSM (/github-tokens). ROBOT_CLICKHOUSE_COMMIT_TOKEN используется на шаге checkout в workflow Actions, а не для вызовов API. Аккаунты роботов (robot-clickhouse, clickhouse-gh) исключаются при назначении ответственного.
Кэш API GitHub
GitHubCache (из cache_utils.py) сохраняет кэш объектов PyGithub в S3, сокращая количество вызовов API при ежечасных запусках. Кэш загружается в начале и выгружается в конце каждого запуска.
Обработка ошибок
BackportException. В CI это приводит к отправке уведомления в командный чат через CIBuddy.