> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-fix-nav-issues.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Обзор политики бэкпорта ClickHouse и автоматизированной системы

# Backport System

В этом документе описаны политика бэкпорта ClickHouse и автоматизированная система, которая её реализует.

<div id="release-model">
  ## Модель релизов
</div>

Версии ClickHouse следуют схеме `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 месяцев.

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

<div id="backport-policy">
  ## Политика бэкпортов
</div>

Бэкпортируются не все изменения. Цель — поддерживать ветки релиза в стабильном состоянии, поэтому объём бэкпортов намеренно ограничен:

* **Исправления уязвимостей** — бэкпортируются всегда.
* **Исправления критических ошибок** (исключения (логические ошибки), потеря данных, некорректные результаты, проблемы с 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, чтобы человек мог разрешить конфликты и завершить бэкпорт.

<div id="backport-tool">
  ## Инструмент бэкпорта
</div>

Описанная выше политика бэкпорта реализована в автоматизированном инструменте `tests/ci/cherry_pick.py`. Инструмент запускается как workflow GitHub Actions в инфраструктуре ClickHouse и охватывает все требования: обнаружение активных веток релиза, выбор PR, подходящих для бэкпорта, выполнение двухэтапной процедуры `cherry-pick` и бэкпорта, разрешение конфликтов, соблюдение политики задержки и поддержание синхронизации меток.

Долгосрочная цель — выделить эту реализацию в автономный open-source инструмент на Python, который смогут использовать и другие проекты. Целевая архитектура выглядит так:

* **Настраиваемый** — все параметры политики (подходящие метки, окно задержки, пороги устаревания PR, поведение во время раскатки и т. д.) задаются в виде файла конфигурации, чтобы инструмент можно было адаптировать под требования бэкпорта любого проекта без изменений в коде.
* **Распространяемый** — упакован как самодостаточный Python wheel, устанавливаемый из PyPI, без зависимости от CI-инфраструктуры ClickHouse.
* **Программируемый** — предоставляет понятную объектную модель для pull request, меток и веток релиза, чтобы пользователи могли строить собственные workflow поверх базового движка.

<div id="testing">
  ### Тестирование
</div>

Планируемой частью автономного инструмента является отдельный набор тестов вместе с облегчённой тестовой инфраструктурой. Эта инфраструктура сможет поднимать временные GitHub-репозитории (или их локальные эквиваленты), заранее заполненные:

* настраиваемым набором веток, представляющих линии релизов,
* pull request’ами с различными комбинациями меток бэкпорта,
* релизными PR с меткой `release`, указывающими на ветки релизов.

Это позволит тестам проходить полный цикл автоматизации — обнаружение меток, создание веток для cherry-pick, обработку конфликтов, создание PR бэкпорта, логику назначения назначенных исполнителей, пропуск во время раскатки и политику задержек — на реальном, но временном репозитории, не затрагивая production-среду. Эту же инфраструктуру можно повторно использовать для регрессионного тестирования изменений политик перед их развёртыванием.

<div id="active-release-branches">
  ## Активные ветки релиза
</div>

Активной считается любая ветка релиза, для которой соответствующий релизный PR (с меткой `release`) всё ещё открыт на GitHub. Автоматизация бэкпорта определяет такие ветки динамически при каждом запуске, поэтому при выходе нового релиза или завершении жизненного цикла старого никаких изменений в конфигурации не требуется.

Ветка релиза может находиться в состоянии **раскатки** (если её релизный PR помечен меткой `rolling-out`) в период развёртывания нового релиза. Обычные бэкпорты для веток в состоянии раскатки приостанавливаются, чтобы не усложнять этот процесс. Метки, привязанные к конкретной версии (например, `v25.3-must-backport`), переопределяют это поведение и принудительно запускают бэкпорт даже во время раскатки.

<div id="implementation">
  ## Реализация
</div>

<div id="overview">
  ### Обзор
</div>

Автоматизация бэкпортов запускается каждый час в виде workflow `CherryPick` в GitHub Actions (`.github/workflows/cherry_pick.yml`), реализованного в `tests/ci/cherry_pick.py`. Она работает через GitHub API и локальные операции git на самоуправляемом раннере `style-checker-aarch64`.

Процесс состоит из двух этапов для каждой пары (original PR, ветка релиза):

1. Создается **PR cherry-pick**, чтобы отделить разрешение конфликтов от фактического слияния в целевую ветку. Если конфликтов нет, он сливается автоматически.
2. Создается **бэкпорт PR** в реальную ветку релиза, при этом изменения после cherry-pick объединяются в один коммит.

<div id="labels">
  ### Метки
</div>

Метки в исходном PR определяют, будет ли выполняться бэкпорт и в какие ветки.

| Метка                                                    | Эффект                                                                                                                               |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `pr-must-backport`                                       | Бэкпорт во все активные ветки релиза (кроме веток, помеченных `rolling-out`)                                                         |
| `pr-must-backport-force`                                 | Бэкпорт во все активные ветки релиза без учёта ограничений `rolling-out`                                                             |
| `pr-critical-bugfix`                                     | Автоматически включает `pr-must-backport` (через `AUTO_BACKPORT` в `pr_labels_and_category.py`)                                      |
| `v{VER}-must-backport` (например, `v25.3-must-backport`) | Бэкпорт только в указанную ветку релиза; для этой ветки отменяет пропуск из-за `rolling-out`                                         |
| `pr-backports-created`                                   | Устанавливается ботом, когда созданы все необходимые PR для бэкпорта; снимается, если PR cherry-pick переоткрыт                      |
| `pr-cherrypick`                                          | Применяется к PR cherry-pick, созданным ботом                                                                                        |
| `pr-backport`                                            | Применяется к PR бэкпорта, созданным ботом                                                                                           |
| `do not test`                                            | Применяется к PR cherry-pick, чтобы CI не запускался для них                                                                         |
| `rolling-out`                                            | Устанавливается в **release PR**, чтобы показать, что его ветка сейчас находится в процессе раскатки; обычные бэкпорты её пропускают |

<div id="branch-and-pr-naming">
  ### Именование веток и PR
</div>

Для каждого номера исходного 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>`

<div id="step-by-step-process">
  ### Пошаговая инструкция
</div>

<div id="discover-active-releases">
  #### 1. Поиск активных релизов
</div>

`BackportPRs.receive_release_prs` запрашивает в GitHub все открытые PR с меткой `release`. Head ref этих PR — это имена веток релиза (например, `release/25.3`). На их основе формируется набор меток совместимости: `v25.3-must-backport` и т. д.

<div id="find-prs-to-backport">
  #### 2. Найдите PR для бэкпорта
</div>

`BackportPRs.receive_prs_for_backport` использует API поиска GitHub, чтобы найти слитые PR, которые:

* имеют как минимум одну метку для бэкпорта (`pr-must-backport`, `pr-must-backport-force`, `pr-critical-bugfix` или метку для конкретной версии), и
* **не** имеют `pr-backports-created`, и
* были слиты после самой ранней даты коммита, найденной в любой release branch, и
* обновлялись в течение последних 90 дней (чтобы поисковый запрос оставался эффективным).

<div id="rolling-out-branch-handling">
  #### 3. Обработка ветки в процессе раскатки
</div>

Если релизный PR помечен меткой `rolling-out`, общие метки бэкпорта (`pr-must-backport`, `pr-critical-bugfix`) не применяются к этой ветке. Бот закрывает все ранее созданные PR с cherry-pick или бэкпортом для этой ветки, добавляя поясняющий комментарий. Метка для конкретной версии (например, `v25.3-must-backport`) всегда имеет приоритет над этим правилом. `pr-must-backport-force` отключает проверку `rolling-out` для всех веток.

<div id="cherry-pick-stage">
  #### 4. Этап cherry-pick (`ReleaseBranch.create_cherrypick`)
</div>

Для каждой пары (исходный PR, ветка релиза), для которой PR cherry-pick ещё не существует:

1. Переключитесь на ветку релиза и создайте от неё **ветку бэкпорта** (`backport/release/X.Y/N`).
2. Выполните `git merge -s ours` относительно первого родителя коммита слияния, чтобы создать синтетическую базу слияния без изменений содержимого.
3. Принудительно создайте **ветку cherry-pick** (`cherrypick/release/X.Y/N`), указывающую непосредственно на коммит слияния исходного PR.
4. Попробуйте выполнить `git merge --no-commit --no-ff` ветки cherry-pick в ветку бэкпорта:
   * Если ветка уже в актуальном состоянии, изменение уже присутствует в ветке релиза — пометьте как выполненное и пропустите.
   * В противном случае (с конфликтами или без них) выполните reset и отправьте обе ветки.
5. Создайте PR cherry-pick из `cherrypick/release/X.Y/N` в `backport/release/X.Y/N`, добавив метки `pr-cherrypick` и `do not test`.
6. Перенесите `pr-bugfix` или `pr-critical-bugfix` из исходного PR, если применимо.
7. Назначенные исполнители на этом этапе **не** задаются; их добавляют только при обнаружении конфликтов.

<div id="auto-merge-conflict-free-cherry-pick-prs">
  #### 5. Автоматическое слияние PR cherry-pick без конфликтов
</div>

Если PR cherry-pick можно слить без конфликтов, бот автоматически выполняет слияние через GitHub API и сразу переходит к этапу бэкпорта.

<div id="backport-stage">
  #### 6. Этап бэкпорта (`ReleaseBranch.create_backport`)
</div>

После слияния PR cherry-pick:

1. Переключитесь на ветку бэкпорта и выполните `pull`.
2. Найдите merge-base между веткой релиза и веткой бэкпорта.
3. Выполните `git reset --soft` до merge-base, объединив все cherry-pick-коммиты в один.
4. Создайте коммит, используя в качестве сообщения заголовок PR бэкпорта.
5. Выполните принудительный `push` ветки бэкпорта и откройте PR бэкпорта в целевую ветку релиза.
6. Добавьте к PR метку `pr-backport` (а также `pr-bugfix` / `pr-critical-bugfix`, если применимо).
7. Назначьте PR автору исходного PR, пользователю, выполнившему слияние, и уже назначенным исполнителям (исключая учётные записи роботов).

<div id="completion">
  #### 7. Завершение
</div>

Когда бэкпорт выполнен во все ветки релиза для данного исходного PR, бот добавляет к исходному PR метку `pr-backports-created`.

<div id="pre-check">
  #### 8. Предварительная проверка
</div>

Перед началом любой работы с PR `ReleaseBranch.pre_check` запускает `git merge-base --is-ancestor`, чтобы убедиться, что коммит слияния ещё не достижим из релизной ветки. Если достижим, PR считается уже бэкпортированным и пропускается.

<div id="stale-cherry-pick-pr-handling">
  ### Обработка устаревших PR cherry-pick
</div>

Класс `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, чтобы его можно было обработать повторно.

Для PR cherry-pick, ожидающих ручного разрешения конфликтов:

* Через **3 дня** без обновлений бот публикует комментарий с Ping и упоминанием назначенных исполнителей.
* Через **7 дней** без обновлений бот публикует комментарий о закрытии и закрывает PR.

<div id="conflict-resolution">
  ### Разрешение конфликтов
</div>

Если при cherry-pick возникают конфликты, PR cherry-pick остаётся открытым, чтобы их мог разрешить человек. Бот назначает его автору исходного PR, тому, кто его слил, и назначенным исполнителям. После разрешения конфликтов и слияния PR cherry-pick бот создаёт PR бэкпорта при следующем запуске, который выполняется раз в час.

Чтобы полностью отказаться от бэкпорта, закройте PR cherry-pick. Бот воспримет это как намеренный пропуск.

Чтобы заново создать повреждённый PR cherry-pick с нуля:

1. Удалите метку `pr-cherrypick` из PR cherry-pick.
2. Удалите ветку `cherrypick/...`.
3. Удалите `pr-backports-created` из исходного PR, если он есть.

<div id="ci-for-backport-prs">
  ### CI для PR бэкпорта
</div>

PR бэкпорта нацелены на ветки релиза, поэтому для них используется отдельный CI workflow (`BackportPR`, определённый в `ci/workflows/backport_branches.py`), а не стандартный workflow для pull request. Этот workflow запускает репрезентативное подмножество CI: сборки ASan/UBSan и TSan, релизные сборки, сборки для macOS, функциональные тесты под ASan, стресс-тесты под TSan и интеграционные тесты. Он проверяет, что ветка бэкпорта содержит от 1 до 50 коммитов и как минимум один изменённый файл (это проверяется скриптом `check_backport_branch.py`).

<div id="authentication">
  ### Аутентификация
</div>

В этом рабочем процессе для операций git push используется SSH-ключ (`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`) исключаются при назначении ответственного.

<div id="github-api-cache">
  ### Кэш API GitHub
</div>

`GitHubCache` (из `cache_utils.py`) сохраняет кэш объектов PyGithub в S3, сокращая количество вызовов API при ежечасных запусках. Кэш загружается в начале и выгружается в конце каждого запуска.

<div id="error-handling">
  ### Обработка ошибок
</div>

Ошибки при обработке отдельных PR перехватываются и записываются в журнал, но не прерывают выполнение. После обработки всех PR, если возникли какие-либо ошибки, выбрасывается `BackportException`. В CI это приводит к отправке уведомления в командный чат через `CIBuddy`.
