mantendo uma base de código diversa e crescente com integração contínua


10

Preciso de ajuda com a filosofia e o design de uma instalação de integração contínua.

Nossa configuração atual de IC usa o buildbot. Quando comecei a projetá-lo, herdei (bem, não estritamente, porque eu estava envolvido no design dele um ano antes) um construtor de CI sob medida que foi adaptado para executar toda a compilação de uma só vez, durante a noite. Depois de um tempo, decidimos que isso era insuficiente e começamos a explorar diferentes estruturas de IC, eventualmente escolhendo o buildbot. Um dos meus objetivos na transição para o buildbot (além de aproveitar todos os extras do whiz-bang) era superar algumas das inadequações do nosso construtor noturno sob medida.

Humor-me por um momento, e deixe-me explicar o que eu herdei. A base de código da minha empresa é quase 150 aplicativos Windows c ++ exclusivos, cada um com dependências em uma ou mais de uma dúzia de bibliotecas internas (e muitas também em bibliotecas de terceiros). Algumas dessas bibliotecas são interdependentes e têm aplicativos dependentes que (embora não tenham nada a ver um com o outro) precisam ser construídos com a mesma construção dessa biblioteca. Metade desses aplicativos e bibliotecas são considerados "herdados" e não portáveis ​​e devem ser construídos com várias configurações distintas do compilador IBM (para o qual escrevi subclasses exclusivas de Compile), e a outra metade é criada com o visual studio.ShellCommands, pois não há suporte para o VSS).

Nosso construtor noturno original simplesmente retirou a fonte de tudo e construiu as coisas em uma determinada ordem. Não havia como criar apenas um único aplicativo, escolher uma revisão ou agrupar coisas. Ele lançaria máquinas virtuais para criar vários aplicativos. Não era muito robusto, não era distribuível. Não era terrivelmente extensível. Eu queria ser capaz de superar todas essas limitações no buildbot.

A maneira como eu fiz isso originalmente foi criar entradas para cada um dos aplicativos que desejávamos criar (todos eles são 150), criar agendadores acionados que pudessem criar vários aplicativos como grupos e, em seguida, incluir esses grupos em um agendador noturno geral. Eles poderiam ser executados em escravos dedicados (sem mais trapaceiros de máquinas virtuais) e, se eu quisesse, poderia simplesmente adicionar novos escravos. Agora, se queremos fazer uma compilação completa fora do cronograma, é apenas um clique, mas também podemos criar apenas um aplicativo, se assim o desejarmos.

Existem quatro pontos fracos dessa abordagem, no entanto. Uma é a complexa rede de dependências da nossa árvore de fontes. Para simplificar a manutenção da configuração, todos os construtores são gerados a partir de um dicionário grande. As dependências são recuperadas e construídas de uma maneira não muito robusta (ou seja, interromper certas coisas no meu dicionário de construção de destino). A segunda é que cada compilação possui entre 15 e 21 etapas de compilação, o que é difícil de navegar e observar na interface da web e, como existem cerca de 150 colunas, leva uma eternidade para carregar (pense em 30 segundos a vários minutos). Em terceiro lugar, não temos mais a descoberta automática de alvos de criação (embora, por mais que um dos meus colegas de trabalho me fale sobre isso, não vejo o que isso nos levou). Finalmente,

Agora, mudando para um novo desenvolvimento, estamos começando a usar o g ++ e o subversion (não portando o repositório antigo, lembre-se - apenas para as novidades). Além disso, estamos começando a fazer mais testes de unidade ("mais" pode dar uma imagem errada ... é mais como qualquer outro ) e testes de integração (usando python). Estou tendo dificuldade em descobrir como encaixá-los na minha configuração existente.

Então, onde eu errei filosoficamente aqui? Como posso avançar da melhor maneira (com buildbot - é a única peça do quebra-cabeça em que tenho licença para trabalhar), para que minha configuração seja realmente mantida? Como abordar algumas das fraquezas do meu design? O que realmente funciona em termos de estratégias de IC para grandes bases de códigos (possivelmente super) complexas?

EDITAR:

Pensei ter explicado meu problema, mas obviamente não estava suficientemente claro. Estou não à procura de sugestões para alterar plataformas CI. Isso não vai acontecer, e respostas sugerindo que não serão aceitas. O que eu quero saber é como outras pessoas gerenciam bases de código complicadas usando o CI. Tenho uma dúzia de produtos diferentes ao quadrado , e tenho dependências espalhadas pelo vento, e todas são diferentes. É com isso que quero saber como lidar.


Eu também gostaria de saber a resposta para isso. Nosso ambiente não é tão complexo quanto o seu, mas temos dependências de dependências de dependências (no caso do projeto de instalação, ele tem dependências com quatro camadas de profundidade). Não sei se cada projeto deve ser um projeto de IC ou se devo apenas usar o arquivo .sln do Visual Studio para cuidar dele para que eu não precise recriar a árvore de dependência de cada projeto (e projeto futuro).
moswald

Não poder construir na sua máquina local torna a missão do servidor de CI essencial para os seus negócios. Você pode repensar isso.
Thorbjørn Ravn Andersen

Respostas:


3

Embora eu não tenha enfrentado uma situação tão ruim quanto você descreve, eu tenho mantido uma configuração de IC com dezenas de componentes, entre os quais existem algumas dependências "simples". Espero que minha abordagem possa lhe dar algumas dicas para prosseguir. Definitivamente, o problema não está relacionado apenas à escolha do servidor de IC, mas também ao processo geral de construção e à estrutura do projeto.

Vou dividir o problema em duas partes: Construção e CI.

Construção

Para "Construção", significa o processo de alterar o código fonte disponível para o artefato final. Nossa empresa usa principalmente Java no desenvolvimento, e a ferramenta de construção que usamos é o Maven. Você provavelmente não conseguirá usá-lo devido à natureza do seu projeto, mas há um conceito valioso no Maven que vale a pena notar:

1) No mundo Maven, cada artefato (uma biblioteca, o programa real etc.) precisa ser claramente isolado e dissociado. As dependências entre o artefato devem ser claras. Devo enfatizar que a dependência confusa, especialmente as dependências circulares entre os artefatos de construção, tornarão o processo de criação uma bagunça.

Por exemplo, vi alguns projetos java antes disso, embora, após todo o processo de construção, existam vários JARs (você pode considerá-lo como lib / dll em Java), eles são de fato interdependentes. Assim, A.jar está usando coisas em B.jar e vice-versa. Esse tipo de 'modularização' é totalmente sem sentido. A.jar e B.jar sempre precisam ser implementados e usados ​​juntos. A implicação é que, posteriormente, se você quiser separá-los em projetos diferentes (para reutilização em outros projetos, por exemplo), você não poderá fazê-lo, porque não é possível determinar qual projeto, A ou B, primeiro a ser construído.

Sim, ele precisa ser atendido no design do seu software, mas eu sempre acredito que, em vez de gastar tempo para fazer com que um modelo / ferramentas de construção complicado funcione em um projeto bagunçado, prefiro gastar tempo para reorganizar o design para fazer uso de um modelo de construção simples.

2) As dependências devem ser declarativas. Já vi muitos processos de compilação, que incluem todas as bibliotecas necessárias localmente no projeto. Isso tornará o processo de construção extremamente problemático se algumas das bibliotecas forem de fato outros artefatos que você precisa construir.

3) Armazenamento "centralizado" para os artefatos para obter dependências ou implantar artefatos após a compilação. Ele não precisa ser "centralizado" para todo o escritório (será ótimo se for), apenas um diretório local já estará bom.

Mais detalhes sobre 2 e 3. Apenas para dar um exemplo, me deparei com um projeto que envolvia 3 projetos independentes. Cada projeto é construído com base na fonte, mais as bibliotecas no diretório lib / no diretório de origem. O Projeto A criará várias bibliotecas, que por sua vez são usadas pelos projetos B e C. Existem algumas desvantagens: o procedimento de compilação é complicado e mais difícil de automatizar; o controle de origem está ficando inchado com JARs duplicados desnecessários reutilizados em diferentes projetos

No mundo do Maven, o que é feito é que o projeto B e C realmente não contém A.jar (e outras dependências, como outras bibliotecas de terceiros) na fonte do projeto. É declarativo. Por exemplo, na configuração de construção do projeto B, simplesmente declara que precisa: A.lib v1.0, xyz.lib v2.1 etc, e o script de construção consultará a lib em /A/1.0/A. jar e /xyz/2.1/xyz.lib

Para o mundo que não é do Maven, esse diretório de artefato precisa apenas ser um ou dois diretórios com a estrutura de diretórios acordada. Você pode colocar todas as bibliotecas de terceiros em um local de compartilhamento e permitir que os desenvolvedores sincronizem ou copiem em suas máquinas locais. Nos meus projetos em C ++ que fiz muitos anos antes, o que estou fazendo é definir a lib e o cabeçalho como $ {artifact_dir} / lib_name / ver e com artifact_id declarado como variável de ambiente.

Quando o projeto A é construído, ele terá uma cópia do resultado nesse artifact_dir, para que, quando construímos o projeto B, B possa obter o resultado de A automaticamente, sem copiar manualmente.

4) Libertação não mutável. Depois de lançar o A.lib 1.0, é isso, você não esperará que o conteúdo do A.lib 1.0 seja alterado após 1 mês, apenas bcos, existem algumas correções. Nesse caso, deve ser A.lib 1.1. O artefato de uma base de código alterada deve considerar uma "versão" especial, para a qual no Maven a chamamos de instantâneo.

A liberação não mutável é mais uma questão ética. Mas o que ele resolveu é claro: quando você tem muitos projetos, usando a mesma lib, você sabe qual versão dessa lib você está usando e você terá certeza de que a mesma versão da lib usada em projetos diferentes é realmente a mesma . Acho que muitos de nós enfrentamos o problema de: Por que o projeto X e Y estão usando a lib A, mas o resultado da compilação é diferente? (e acontece que a lib A usada por X e Y é de fato diferente depois de pesquisar o conteúdo ou o tamanho do arquivo da lib).


Tudo isso garante que seus projetos possam ser construídos independentemente, sem muitos truques manuais, como criar o projeto A primeiro, copiar A.lib para o projeto B e, em seguida, criar o projeto B ...

Após realizar um exercício como este, por exemplo, ao criar o projeto A, ele tentará obter as dependências do armazenamento de artefato centralizado. Caso algumas dependências (que são outros projetos da sua empresa, por exemplo, o projeto B) não sejam encontradas, o que você precisa fazer é obter a fonte do projeto B, construí-lo (que será implantado no armazenamento centralizado com êxito) e então crie o projeto A novamente.


CI

Com um sistema de criação fácil, com dependências claras, o IC será muito mais fácil. Espero que seu servidor de IC atenda aos seguintes requisitos:

1) Monitore o controle de origem, somente faça o checkout + build quando houver alterações na fonte

2) Capaz de configurar dependências entre projetos

Com uma dependência clara para projetos, você só precisa configurar projetos no IC de acordo com seus projetos reais, configurar a dependência do projeto de CI de acordo com a dependência dos seus projetos. Seu servidor de IC deve ser configurado para criar projetos dependentes antes de criar qualquer projeto (e, é claro, a construção só acontece se a fonte do projeto realmente tiver sido alterada).

Se tudo der certo, você deve ter um IC enorme, complexo, mas ainda gerenciável (e ainda mais importante, uma estrutura de projeto gerenciável)


Gosto de onde está indo essa resposta, mas você poderia entrar em mais detalhes na seção "construção"? Ou seja, dê alguns exemplos, explique alguns dos conceitos de maneira mais completa e elaborada.
Nate

hm ... gosta de qual parte? Eu tento editar o post para dar mais detalhes de cada parte. Será melhor se você puder me dizer em que parte você sente que precisa ser elaborado. A propósito, se você também estiver usando Java, consulte maven.apache.org. O Maven pode não ser a coisa mais fácil de adotar, mas força você a deixar suas coisas limpas e organizadas.
Adrian Shum

1

O que funcionou em situações semelhantes para mim:

  • Abstração no nível do aplicativo transferindo algumas partes da compilação para aplicativos de compilação dedicados (no nosso caso, esses são scripts PHP chamados da compilação principal). Isso pode reduzir algumas dezenas de linhas de etapas de criação em uma única etapa.
  • Abstração no nível da compilação criando sub-scripts de compilação que são lançados a partir do script de compilação principal. Não faço ideia se é relevante para o buildbot (nenhuma experiência com esse produto).

Eu sugiro que você trate a compilação em si como um projeto de desenvolvimento de software. Isso significa que você precisa modularizar a base de código de compilação e escrever alguns testes automatizados para ela (por exemplo, criar um número de revisão conhecido e verificar se ele produz resultados corretos).


0

Eu consideraria o Jenkins um servidor de IC; você pode ver os recursos aqui:

https://wiki.jenkins-ci.org/display/JENKINS/Meet+Jenkins

Por quê? É fácil de instalar, possui uma interface maravilhosa, é fácil de configurar e estender e existem MUITOS plugins prontos para quase tudo:

https://wiki.jenkins-ci.org/display/JENKINS/Plugins

De uma chance :)


2
Sinto que você leu o título da minha pergunta, mas não leu o corpo ... Não estou procurando uma sugestão de produto. Eu escolhi um produto. Estou procurando como organizar uma configuração complexa de IC.
Nate

Nate, li todas as suas postagens e acho que você escolheu o produto errado, por isso sugeri Jenkins. Eu uso Jenkins no trabalho para vários tipos de teste de produtos C ++ (mesmo testes escritos em vários idiomas) com clustering em várias máquinas e está funcionando muito bem. É muito fácil administrar e configurar. Realmente, experimentá-lo :)
Patrizio Rullo

Veja também este post do blog: vperic.blogspot.com/2011/05/…
Patrizio Rullo

0

Agora usamos o Go by ThoughtWorks antes disso, estávamos usando o CruiseControl.Net . Tem sido útil, considerando que temos duas equipes a metade do mundo distantes uma da outra e há uma grande diferença de fuso horário entre nós.

Estamos gerenciando vários projetos e a maioria das tarefas envolve sobreposições entre dois desenvolvedores de ambos os lados do mundo, portanto, devemos ter em mente que todos os arquivos não devem interromper a construção de outra pessoa.

Além disso, o gerenciamento é mais fácil com o Go e, por ser um obstáculo ao processo ágil, causou menos dores de cabeça após a instalação. Os testes também foram integrados ao Go.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.