Modelo de lançamento
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.
Política de backport
- 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 quepr-must-backportseja 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.
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.
Ferramenta de Backport
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.
Testes
- 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
releaseapontando para as branches de release.
Branches de release ativas
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.
Implementação
Visão geral
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):
- 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.
- Um PR de backport é criado na branch de release real, com as alterações aplicadas via cherry-pick consolidadas em um único commit.
Rótulos
Nomenclatura de branches e PRs
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>
Processo passo a passo
1
Descubra os lançamentos ativos
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.2
Encontre PRs para fazer 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-bugfixou 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).
3
Tratamento da branch durante o rolling-out
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.4
Etapa de cherry-pick (ReleaseBranch.create_cherrypick)
Para cada par (PR original, branch de release) em que ainda não exista um PR de cherry-pick:
- Faça checkout da branch de release e crie uma branch de backport (
backport/release/X.Y/N) a partir dela. - Execute
git merge -s ourscontra o primeiro parent do commit de merge para criar uma base de merge sintética, sem alterações de conteúdo. - Crie à força uma branch de cherry-pick (
cherrypick/release/X.Y/N) apontando diretamente para o commit de merge do PR original. - Tente executar
git merge --no-commit --no-ffda 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.
- Crie o PR de cherry-pick com destino a
backport/release/X.Y/Na partir decherrypick/release/X.Y/N, com os rótulospr-cherrypickedo not test. - Propague
pr-bugfixoupr-critical-bugfixdo PR original, se aplicável. - Os responsáveis não são definidos neste momento; eles só são adicionados quando forem detectados conflitos.
5
Merge automático de PRs de cherry-pick sem conflitos
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.
6
Etapa de backport (ReleaseBranch.create_backport)
Depois que o PR de cherry-pick for mesclado:
- Faça checkout da branch de backport e execute pull.
- Encontre a merge-base entre a branch de release e a branch de backport.
- Execute
git reset --softaté a merge-base, fazendo squash de todos os commits de cherry-pick em um só. - Faça commit usando o título do PR de backport como mensagem.
- Faça force-push da branch de backport e abra um PR de backport com destino à branch de release real.
- Adicione ao PR o rótulo
pr-backport(epr-bugfix/pr-critical-bugfix, se aplicável). - Atribua o PR ao autor do PR original, a quem fez o merge e aos responsáveis já definidos (excluindo contas de robô).
7
Conclusão
Quando todas as branches de release de um determinado PR original tiverem recebido backport, o bot adicionará
pr-backports-created ao PR original.8
Verificação prévia
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.Tratamento de Cherry-pick PRs Inativos
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ótulopr-backports-createdserá removido do PR original para que ele possa ser reprocessado.
- 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.
Resolução de conflitos
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:
- Remova o rótulo
pr-cherrypickda PR decherry-pick. - Exclua a branch
cherrypick/.... - Remova
pr-backports-createdda PR original, se estiver presente.
CI para PRs de backport
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).
Autenticação
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.
Cache da API do GitHub
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.
Tratamento de erros
BackportException será gerada. No CI, isso dispara uma notificação via CIBuddy para o chat da equipe.