Cinco dicas para repositórios Git de fácil utilização com CI

Sarah Goff-Dupont
Sarah Goff-Dupont
Voltar para a lista

Se você segue a Atlassian, sabe que temos vasta experiência em integração contínua ("IC") e Git, de forma separada, claro: e ainda mais experiência em fazer esses dois trabalharem juntos. Hoje quero compartilhar algumas dicas para fazer com que seu sistema de IC interaja de maneira ideal com seu repositório, que é onde tudo começa.

1: Evite monitorar arquivos grandes em seu repositório

Algo que você escuta com frequência sobre Git é que deve evitar colocar arquivos grandes em seu repositório: binários, arquivos de mídia, artefatos arquivados, etc. Isso porque, após adicionar um arquivo, ele sempre vai estar no histórico do repositório, o que significa que, sempre que o repositório for clonado, esse arquivo enorme e pesado também vai ser clonado. E tirar qualquer arquivo do histórico do repositório é muito complicado: é o mesmo que realizar uma lobotomia em sua base de código. Essa extração cirúrgica do arquivo altera todo o histórico do repositório, fazendo com que você perca a visibilidade de quais alterações foram feitas, e quando. São ótimos motivos para evitar arquivos grandes, como regra geral.

Porém, não utilizar arquivos grandes tem especial importância se você estiver realizando uma IC.

Sempre que você executa um build, seu servidor de CI precisa clonar seu repositório no diretório de build de trabalho. Se o seu repositório estiver repleto de artefatos enormes, ele tornará esse processo lento e aumentará o tempo que seus desenvolvedores precisarão esperar pelos resultados do build.

Ok, tudo bem. Mas e se o seu build depender de binários de outros projetos ou artefatos grandes? Essa é uma situação muito comum, e provavelmente sempre será. Assim, a pergunta é: como podemos lidar com ele de maneira eficaz?

Os sistemas de armazenamento como o Artifactory (que cria um complemento para o Bamboo), o Nexus ou o Archiva podem ajudar no caso dos artefatos gerados pela sua equipe ou pelas equipes ao redor. Os arquivos de que você precisa podem ser incluídos no diretório de builds no início do build – assim como as bibliotecas de terceiros que você obtém via Maven ou Gradle.

Agora você pode estar pensando: “Ah, vou sincronizar meus arquivos grandes com o servidor de build todas as noites, então só preciso fazer a transferência deles para o disco no momento do build”.

Mesmo que uma transferência de disco seja muito mais rápida do que a transferência de rede, não recomendo que seja feita, ainda mais se os artefatos são alterados com frequência. Entre as sincronizações noturnas, a equipe vai acabar desenvolvendo com versões obsoletas dos artefatos. Além disso, os desenvolvedores vão precisar desses arquivos para os builds em suas estações de trabalho locais de qualquer jeito. Então, no geral, o melhor a se fazer é integrar o download de artefatos ao processo de build.

2: Usar clones superficiais para CI

Sempre que um build é executado, seu servidor de build clona o repositório no diretório de trabalho atual. Como mencionei antes, quando o Git clona um repositório, ele clona todo o histórico do repositório por padrão. Então, com o tempo, essa operação vai levar cada vez mais tempo. A menos que seu sistema de IC use clones superficiais.

Com clones superficiais, apenas o instantâneo atual do seu repositório será aberto. Assim, pode ser bastante útil para reduzir tempos de execução de build, especialmente ao trabalhar com repositórios grandes e/ou mais antigos.

Mas digamos que seu build requer o histórico completo do repositório: se, por exemplo, há um build de lançamento que adiciona uma marcação ou atualiza a versão em seu POM, ou você está mesclando duas ramificações com cada build.

As versões anteriores do Git exigem que o histórico completo do repositório esteja presente para enviar as alterações. A partir da versão 1.9, alterações simples nos arquivos podem ser enviadas sem que esse histórico esteja presente. Porém, para o processo de merge, o histórico completo ainda é necessário, pois o Git precisa rever informações para encontrar o ancestral comum das duas ramificações. E essa exigência é problemática quando o build usa clonagem superficial. O que me leva à dica nº 3.

3: Armazenar em cache o repositório em agentes de build

A operação de clonagem também fica muito mais rápida e alguns servidores de IC fazem isso por padrão.

Observe que o armazenamento do repositório em cache o beneficia apenas se você estiver usando agentes que persistam de um build para outro. Se você criar e destruir agentes de build no EC2 ou em outro provedor de nuvem sempre que um build é executado, o armazenamento do repositório em cache não vai fazer diferença, pois você vai trabalhar com um diretório de build vazio e vai sempre precisar obter uma cópia completa do repositório de qualquer maneira.

Clones superficiais mais armazenamento do repositório em cache, divididos pelos agentes elásticos versus persistentes, é igual a uma interessante rede de fatores. A seguir está uma pequena matriz para ajudar você a criar uma estratégia.

Matriz

4: Escolha seus acionadores com sabedoria

É (quase) desnecessário dizer que implementar a integração contínua em todas as suas ramificações ativas é mais do que recomendado. Porém, é bom executar todas os builds em todas as ramificações em relação a todos os commits? É provável que não. Entenda o motivo.

Considere a Atlassian, por exemplo. A gente tem mais de 500 desenvolvedores, todos enviando alterações ao repositório várias vezes ao dia – a maioria envia para as ramificações de recurso. São muitas builds! E, a menos que você dimensione os agentes de build de forma instantânea e infinita, essa imensidão de builds resulta em muita espera na fila.

Um dos nossos servidores internos do Bamboo abriga 935 planos de builds diferentes. A gente conectou 141 agentes de build a esse servidor sem deixar de seguir as práticas recomendadas, como aprovação de artefatos e paralelização de teste, para que todo build seja o mais eficiente possível. E ainda assim: gerar cada commit estava atrasando os trabalhos.

Em vez de simplesmente montar outra instância do Bamboo com outros 100 agentes ou mais, recuamos e perguntamos se isso era realmente necessário. E a resposta foi negativa.

Descobrimos que uma boa maneira de equilibrar o rigor dos testes com a conservação de recursos é criar builds nas ramificações de desenvolvimento com o clicar de um botão. É aqui que a maior parte da atividade de alteração acontece, por isso, é a maior oportunidade de economia. Os desenvolvedores percebem que isso se encaixa com naturalidade em seu fluxo de trabalho e gostam do controle e da flexibilidade extras que isso lhes dá.

Para ramificações críticas, como branches da versão estável e principais, os builds são acionados de modo automático ao pesquisar alterações no repositório. Uma vez que a gente usa ramificações de desenvolvimento para todo o trabalho em andamento, os únicos commits que vêm para o principal devem (em teoria) ser ramificações de desenvolvimento sendo mescladas. Além disso, essas são as linhas de código com base nas quais a gente lança e cria as ramificações de desenvolvimento. Por isso, é muito importante termos resultados de teste em tempo hábil para cada commit.

5: Pare de sondar, comece a capturar

Outra opção é não realizar a pesquisa e fazer com que o repositório chame seu servidor de IC quando uma alteração for enviada e precisar ser criada. Em geral, isso é feito por meio de um hook no seu repositório.

Você pode fazer isso com qualquer ferramenta que tiver, porém, fizemos a adição recente de uma integração entre o Bitbucket Server e o Bamboo que torna essa configuração extra desnecessária. Assim que o Bamboo e Bitbucket Server estiverem vinculados no back-end, o build orientado a repositório aciona o Just Work™ pronto para uso. Não é necessário usar hooks ou configurações especiais.

Independente da ferramenta, os acionadores controlados por repositório têm a vantagem de "hibernarem" de modo automático quando a ramificação de destino fica inativa. Ou seja, chega de desperdiçar os ciclos de CPU do sistema de IC pesquisando centenas de ramificações abandonadas ou de perder tempo com o fechamento manual dos builds das ramificações. (Vale observar que o Bamboo pode ser configurado com facilidade para ignorar as ramificações após X dias de inatividade, caso você ainda queira fazer pesquisas).

Entre em ação

Cada dica dada aqui pode ser implementada em qualquer servidor de IC no mercado. Mas como estamos sempre procurando tornar as práticas recomendadas fáceis de se aplicar, todas elas foram integradas ao Bamboo para que sejam simples de configurar. Acesse o tour e consulte todas as coisas boas.

Pronto(a) para aprender Git?

Tente este tutorial interativo.

Comece agora mesmo