Estou trabalhando em um projeto de código aberto liderado por pesquisas, com muitos outros colaboradores regulares. Como o projeto agora é bastante grande, um consórcio (composto por dois funcionários em período integral e poucos membros) é responsável por manter o projeto, a integração contínua (IC) etc. Eles simplesmente não têm tempo para a integração de recursos externos. contribuições embora.
O projeto é composto de uma estrutura "básica", com cerca de meio milhão de linhas de código, um monte de "plugins" mantidos pelo consórcio e vários plugins externos, a maioria dos quais não somos ' nem mesmo ciente.
Atualmente, nosso IC constrói o núcleo e os plugins mantidos.
Um dos grandes problemas que enfrentamos é que a maioria dos colaboradores (e principalmente os ocasionais) não está construindo 90% dos plug-ins mantidos; portanto, quando propõem mudanças de refatoração no núcleo (o que atualmente acontece regularmente), eles verificaram se o código é compilado na máquina antes de fazer uma solicitação de recebimento no GitHub.
O código funciona, eles estão felizes e, em seguida, o IC termina a construção e os problemas começam: a compilação falhou em um plug-in mantido por consórcio, que o colaborador não construiu em sua máquina.
Esse plug-in pode ter dependências de bibliotecas de terceiros, como CUDA, por exemplo, e o usuário não deseja, não sabe como, ou simplesmente não pode, por razões de hardware, compilar esse plug-in quebrado.
Então, então - ou o PR permanece ad aeternam no limbo de PRs que nunca serão mesclados - Ou o colaborador cumprimenta a variável renomeada na fonte do plugin quebrado, altera o código, pressiona sua ramificação, aguarda por o IC para concluir a compilação, geralmente recebe mais erros e reitera o processo até que o IC esteja satisfeito - ou uma das duas permanentes já contratadas em excesso no consórcio dá uma mãozinha e tenta consertar o PR em sua máquina.
Nenhuma dessas opções é viável, mas simplesmente não sabemos como fazê-lo de maneira diferente. Você já foi confrontado com uma situação semelhante em seus projetos? E se sim, como você lidou com esse problema? Existe uma solução que não estou vendo aqui?
fonte
Respostas:
Desenvolvimento orientado por CI está bom! Isso é muito melhor do que não executar testes e incluir código quebrado! No entanto, existem algumas coisas para tornar isso mais fácil para todos os envolvidos:
Defina expectativas: tenha documentação de contribuição que explique que o IC frequentemente encontra problemas adicionais e que esses problemas precisam ser corrigidos antes da mesclagem. Talvez explique que mudanças locais pequenas são mais propensas a funcionar bem - portanto, pode ser sensato dividir uma grande mudança em vários PRs.
Incentivar testes locais: facilite a configuração de um ambiente de teste para o seu sistema. Um script que verifica se todas as dependências foram instaladas? Um contêiner do Docker pronto para ser usado? Uma imagem de máquina virtual? Seu executor de testes possui mecanismos que permitem priorizar testes mais importantes?
Explique como usar o IC para si: Parte da frustração é que esse feedback só ocorre após o envio de um PR. Se os contribuintes configurarem o IC para seus próprios repositórios, eles receberão comentários anteriores - e produzirão menos notificações de IC para outras pessoas.
Resolva todos os PRs, de qualquer maneira: se algo não puder ser mesclado porque está quebrado e se não houver progresso no sentido de solucionar os problemas, basta fechá-lo. Esses PRs abertos abandonados apenas bagunçam tudo, e qualquer feedback é melhor do que simplesmente ignorar o problema. É possível expressar isso muito bem e deixar claro que é claro que você ficaria feliz em mesclar quando os problemas forem resolvidos. (veja também: A arte do fechamento de Jessie Frazelle , Melhores práticas para mantenedores: aprendendo a dizer não )
Considere também tornar esses PRs abandonados detectáveis, para que alguém possa buscá-los. Isso pode até ser uma boa tarefa para novos colaboradores, se os problemas restantes forem mais mecânicos e não precisarem de profunda familiaridade com o sistema.
Para a perspectiva de longo prazo, essas alterações parecem quebrar a funcionalidade não relacionada com tanta frequência que podem significar que seu design atual é um pouco problemático. Por exemplo, as interfaces do plug-in encapsulam adequadamente os internos do seu núcleo? O C ++ facilita o vazamento acidental de detalhes de implementação, mas também possibilita a criação de abstrações fortes e muito difíceis de usar indevidamente. Você não pode mudar isso durante a noite, mas pode orientar a evolução a longo prazo do software em direção a uma arquitetura menos frágil.
fonte
Construir um modelo de plug-in sustentável requer que sua estrutura principal exponha uma interface estável na qual os plug-ins possam confiar. A regra de ouro é que você pode introduzir novas interfaces ao longo do tempo, mas nunca pode modificar uma interface já publicada. Se você seguir esta regra, poderá refatorar a implementação da estrutura principal tudo o que quiser, sem medo de quebrar acidentalmente os plug-ins, seja um mantido por consórcio ou um externo.
Pelo que você descreveu, parece que você não tem uma interface bem definida e isso dificulta saber se uma alteração interromperá os plugins. Trabalhe para definir essa interface e torná-la explícita na sua base de código, para que os colaboradores saibam o que não devem modificar.
fonte
Para ser honesto, não acho que você possa lidar com isso de uma maneira melhor - se as alterações resultarem na quebra de partes mantidas do seu projeto, o IC falhará.
O seu projeto tem
contributing.md
algo ou algo semelhante para ajudar novos e ocasionais colaboradores a preparar suas contribuições? Você tem uma lista clara, quais plug-ins fazem parte do núcleo e precisam permanecer compatíveis?Se for difícil criar tudo em uma máquina devido a dependências, etc., você pode pensar em criar imagens de janela de encaixe prontas para uso como ambientes de construção para seus colaboradores usarem.
fonte
Então, acho que é aqui que o estilo mais amplo de projetos de código aberto pode cair; a maioria dos projetos organizados centralmente desconfia da refatoração principal, especialmente quando ela cruza um limite da API. Se eles refatoram um limite da API, geralmente é um "big bang", onde todas as alterações são agendadas ao mesmo tempo com um incremento na versão principal da API, e a API antiga é mantida.
Eu proporia uma regra "todas as alterações na API devem ser planejadas com antecedência": se um PR vier fazer uma alteração incompatível com a API, de alguém que não tenha entrado em contato com os mantenedores para concordar com sua abordagem antecipadamente, simplesmente é fechado e o remetente aponta para a regra.
Você também precisará de versão explícita da API do plug-in. Isso permite que você desenvolva a v2 enquanto todos os plugins da v1 continuam a ser construídos e a trabalhar.
Também questionaria um pouco mais por que estão sendo feitas tantas refatorações principais e alterações de API. Eles são realmente necessários ou apenas pessoas impõem seu gosto pessoal ao projeto?
fonte
Parece que o processo de IC precisa ser mais rigoroso, mais abrangente e mais visível para os colaboradores antes que eles aumentem um PR. Como exemplo, o BitBucket possui um recurso de pipelines que permite isso, onde você fornece um arquivo que define no código o processo de criação do IC e, se falhar, a ramificação será impedida de ser mesclada.
Independentemente da tecnologia, o fornecimento automático de compilações quando um colaborador envia para uma filial fornecerá a eles feedback muito mais rápido sobre as dicas a serem observadas ao fazer alterações e levará a PRs que não precisam ser consertados após o fato.
Seria bom corrigir problemas de design, mas são ortogonais a esse problema.
fonte
Sua solução é simples: reduza a barreira à contribuição .
A maneira mais simples de (1) acelerar o ciclo de edição-compilação-teste e (2) diferenças suaves do ambiente é fornecer servidores de construção :
E, em seguida, abra esses servidores de construção para colaboradores. Eles devem poder fazer logon remotamente em uma imagem nova do Docker e editar-compilar-teste remotamente nesta máquina.
Então:
Em geral, os servidores de compilação podem ser compartilhados entre vários colaboradores, no entanto, quando periféricos de hardware especiais estão envolvidos, pode ser necessário que um colaborador use esse periférico sozinho.
Fonte: trabalhando em software usando FPGAs, dado o preço dos animais e a variedade de modelos que precisamos, você não encontra cada modelo de FPGA instalado na máquina de todos os desenvolvedores.
fonte
Se contribuir com o núcleo sem alterar nenhum contrato pode quebrar o software dependente, sugere que:
Qualquer um dos problemas deve ser fácil de resolver, mas você menciona que a equipe principal pode não ter capacidade para fazê-lo. Uma opção seria pedir ajuda à comunidade para resolver o problema.
fonte
Ninguém mais parece ter levantado isso como uma solução potencial.
Ao desenvolver o núcleo, incentive os desenvolvedores a executar esses testes de compatibilidade. Se eles falharem, não faça o check-in.
Isso não garante 100% de compatibilidade, mas irá capturar muito mais problemas desde o início.
Um benefício secundário é que essas gravações podem destacar quais interfaces são usadas ativamente e quais recursos estão sendo usados ativamente.
fonte
Estou tendo problemas para entender a situação como parece: o IC cria apenas uma ramificação?
Existe um motivo para você não criar mais de uma ramificação com o IC?
A solução mais simples para esse problema seria possibilitar a qualquer colaborador executar a criação do IC em sua ramificação de recursos .
Em seguida, você simplesmente exige uma compilação de IC bem-sucedida na ramificação de recursos para que a solicitação de recebimento dessa ramificação seja aceita.
fonte