> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> Visão geral da política de backport e da automação do ClickHouse

# Backport System

Este documento descreve a política de backport do ClickHouse e o sistema automatizado que a implementa.

<div id="release-model">
  ## Modelo de lançamento
</div>

As versões do ClickHouse seguem o formato `YY.M.patch.build-type`, em que `YY` é o ano com dois dígitos, `M` é o mês do lançamento (sem zero à esquerda), `patch` é o número do patch dentro da branch, `build` é um número de build que cresce monotonamente, e `type` é `stable` ou `lts`.

Exemplo: `25.3.8.23-lts` — LTS de março de 2025, patch 8, build 23.

Há dois canais de lançamento:

* As versões **Stable** são publicadas aproximadamente uma vez por mês. As três versões stable mais recentes recebem patches, o que garante aproximadamente três meses de suporte ativo para cada versão.
* As versões **LTS (Long-Term Support)** são publicadas em março e agosto de cada ano. Duas versões LTS têm suporte simultaneamente, cada uma por pelo menos 12 meses.

Recomenda-se que os usuários que executam workloads de produção usem a versão stable mais recente ou uma versão LTS e atualizem rapidamente para novas versões de patch, já que versões de patch não introduzem mudanças incompatíveis.

<div id="backport-policy">
  ## Política de backport
</div>

Nem todas as mudanças passam por backport. O objetivo é manter as branches de release estáveis, por isso o escopo dos backports é intencionalmente limitado:

* **Correções de segurança** — sempre passam por backport.
* **Correções de bugs críticos** (exceptions (erros lógicos), perda de dados, resultados incorretos, problemas de RBAC) — selecionadas automaticamente para backport de acordo com as regras gerais de backport; identificadas pelo rótulo `pr-critical-bugfix`, que faz com que `pr-must-backport` seja adicionado automaticamente.
* **Correções de estabilidade e regressões** — passam por backport quando o risco da mudança é baixo em relação ao risco de deixar o bug sem correção; identificadas por `pr-must-backport`, adicionado manualmente pelos maintainers.
* **Correções de bugs menores com workaround disponível** — em geral, não passam por backport para evitar desestabilizar as branches de release.
* **Novos recursos, melhorias e trabalho de performance** — não passam por backport.

O rótulo `pr-must-backport` é a substituição manual usada pelos maintainers para marcar um PR para backport. O rótulo `pr-critical-bugfix` faz com que `pr-must-backport` seja adicionado automaticamente pelo hook de CI (consulte `pr_labels_and_category.py`).

**Escalonamento de conflitos.** Quando o backport automático não consegue resolver conflitos de merge, ainda assim um cherry-pick PR deve ser criado e atribuído ao autor, a quem fez o merge e às pessoas já atribuídas no PR original, para que alguém resolva os conflitos e conclua o backport.

<div id="backport-tool">
  ## Ferramenta de Backport
</div>

A política de backport descrita acima é implementada pela ferramenta automatizada em `tests/ci/cherry_pick.py`. A ferramenta é executada como um workflow do GitHub Actions na infraestrutura do ClickHouse e cobre todos os requisitos: descobrir branches de release ativas, selecionar PRs qualificadas para backport, executar o procedimento de cherry-pick e backport em duas etapas, gerenciar conflitos, aplicar a política de atraso e manter os rótulos sincronizados.

O objetivo de longo prazo é extrair essa implementação para uma ferramenta open-source independente em Python que outros projetos possam adotar. O design pretendido é:

* **Configurável** — todos os parâmetros da política (rótulos de qualificação, janela de atraso, limites para PRs desatualizadas, comportamento durante rolling-out etc.) expressos em um arquivo de configuração, para que a ferramenta possa ser adaptada aos requisitos de backport de qualquer projeto sem alterações no código.
* **Distribuível** — empacotada como uma wheel Python autocontida, instalável via PyPI, sem dependência da infraestrutura de CI do ClickHouse.
* **Programável** — expondo um modelo de objetos claro para pull requests, rótulos e branches de release, para que os usuários possam criar scripts e workflows personalizados sobre o engine principal.

<div id="testing">
  ### Testes
</div>

Uma parte planejada da ferramenta independente é uma suíte de testes dedicada, juntamente com uma infraestrutura de testes leve. A infraestrutura será capaz de criar temporariamente repositórios do GitHub (ou equivalentes locais) já preenchidos com:

* um conjunto configurável de branches que representam linhas de release,
* pull requests com várias combinações de rótulos de backport,
* PRs de release com o rótulo `release` apontando para as branches de release.

Isso permite que os testes exercitem todo o ciclo de automação — detecção de rótulos, criação de branch de cherry-pick, tratamento de conflitos, criação de PR de backport, lógica de atribuição de responsáveis, omissão durante rolling-out e política de atraso — em um repositório real, mas descartável, sem afetar o estado de produção. A mesma infraestrutura também pode ser reutilizada para testes de regressão de mudanças de política antes da implantação.

<div id="active-release-branches">
  ## Branches de release ativas
</div>

Uma branch de release ativa é qualquer branch cujo PR de release correspondente (com o rótulo `release`) ainda esteja aberto no GitHub. A automação de backport detecta essas branches dinamicamente a cada execução, portanto não é necessário fazer alterações de configuração quando uma nova release é criada ou quando uma antiga chega ao fim de vida.

Uma branch de release pode estar no estado **rolling-out** (seu PR de release tem o rótulo `rolling-out`) durante o período em que uma nova release está sendo implantada. Os backports gerais são pausados para branches em rolling-out para evitar complicar o rollout. Rótulos específicas de versão (por exemplo, `v25.3-must-backport`) substituem esse comportamento e forçam o backport mesmo durante um rollout.

Um rótulo específico de versão define a release *mais antiga* que o PR precisa alcançar: ele recebe backport para essa release **e para todas as branches de release ativas mais recentes**, não apenas para a nomeada. Por exemplo, `v25.3-must-backport` em um PR merged na branch de development faz backport para `25.3` e para todas as releases ativas posteriores (`25.4`, `25.5`, …). Se houver vários rótulos específicos de versão, a menor versão prevalece, já que ela já cobre as mais recentes.

A release nomeada não precisa estar ativa. Um rótulo para uma release em fim de vida (uma sem PR de release aberto) ainda leva a correção adiante para todas as releases ativas posteriores, para que uma atualização a partir dessa release nunca perca a correção silenciosamente. Por exemplo, `v25.12-must-backport` em um PR continua fazendo backport para `26.1`, `26.2`, … mesmo depois de a própria `25.12` ter chegado ao fim de vida.

<div id="implementation">
  ## Implementação
</div>

<div id="overview">
  ### Visão geral
</div>

A automação de backport é executada a cada hora como o workflow `CherryPick` do GitHub Actions (`.github/workflows/cherry_pick.yml`), implementado em `tests/ci/cherry_pick.py`. Ela opera por meio da API do GitHub e de operações locais do git em um runner `style-checker-aarch64` self-hosted.

O processo ocorre em duas etapas para cada par (PR original, branch de release):

1. Um **PR de cherry-pick** é criado para isolar a resolução de conflitos do destino real do merge. Se não houver conflitos, ele será mesclado automaticamente.
2. Um **PR de backport** é criado na branch de release real, com as alterações aplicadas via cherry-pick consolidadas em um único commit.

<div id="labels">
  ### Rótulos
</div>

Os rótulos no PR original controlam se e onde o backport será feito.

| Rótulo                                                      | Efeito                                                                                                                                                                                                                                                                                                                                                            |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pr-must-backport`                                          | Backport para todas as branches de release ativas (ignorando branches marcadas como `rolling-out`)                                                                                                                                                                                                                                                                |
| `pr-must-backport-force`                                    | Backport para todas as branches de release ativas, ignorando as restrições de `rolling-out`                                                                                                                                                                                                                                                                       |
| `pr-critical-bugfix`                                        | Aciona `pr-must-backport` automaticamente (via `AUTO_BACKPORT` em `pr_labels_and_category.py`)                                                                                                                                                                                                                                                                    |
| `v{VER}-must-backport` (por exemplo, `v25.3-must-backport`) | Backport para essa branch de release **e para todas as branches de release ativas mais novas** — a versão marca o *lançamento* mais antigo que o PR deve alcançar, mesmo quando o lançamento nomeado já estiver em fim de vida. Com vários rótulos desse tipo, a menor versão prevalece. Sobrescreve o comportamento de ignorar `rolling-out` para essas branches |
| `pr-backports-created`                                      | Definido pelo bot quando todos os PRs de backport obrigatórios tiverem sido criados; removido se um PR de cherry-pick for reaberto                                                                                                                                                                                                                                |
| `pr-cherrypick`                                             | Aplicado aos PRs de cherry-pick criados pelo bot                                                                                                                                                                                                                                                                                                                  |
| `pr-backport`                                               | Aplicado aos PRs de backport criados pelo bot                                                                                                                                                                                                                                                                                                                     |
| `do not test`                                               | Aplicado aos PRs de cherry-pick para que o CI não seja executado neles                                                                                                                                                                                                                                                                                            |
| `rolling-out`                                               | Definido em um **PR de release** para indicar que sua branch está em rollout no momento; backports gerais a ignoram                                                                                                                                                                                                                                               |

<div id="branch-and-pr-naming">
  ### Nomenclatura de branches e PRs
</div>

Para cada número de PR original `N` e branch de release `release/X.Y`:

* Branch de cherry-pick: `cherrypick/release/X.Y/N`
* Branch de backport: `backport/release/X.Y/N`
* Título da PR de cherry-pick: `Cherry pick #N to release/X.Y: <original title>`
* Título da PR de backport: `Backport #N to release/X.Y: <original title>`

<div id="step-by-step-process">
  ### Processo passo a passo
</div>

<Steps>
  <Step title="Descubra os lançamentos ativos" id="discover-active-releases">
    `BackportPRs.receive_release_prs` consulta o GitHub em busca de todos os PRs abertos com o rótulo `release`. Os head refs desses PRs são os nomes das branches de lançamento (por exemplo, `release/25.3`). A partir deles, ele deriva o conjunto de rótulos específicos de versão a procurar: todo rótulo `v{VER}-must-backport` que exista no repositório e cuja versão não seja mais recente do que o lançamento ativo mais recente. Rótulos mais antigos são incluídos mesmo quando seu lançamento não está mais ativo (um rótulo mais recente do que todos os lançamentos ativos é ignorado, já que não poderia se expandir para nenhuma branch ativa), portanto um PR rotulado para um lançamento em fim de vida ainda é encontrado, desde que um lançamento mais recente esteja ativo.
  </Step>

  <Step title="Encontre PRs para fazer backport" id="find-prs-to-backport">
    `BackportPRs.receive_prs_for_backport` usa a API de busca do GitHub para encontrar PRs mesclados que:

    * têm pelo menos um rótulo de backport (`pr-must-backport`, `pr-must-backport-force`, `pr-critical-bugfix` ou um rótulo específico de versão), e
    * **não** têm `pr-backports-created`, e
    * tiveram merge após a data do commit mais antigo encontrada em qualquer branch de release, e
    * foram atualizados nos últimos 90 dias (para manter a consulta de busca eficiente).
  </Step>

  <Step title="Tratamento da branch durante o rolling-out" id="rolling-out-branch-handling">
    Quando um PR de release recebe o rótulo `rolling-out`, os rótulos gerais de backport (`pr-must-backport`, `pr-critical-bugfix`) ignoram essa branch. O bot fecha quaisquer PRs de cherry-pick ou de backport criados anteriormente para essa branch com um comentário explicativo. Um rótulo específico de versão (por exemplo, `v25.3-must-backport`) sempre prevalece sobre isso — para o release nomeado e para cada branch de release ativa mais recente à qual ele se expande. `pr-must-backport-force` ignora a verificação de `rolling-out` para todas as branches.
  </Step>

  <Step title={<>Etapa de cherry-pick (<code>ReleaseBranch.create_cherrypick</code>)</>} id="cherry-pick-stage">
    Para cada par (PR original, branch de release) em que ainda não exista um PR de cherry-pick:

    1. Faça checkout da branch de release e crie uma **branch de backport** (`backport/release/X.Y/N`) a partir dela.
    2. Execute `git merge -s ours` contra o primeiro parent do commit de merge para criar uma base de merge sintética, sem alterações de conteúdo.
    3. Crie à força uma **branch de cherry-pick** (`cherrypick/release/X.Y/N`) apontando diretamente para o commit de merge do PR original.
    4. Tente executar `git merge --no-commit --no-ff` da branch de cherry-pick na branch de backport:
       * Se já estiver atualizada, a alteração já está presente na branch de release — marque como concluído e pule esta etapa.
       * Caso contrário (com ou sem conflitos), faça reset e envie ambas as branches.
    5. Crie o PR de cherry-pick com destino a `backport/release/X.Y/N` a partir de `cherrypick/release/X.Y/N`, com os rótulos `pr-cherrypick` e `do not test`.
    6. Propague `pr-bugfix` ou `pr-critical-bugfix` do PR original, se aplicável.
    7. Os responsáveis **não** são definidos neste momento; eles só são adicionados quando forem detectados conflitos.
  </Step>

  <Step title="Merge automático de PRs de cherry-pick sem conflitos" id="auto-merge-conflict-free-cherry-pick-prs">
    Se o PR de cherry-pick puder ser mesclado (sem conflitos), o bot faz o merge automaticamente pela API do GitHub e prossegue imediatamente para a etapa de backport.
  </Step>

  <Step title={<>Etapa de backport (<code>ReleaseBranch.create_backport</code>)</>} id="backport-stage">
    Depois que o PR de cherry-pick for mesclado:

    1. Faça checkout da branch de backport e execute pull.
    2. Encontre a merge-base entre a branch de release e a branch de backport.
    3. Execute `git reset --soft` até a merge-base, fazendo squash de todos os commits de cherry-pick em um só.
    4. Faça commit usando o título do PR de backport como mensagem.
    5. Faça force-push da branch de backport e abra um PR de backport com destino à branch de release real.
    6. Adicione ao PR o rótulo `pr-backport` (e `pr-bugfix` / `pr-critical-bugfix`, se aplicável).
    7. Atribua o PR ao autor do PR original, a quem fez o merge e aos responsáveis já definidos (excluindo contas de robô).
  </Step>

  <Step title="Conclusão" id="completion">
    Quando todas as branches de release de um determinado PR original tiverem recebido backport, o bot adicionará `pr-backports-created` ao PR original.
  </Step>

  <Step title="Verificação prévia" id="pre-check">
    Antes de iniciar qualquer trabalho em um PR, `ReleaseBranch.pre_check` executa `git merge-base --is-ancestor` para verificar se o commit de merge já é alcançável a partir da branch de release. Se for, o PR é considerado como já tendo recebido backport e é ignorado.
  </Step>
</Steps>

<div id="stale-cherry-pick-pr-handling">
  ### Tratamento de Cherry-pick PRs Inativos
</div>

A classe `CherryPickPRs` é executada no início de cada execução horária e trata de dois cenários:

* **PRs de cherry-pick órfãos**: se a branch de release de um PR de cherry-pick não tiver mais um PR de release aberto (ou seja, o release foi fechado), o PR de cherry-pick será fechado automaticamente.
* **PRs de cherry-pick reabertos**: se um PR original já tiver o rótulo `pr-backports-created`, mas um PR de cherry-pick correspondente ainda estiver aberto, o rótulo `pr-backports-created` será removido do PR original para que ele possa ser reprocessado.

Para PRs de cherry-pick que aguardam resolução manual de conflitos:

* Após **3 dias** sem atualizações, o bot publica um comentário de ping mencionando os responsáveis atribuídos.
* Após **7 dias** sem atualizações, o bot publica um comentário de encerramento e fecha o PR.

<div id="conflict-resolution">
  ### Resolução de conflitos
</div>

Quando um `cherry-pick` gera conflitos, a PR de `cherry-pick` permanece aberta para resolução manual. O bot a atribui ao autor da PR original, a quem fez o merge e aos responsáveis designados. Depois que os conflitos são resolvidos e a PR de `cherry-pick` é mesclada, o bot cria a PR de backport na próxima execução horária.

Para descartar um backport completamente, feche a PR de `cherry-pick`. O bot a tratará como intencionalmente ignorada.

Para recriar do zero uma PR de `cherry-pick` com falha:

1. Remova o rótulo `pr-cherrypick` da PR de `cherry-pick`.
2. Exclua a branch `cherrypick/...`.
3. Remova `pr-backports-created` da PR original, se estiver presente.

<div id="ci-for-backport-prs">
  ### CI para PRs de backport
</div>

Os PRs de backport têm como destino branches de release, por isso usam um workflow de CI dedicado (`BackportPR`, definido em `ci/workflows/backport_branches.py`) em vez do workflow padrão de pull request. Esse workflow executa um subconjunto representativo da CI: builds com ASan/UBSan e TSan, builds de release, builds de macOS, testes funcionais com ASan, testes de estresse com TSan e testes de integração. Ele verifica se a branch de backport tem entre 1 e 50 commits e pelo menos um arquivo alterado (conforme validado por `check_backport_branch.py`).

<div id="authentication">
  ### Autenticação
</div>

O workflow usa uma chave SSH (`ROBOT_CLICKHOUSE_SSH_KEY`) para operações de `git push`. As chamadas à API do GitHub são autenticadas via `get_best_robot_token`, que seleciona o token com a maior cota restante de um conjunto armazenado no SSM (`/github-tokens`). `ROBOT_CLICKHOUSE_COMMIT_TOKEN` é usado pela etapa de checkout no workflow do Actions, não para chamadas de API. As contas de robô (`robot-clickhouse`, `clickhouse-gh`) são excluídas ao atribuir um responsável.

<div id="github-api-cache">
  ### Cache da API do GitHub
</div>

`GitHubCache` (de `cache_utils.py`) salva o cache de objetos do PyGithub no S3, reduzindo as chamadas à API entre execuções horárias. O cache é baixado no início e enviado ao final de cada execução.

<div id="error-handling">
  ### Tratamento de erros
</div>

Erros durante o processamento individual de PRs são capturados e registrados em log, mas não interrompem a execução. Depois que todos os PRs forem processados, se tiver ocorrido algum erro, uma `BackportException` será gerada. No CI, isso dispara uma notificação via `CIBuddy` para o chat da equipe.
