O que Manutenção de Fork Downstream faz?
Visão Geral
O propósito de manter um fork downstream não é deixá-lo gradualmente se afastar do upstream e se tornar um fork independente em evolução. É manter continuamente um conjunto de patches que permaneçam utilizáveis, construíveis e testáveis.
O downstream segue o upstream enquanto fornece prontamente aos usuários código e artefatos contendo recursos e correções quando os PRs ainda não foram mesclados, os mantenedores do upstream estão ocupados, ou um patch é nicho mas urgente. Não é um arquivo de patches estático, mas um canal de distribuição onde os patches realmente são executados e permanecem sob validação.
Um resultado integrado utilizável também permite que mais pessoas testem livremente PRs do upstream, patches downstream e suas próprias alterações. Conforme o upstream avança, construir, testar e revisar continuamente os patches reduz o risco de que os PRs se deteriorem após ficarem para trás do upstream, expõe problemas mais cedo e ajuda as alterações a eventualmente fluírem de volta para o upstream.
Objetivos Principais
- Evitar que o fork downstream se torne um fork independente que permaneça separado do upstream.
- Seguir continuamente o upstream, em vez de preservar apenas uma branch estática de um ponto no tempo.
- Manter cada patch independentemente construível, testável, revisável e adequado para submissão ou manutenção contínua de PR.
- Fornecer código e artefatos diretamente utilizáveis através da branch principal ou de release.
- Permitir que os usuários testem PRs, patches e suas próprias alterações contra um resultado integrado real.
- Detectar conflitos entre patches e o upstream cedo, manter os PRs mescláveis e ajudar os patches a fluírem para o upstream.
Modelo de Branches
O repositório determina os nomes reais das branches. Não assuma uma branch padrão, nome de remote ou plataforma de hospedagem.
Branch Main do Upstream
A branch main do upstream é o ponto de partida para cada reconstrução. Busque seu estado mais recente antes de sincronizar e use um commit específico do upstream como linha de base para a construção atual.
A branch main ou de release do downstream não deve se tornar a nova linha de base para patches. Um patch deve sempre deixar claro o que altera em relação ao upstream.
Branches de Patch
Mantenha cada patch independente em sua própria branch e siga os padrões normais de PR:
- Mantenha o escopo focado e os commits e a explicação claros.
- Inclua testes suficientes para demonstrar o comportamento.
- Torne-o independentemente construível e revisável no upstream atual.
- Declare apenas dependências necessárias e evite vinculá-lo a outros patches downstream.
- Torne-o adequado para submissão direta como um PR upstream ou para manutenção contínua de um PR existente.
- Em geral, uma branch de patch não deve modificar arquivos de release mantidos centralmente pelos mantenedores do upstream, como
CHANGELOG. As alterações de documentação necessárias devem ser limitadas a comentários de código, documentação de API e notas de implementação diretamente relacionadas àquele patch. Mantenha a lista de patches downstream e as notas de release centralmente na branch de manutenção.
A branch de patch é a fonte de verdade de longo prazo para aquele patch. Um commit de integração na branch de release não é a única cópia do patch e não deve conter correções temporárias que existam apenas no histórico de release.
Branches de Patch de Compatibilidade Temporária
Quando vários patches se aplicam independentemente, mas produzem um conflito de construção, teste ou comportamento quando combinados que não pertence a nenhuma branch de PR individual, mantenha um patch de compatibilidade em uma branch tmp/patch/<nome>.
Tal branch aborda apenas o problema de compatibilidade downstream para uma combinação específica de patches. Não force a correção em uma das branches de PR independentes e não a deixe se tornar uma nova branch de recurso de longo prazo. Ela deve declarar de quais patches depende, onde pertence na ordem, qual comportamento combinado valida e quando pode ser aposentada.
Na orquestração de sincronização na branch ci, adicione tmp/patch/<nome> em ordem imediatamente após os patches dos quais depende. Se uma mesclagem Git anterior já produziu conflitos textuais não resolvidos, uma branch de compatibilidade posterior não pode cruzar esse conflito. Nesse caso, a orquestração ci deve aplicar explicitamente o tratamento de compatibilidade correspondente na etapa onde o conflito ocorre, antes de continuar a reconstrução; apenas anexar a branch ao final da lista não é suficiente.
A Branch de Manutenção ci
Quando o fork precisar carregar automação como GitHub Actions, mantenha uma branch ci separada. Se o repositório já tiver uma branch equivalente, mantenha seu nome. Esta branch é a sobreposição de release do fork downstream e é responsável por:
- CI/CD específica do downstream, empacotamento, releases e configuração do repositório.
- Buscar automaticamente o upstream, integrar patches em ordem, reconstruir a branch de release e fazer push com segurança.
- Testes adicionais de patch único e testes de integração de múltiplos patches downstream.
- O README downstream, lista de patches, notas de release e quaisquer instruções de manutenção específicas do downstream.
- A ordem de integração fixa de patches regulares habilitados e branches
tmp/patch/<nome>.
Se o usuário ou fork downstream tiver requisitos de manutenção além deste fluxo de trabalho geral, mantenha-os em um arquivo MAINTAIN.md de propriedade do downstream na branch ci. Ele deve declarar como este fork em particular deve ser mantido e registrar quaisquer regras especiais que futuros mantenedores e automação devem seguir. Trate-o como orientação específica do repositório: leia-o antes de alterar branches de patch, orquestração de sincronização ou histórico de release, e mantenha-o atualizado quando esses requisitos mudarem.
Quando as regras especiais de manutenção poderiam ser perdidas, adicione um aviso conciso no README.md da branch ci com um link para MAINTAIN.md. Mantenha as regras detalhadas em MAINTAIN.md em vez de duplicá-las no README.
A branch ci pode carregar centralmente a automação de release downstream, mas não misture patches de comportamento de produto nela. As alterações de runtime devem permanecer em branches adequadas para construção, teste e submissão de PR upstream independentes.
Esta skill inclui o arquivo GitHub Actions usado pela prática atual: ref/upstream-sync.yml. Quando a sincronização automática do GitHub for necessária, copie-o como ponto de partida para .github/workflows/upstream-sync.yml, depois adapte o endereço upstream, a branch padrão/de release, as listas ci e de patches, a ordem tmp/patch/<nome>, o agendamento de sincronização, os arquivos de propriedade do downstream, segredos e verificações para o repositório alvo. O arquivo retém configuração específica do projeto de seu projeto original e não deve ser habilitado sem revisão.
Ao configurar schedule, primeiro pergunte ao usuário durante quais horas do dia a sincronização deve ser executada. O cron do GitHub usa UTC por padrão, a menos que o workflow defina explicitamente um timezone suportado. Não agende trabalhos no minuto 0 da hora: a documentação oficial do GitHub afirma que a carga é alta no início de cada hora, então os trabalhos agendados podem ser atrasados ou até descartados sob carga suficientemente alta. Para evitar ainda mais marcas comuns de cinco minutos, não use um múltiplo de 5, como 10, 15, 20, 30, 40 ou 45. Escolha aleatoriamente um minuto de 1 a 59 que não seja um múltiplo de 5 e escreva-o como um valor fixo no workflow, por exemplo executando python -c 'import secrets; print(secrets.choice([m for m in range(1, 60) if m % 5]))'. Aqui, "aleatório" se aplica apenas ao gerar a configuração; não faça o workflow esperar por um atraso aleatório dinâmico em cada execução.
Branch Main ou de Release
A branch main ou de release começa a partir do upstream mais recente e integra a branch de manutenção e as branches de patch necessárias em uma ordem fixa. Ela fornece o código completo, builds, testes e artefatos de release.
Esta branch representa apenas o resultado integrado downstream atual. Não é a fonte de manutenção de longo prazo para nenhum patch. Um problema pode ser reproduzido e analisado aqui primeiro, mas a correção deve retornar à branch de patch ou manutenção proprietária, após o que o resultado integrado deve ser regenerado.
No histórico de release, cada patch deve aparecer como um commit de integração claro e contável, tornando visíveis tanto o número de patches downstream quanto o número de commits que o fork está atrasado em relação ao upstream.
Fluxo de Trabalho
1. Inspecione o Estado Atual
Antes de começar, leia as instruções de manutenção e contribuição do próprio repositório, incluindo o MAINTAIN.md downstream quando presente e qualquer aviso no README que aponte para ele. Inspecione a árvore de trabalho, remotes, branch atual, relacionamentos upstream e processo de sincronização e release existente. Preserve ou contorne qualquer trabalho de usuário não commitado; não o limpe ou sobrescreva sem permissão.
2. Verifique Issues e Pull Requests do Upstream Antes de Codificar
Antes de implementar qualquer nova solicitação ou escrever código, pesquise o rastreador de issues upstream e pull requests para discussão ou implementação relacionada. Verifique issues abertas e fechadas e PRs abertos, rascunhos, fechados e mesclados; pesquise não apenas a redação exata da solicitação, mas também nomes de componentes relevantes, mensagens de erro e sinônimos razoáveis. Siga issues vinculadas, revisões, commits e PRs substitutos o suficiente para estabelecer o status atual.
Use o resultado para escolher a próxima ação:
- Se o upstream já implementa o requisito, verifique a versão aplicável e o comportamento antes de decidir se alguma alteração downstream ainda é necessária.
- Se uma issue ou PR ativo cobrir isso, prefira contribuir, testar, atualizar ou carregar temporariamente esse trabalho em vez de criar um duplicado ignorante.
- Se uma abordagem anterior foi rejeitada, revertida ou substituída, entenda o motivo declarado antes de propor uma nova implementação.
- Registre os links upstream relevantes e a conclusão no plano de trabalho, notas de patch ou descrição do PR para que a decisão permaneça revisável.
Quando issues ou PRs upstream relacionados existirem, não prossiga diretamente para a implementação. Primeiro estabeleça seu status real, escopo, decisões, preocupações não resolvidas, feedback de revisão, estado de implementação e relacionamento com a solicitação do usuário. Em seguida, sintetize as informações upstream relevantes para o usuário em vez de encaminhar uma lista bruta de links. Explique claramente o que foi aprendido, como isso afeta as suposições e o plano atuais e quais partes da solicitação original permanecem necessárias.
Se essa investigação produzir uma nova descoberta que possa alterar o escopo, abordagem, prioridade, propriedade, estratégia de compatibilidade ou necessidade do trabalho, pergunte explicitamente ao usuário se o plano deve ser alterado. Pause a implementação até que o usuário confirme se deve manter ou revisar o plano. Não reinterprete silenciosamente a solicitação com base na discussão upstream.
Não comece a implementação meramente porque uma pesquisa com termo exato não retornou nenhum resultado. Realize uma pesquisa razoável primeiro; se nenhuma discussão ou implementação relevante for encontrada, declare esse resultado e continue.
3. Reproduza no Resultado Integrado Completo
Primeiro reproduza o problema na branch main ou de release atual para confirmar como ele ocorre na combinação de patches que os usuários realmente executam.
Durante a análise, distinga entre:
- Um problema no próprio upstream.
- Um problema em um patch independente.
- Um problema de integração entre patches; se não pertencer a nenhum PR individual, coloque-o em
tmp/patch/<nome>. - Um problema em CI, empacotamento, sincronização ou documentação downstream.
4. Localize e Corrija a Branch Proprietária
Encontre a branch de patch responsável pelo comportamento e conclua a correção e os testes nessa branch. Se o problema disser respeito à compatibilidade entre vários patches, crie ou atualize tmp/patch/<nome>. Se disser respeito à automação de release ou outro trabalho de manutenção downstream, modifique a branch ci.
Não faça commit de uma correção de conflito ou correção de produto apenas na branch main ou de release. Ela seria perdida na próxima reconstrução a partir do upstream e impediria que o patch fosse revisado independentemente.
5. Mantenha o Patch Independentemente Utilizável
Seguindo as convenções do repositório, atualize o patch para o upstream mais recente, resolva conflitos na branch de patch e execute as construções e testes exigidos por esse patch.
Confirme que ele não depende de alterações ocultas na branch de release e ainda pode ser explicado, revisado e submetido como um PR por conta própria. Se um PR upstream já existir, atualize-o também e continue mantendo-o em resposta ao feedback upstream.
6. Reconstrua a Branch Main ou de Release
Busque o upstream mais recente novamente, crie uma branch candidata limpa a partir de um cabeçalho específico do upstream e, em seguida, integre a sobreposição de release, patches regulares e branches tmp/patch/<nome> necessárias na ordem fixa declarada pela branch ci.
Cada patch regular e patch de compatibilidade temporária deve produzir seu próprio commit de integração claro no histórico de release. Um repositório pode usar squash merge ou o método equivalente estabelecido pelo projeto, mas o resultado deve ser fácil de revisar e contar.
7. Valide o Resultado Utilizável
Execute as verificações exigidas do repositório contra a versão candidata completa, cobrindo pelo menos:
- Testes focados para cada patch em si.
- Comportamento integrado após combinar vários patches.
- Se CI, empacotamento e documentação downstream vêm das branches corretas.
- Se o código ou artefatos que os usuários realmente obtêm correspondem ao commit validado.
Atualize a branch main ou de release remota apenas depois que tanto o código candidato quanto os artefatos passarem em toda a validação exigida.
Sincronização Automática
O objetivo da sincronização automática é regenerar periodicamente um resultado de release downstream utilizável a partir de um novo cabeçalho upstream, não continuar acumulando correções em uma branch de release antiga.
O processo de sincronização deve:
- Usar os controles de concorrência aceitos do repositório para evitar que dois processos de sincronização ou release atualizem a mesma branch ao mesmo tempo.
- Buscar o upstream mais recente, a branch
ci, todos os patches regulares habilitados e todas as branchestmp/patch/<nome>. - Criar uma branch candidata limpa a partir do commit upstream explicitamente selecionado para aquela execução.
- Integrar a sobreposição de release downstream, patches regulares e patches de compatibilidade temporária na ordem fixa, usando os mecanismos de mesclagem do repositório ou equivalentes. A branch
cidefine a ordem, e as branches de patch individuais fornecem o conteúdo; a ferramenta de sincronização não precisa entender o conteúdo do patch. - Executar verificações de validação contra o resultado candidato. Se falharem, fazer push de uma branch de release
_brokenou registrar uma tag de release_brokenpara bloquear o resultado quebrado, e não atualizar a branch main ou de release alvo. - Fazer push da branch alvo apenas quando a validação passar. O commit de cada atualização de branch alvo deve ser push com segurança forçada, de modo que o histórico de integração permaneça linear e qualquer trabalho de release posterior comece do resultado anterior aprovado.
- Notificar falhas por meio dos canais do repositório.
Segurança do Fluxo de Sincronização
A branch main ou de release deve ser atualizada apenas pelo processo de sincronização. O push manual deve ser evitado, pois contorna a validação, a ordem de patch e a rastreabilidade. Se surgir uma correção urgente que não possa esperar o próximo ciclo de sincronização, aplique-a primeiro na branch de patch ou ci apropriada, depois acione a sincronização manualmente ou aguarde seu próximo ciclo.
A branch ci e as branches de patch são mantidas diretamente e não são submetidas ao processo de sincronização. A sincronização lê a partir delas e não escreve nelas.
Tratamento de Erros
Quando um problema é encontrado dentro de um patch independente ou da branch ci, corrija-o em sua branch de origem. Não aplique correções no resultado integrado que serão perdidas na próxima reconstrução.
Quando uma integração contínua com o upstream mais recente se tornar impossível, interrompa o processo de sincronização e relate o bloqueador. Documente o upstream ou a dependência afetada, capture o erro e liste as opções para o mantenedor downstream: reverter, corrigir upstream, ajustar o patch ou atualizar a versão do upstream. O mantenedor downstream é quem toma a decisão; o processo de sincronização não silencia automaticamente verificações com falha.
O processo de sincronização faz push de uma branch _broken ou registra uma tag _broken após uma falha, para que o resultado quebrado não seja acessível e o trabalho subsequente não possa começar acidentalmente do commit com falha.