> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-trino-dialect.mintlify.site/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 lançamento 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 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 lançamento, 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 lançamento,
* pull requests com várias combinações de rótulos de backport,
* PRs de lançamento com o rótulo `release` apontando para as branches de lançamento.

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 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 lançamento ativas
</div>

Uma branch de lançamento ativa é qualquer branch cujo PR de lançamento 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 um novo lançamento é criado ou quando um antigo chega ao fim de vida.

Um rótulo específico de versão define o lançamento *mais antigo* que o PR precisa alcançar: ele recebe backport para esse lançamento **e para todas as branches de lançamento ativas mais recentes**, não apenas para o nomeado. Por exemplo, `v25.3-must-backport` em um PR merged na branch de development faz backport para `25.3` e para todos os lançamentos ativos 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 os mais recentes.

O lançamento nomeado não precisa estar ativo. Um rótulo para um lançamento em fim de vida (um sem PR de lançamento aberto) ainda leva a correção adiante para todos os lançamentos ativos posteriores, para que uma atualização a partir desse lançamento 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 o próprio `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 lançamento ativas                                                                                                                                                                                                                                          |
| `pr-must-backport-force`                                    | Backport para todas as branches de lançamento ativas (equivalente a `pr-must-backport`)                                                                                                                                                                                                       |
| `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 lançamento **e para todas as branches de lançamento 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 |
| `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                                                                                                                                                                                                                        |

<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>

<div id="discover-active-releases">
  #### 1. Identifique as releases ativas
</div>

`BackportPRs.receive_release_prs` consulta o GitHub em busca de todos os PRs abertos com o rótulo `release`. As refs de origem desses PRs correspondem aos nomes dos branches de release (por exemplo, `release/25.3`). A partir delas, é derivado o conjunto de rótulos específicos de versão a serem procurados: todos os rótulos `v{VER}-must-backport` que existirem no repositório e cuja versão não seja mais recente do que a release ativa mais recente. Rótulos mais antigos são incluídos mesmo quando sua release não está mais ativa (um rótulo mais recente do que todas as releases ativas é ignorado, pois não poderia se expandir para nenhum branch ativo), de modo que um PR rotulado para uma release em fim de vida útil ainda seja encontrado, desde que uma release mais recente esteja ativa.

<div id="find-prs-to-backport">
  #### 2. Encontrar PRs para backport
</div>

`BackportPRs.receive_prs_for_backport` usa a API de busca do GitHub para encontrar PRs mesclados que:

* tenham pelo menos um rótulo de backport (`pr-must-backport`, `pr-must-backport-force`, `pr-critical-bugfix` ou um rótulo específico da versão), e
* **não** já tenham `pr-backports-created`, e
* tenham sido mesclados após a data do commit mais antigo encontrada em qualquer release branch, e
* tenham sido atualizados nos últimos 90 dias (para manter a consulta de busca eficiente).

<div id="cherry-pick-stage">
  #### 3. Etapa de cherry-pick (`ReleaseBranch.create_cherrypick`)
</div>

Para cada par (PR original, branch de lançamento) em que ainda não exista um PR de cherry-pick:

1. Faça checkout da branch de lançamento 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 lançamento — 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.

<div id="auto-merge-conflict-free-cherry-pick-prs">
  #### 4. Merge automático de PRs de cherry-pick sem conflitos
</div>

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.

<div id="backport-stage">
  #### 5. Etapa de backport (`ReleaseBranch.create_backport`)
</div>

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 lançamento 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 lançamento 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ô).

<div id="completion">
  #### 6. Conclusão
</div>

Quando todas as branches de lançamento de um determinado PR original tiverem recebido backport, o bot adicionará `pr-backports-created` ao PR original.

<div id="pre-check">
  #### 7. Verificação prévia
</div>

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 lançamento. Se for, o PR é considerado como já tendo recebido backport e é ignorado.

<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.
