mantenendo una base di codice crescente e diversificata con integrazione continua


10

Ho bisogno di aiuto con la filosofia e la progettazione di una configurazione di integrazione continua.

La nostra attuale configurazione di CI utilizza buildbot. Quando ho iniziato a progettarlo, ho ereditato (bene, non rigorosamente, dal momento che ero coinvolto nella sua progettazione un anno prima) un costruttore di elementi della configurazione su misura che era stato progettato per eseguire l'intera costruzione in una sola volta, durante la notte. Dopo un po ', abbiamo deciso che questo era insufficiente e abbiamo iniziato a esplorare diversi framework CI, alla fine scegliendo buildbot. Uno dei miei obiettivi nel passaggio a buildbot (oltre a godermi tutti gli extra di whiz bang) era quello di superare alcune delle inadeguatezze del nostro builder notturno su misura.

Umorizzami per un momento e lasciami spiegare cosa ho ereditato. La base di codice per la mia azienda è quasi 150 applicazioni Windows c ++ uniche, ognuna delle quali ha dipendenze da una o più di una dozzina di librerie interne (e molte anche da librerie di terze parti). Alcune di queste librerie sono interdipendenti e hanno applicazioni dipendenti che (mentre non hanno nulla a che fare l'una con l'altra) devono essere costruite con la stessa build di quella libreria. Metà di queste applicazioni e librerie sono considerate "legacy" e non portabili e devono essere costruite con diverse configurazioni distinte del compilatore IBM (per le quali ho scritto sottoclassi uniche Compile), e l'altra metà è costruita con Visual Studio.ShellCommands, poiché non esiste supporto per VSS).

Il nostro costruttore notturno originale ha semplicemente rimosso la fonte di tutto e ha creato cose in un certo ordine. Non c'era modo di creare una sola applicazione, scegliere una revisione o raggruppare le cose. Avrebbe lanciato macchine virtuali per creare un numero di applicazioni. Non era molto robusto, non era distribuibile. Non era terribilmente estensibile. Volevo essere in grado di superare tutte queste limitazioni in buildbot.

Il modo in cui l'ho fatto originariamente era creare voci per ciascuna delle applicazioni che volevamo creare (tutte 150), quindi creare programmatori attivati ​​che potevano creare varie applicazioni come gruppi e quindi suddividere tali gruppi in uno scheduler di build notturno complessivo. Questi potrebbero essere eseguiti su slave dedicati (niente più chicanery di macchine virtuali) e, se volessi, potrei semplicemente aggiungere nuovi schiavi. Ora, se vogliamo fare una build completa fuori programma, è un clic, ma possiamo anche creare solo un'applicazione se lo desideriamo.

Ci sono quattro punti deboli di questo approccio, tuttavia. Uno è la complessa rete di dipendenze dell'albero dei nostri sorgenti. Per semplificare la manutenzione della configurazione, tutti i costruttori sono generati da un dizionario di grandi dimensioni. Le dipendenze vengono recuperate e costruite in modo non eccessivamente robusto (vale a dire, eliminando alcune cose dal mio dizionario di build-target). Il secondo è che ogni build ha tra 15 e 21 passaggi di build, che è difficile sfogliare e guardare nell'interfaccia web, e poiché ci sono circa 150 colonne, impiega un'eternità a caricarsi (pensa da 30 secondi a più minuti). In terzo luogo, non abbiamo più la scoperta automatica degli obiettivi di costruzione (anche se, per quanto uno dei miei colleghi mi si cacci davanti, non vedo cosa ci abbia portato in primo luogo). Finalmente,

Ora, passando a un nuovo sviluppo, stiamo iniziando a usare g ++ e sovversione (non porting del vecchio repository, intendiamoci, solo per le nuove cose). Inoltre, stiamo iniziando a fare più test unitari ("more" potrebbe dare l'immagine sbagliata ... è più simile a qualsiasi altro ) e test di integrazione (usando python). Sto facendo fatica a capire come inserirli nella mia configurazione esistente.

Quindi, dove ho sbagliato filosoficamente qui? Come posso procedere al meglio (con buildbot - è l'unico pezzo del puzzle su cui ho la licenza di lavorare) in modo che la mia configurazione sia effettivamente mantenibile? Come posso affrontare alcune delle debolezze del mio design? Cosa funziona davvero in termini di strategie di CI per basi di codice di grandi dimensioni (forse troppo) complesse?

MODIFICARE:

Pensavo di aver spiegato il mio problema, ma ovviamente non ero abbastanza chiaro. Sto Non alla ricerca di suggerimenti per la modifica piattaforme CI. E ' non sta per accadere, e non otterrà accettato le risposte che suggeriscono che. Quello che voglio sapere è come le altre persone gestiscono codebase complicate usando CI. Ho una dozzina di prodotti quadrati diversi e ho dipendenze sparse nel vento, e sono tutte diverse. QUESTO è ciò che voglio sapere come affrontare.


Anch'io vorrei sapere la risposta a questo. Il nostro ambiente non è complesso come il tuo, ma abbiamo dipendenze di dipendenze di dipendenze (nel caso del progetto di installazione, ha dipendenze profonde quattro strati). Non so se ogni progetto dovrebbe essere un progetto CI o se dovrei semplicemente usare il file .sln di Visual Studio per occuparmene, in modo da non dover ricreare l'albero delle dipendenze per ogni progetto (e progetto futuro).
moswald,

Non essere in grado di basarsi sul tuo computer locale rende la missione del tuo server CI fondamentale per la tua azienda. Potresti voler ripensare questo.
Thorbjørn Ravn Andersen,

Risposte:


3

Anche se non ho affrontato una situazione così grave come la descrivi, ho mantenuto una configurazione CI con decine di componenti, tra i quali ci sono alcune dipendenze "semplici". Spero che il mio approccio possa darti alcuni suggerimenti con cui procedere. Il problema non è sicuramente legato solo alla scelta del server CI, ma anche al processo di compilazione generale e alla struttura del progetto.

Dividerò il problema in 2 parti: Building e CI.

Costruzione

Per "Costruire" intendo il processo di, cambiare il codice sorgente a portata di mano al manufatto finale. La nostra azienda utilizza principalmente Java nello sviluppo e lo strumento di creazione che abbiamo usato è Maven. Probabilmente non sarai in grado di usarlo a causa della natura del tuo progetto, ma ci sono alcuni preziosi concetti in Maven che vale la pena notare:

1) Nel mondo Maven, ogni artefatto (una lib, il vero programma ecc.) Deve essere chiaramente isolato e disaccoppiato. Le dipendenze tra artefatto dovrebbero essere chiare. Dovrei enfatizzare, la dipendenza disordinata, in particolare le dipendenze circolari tra i tuoi manufatti costruiranno un pasticcio nel processo di costruzione.

Ad esempio, ho visto alcuni progetti Java prima, anche se dopo l'intero processo di compilazione ci sono diversi JAR (si può considerare lib / dll in Java) costruiti, sono in realtà interdipendenti. Ad esempio, A.jar sta usando le cose in B.jar e viceversa. Questo tipo di "modularizzazione" è totalmente insignificante. A.jar e B.jar devono sempre essere distribuiti e utilizzati insieme. L'implicazione è, in seguito, se si desidera separarli in progetti diversi (ad esempio per riutilizzarli in altri progetti), non sarà possibile farlo, poiché non è possibile determinare quale progetto, A o B, costruire per primo.

Sì, deve essere adattato alla progettazione del tuo software, ma credo sempre che, anziché spendere tempo per far funzionare un complicato strumento / modello di costruzione in un progetto disordinato, preferirei passare il tempo a riorganizzare la progettazione per utilizzare un modello di edificio semplice.

2) Le dipendenze dovrebbero essere dichiarative. Ci sono molti processi di compilazione che ho visto prima, che includono tutte le librerie di cui ha bisogno localmente nel progetto. Renderà il processo di compilazione estremamente problematico se alcune delle librerie sono in realtà altri artefatti che è necessario costruire.

3) Archiviazione "centralizzata" per gli artefatti per ottenere dipendenze o per distribuire artefatti una volta compilata. Non ha bisogno di essere "centralizzato" per l'intero ufficio (andrà benissimo se lo è), solo una directory locale andrà già bene.

Altre elaborazioni su 2 e 3. Solo per fare un esempio, mi sono imbattuto in un progetto che ha coinvolto 3 progetti indipendenti. Ogni progetto è costruito sulla base del sorgente, più le librerie nella directory lib / nella directory dei sorgenti. Il progetto A costruirà diverse librerie, che a loro volta sono utilizzate dal progetto B e C. Ci sono alcuni svantaggi: la procedura di compilazione è complicata e più difficile da automatizzare; il controllo del codice sorgente si sta gonfiando con inutili JAR duplicati riutilizzati in diversi progetti

Nel mondo di Maven, ciò che viene fatto è che i progetti B e C non contengono realmente A.jar (e altre dipendenze, come altre librerie di terze parti) nella fonte del progetto. È dichiarativo. Ad esempio, nella configurazione build del progetto B, dichiara semplicemente che è necessario: A.lib v1.0, xyz.lib v2.1 ecc. E lo script build cercherà la libreria da /A/1.0/A. jar e /xyz/2.1/xyz.lib

Per il mondo non Maven, questa directory artefatto deve essere solo una o due directory con una struttura di directory concordata. Puoi mettere tutte le librerie di terze parti in un percorso di condivisione e consentire agli sviluppatori di sincronizzarsi o copiare sui loro computer locali. Nei miei progetti C ++ che ho fatto molti anni prima, quello che sto facendo è impostare lib e header su $ {artefact_dir} / lib_name / ver, e con artefact_id dichiarato come variabile d'ambiente.

Quando il progetto A viene creato, avrà una copia del suo risultato in quel artefatto_dir, in modo che quando costruiamo il progetto B, B possa ottenere automaticamente il risultato di A, senza copia manuale.

4) Rilascio non modificabile. Una volta rilasciato A.lib 1.0, il gioco è fatto, non ti aspetti che il contenuto di A.lib 1.0 cambi dopo 1 mese solo perché ci sono alcune correzioni di bug. In tal caso, dovrebbe essere A.lib 1.1. Il manufatto di una base di codice in evoluzione dovrebbe considerare una speciale "versione", per la quale in Maven la chiamiamo istantanea.

Il rilascio non mutabile è più un problema etico. Ma quello che ha risolto è chiaro: quando hai molti progetti, usando la stessa lib, sai quale versione di quella lib stai usando, e sarai sicuro che, la stessa versione di lib usata in diversi progetti, è davvero la stessa . Penso che molti di noi abbiano affrontato il problema di: perché i progetti X e Y utilizzano entrambi lib A ma il risultato della creazione è diverso? (e risulta che la lib A che usa X e Y è in effetti diversa dopo aver scavato nel contenuto o nella dimensione del file della lib).


Tutti questi assicurano che i tuoi progetti possano costruire indipendentemente, senza troppi trucchi manuali come, prima costruisci il progetto A, copia A.lib nel progetto B, quindi costruisci il progetto B ...

Dopo aver svolto un esercizio come questo, ad esempio, quando costruisci il progetto A, proverà a ottenere le dipendenze dalla memoria centralizzata degli artefatti. Nel caso in cui alcune dipendenze (che sono altri progetti della tua azienda, ad esempio il progetto B) non vengono rilevate, ciò che devi fare è ottenere la fonte del progetto B, crearlo (che verrà distribuito allo storage centralizzato in caso di successo) e quindi compilare nuovamente il progetto A.


CI

Con un sistema di compilazione semplice, con chiare dipendenze, CI sarà molto più semplice. Mi aspetto che il tuo server CI soddisfi i seguenti requisiti:

1) Monitorare il controllo del codice sorgente, solo checkout + build in caso di modifiche al codice sorgente

2) In grado di impostare dipendenze tra progetti

Con una chiara dipendenza per i progetti, devi semplicemente impostare i progetti in CI in base ai tuoi progetti reali, impostare la dipendenza del progetto in CI in base alla dipendenza dei tuoi progetti. Il tuo server CI dovrebbe essere impostato per costruire progetti dipendenti prima di costruire qualsiasi progetto (e, naturalmente, la creazione avviene solo se l'origine del progetto è davvero cambiata).

Se tutto va bene, dovresti avere un elemento di configurazione enorme, complesso, ma comunque gestibile (e ancora più importante, una struttura di progetto gestibile)


Mi piace dove sta andando questa risposta, ma potresti approfondire la sezione "costruzione"? Cioè, dare alcuni esempi, spiegare alcuni dei concetti in modo più completo, elaborato.
Nate,

hm ... come quale parte? Provo a modificare il post per fornire maggiori dettagli su ogni parte. Sarà meglio se puoi dirmi su quale parte senti di dover elaborare. A proposito, se stai usando anche Java, dai un'occhiata a maven.apache.org. Maven potrebbe non essere la cosa più semplice da adottare, ma ti costringe a rendere le tue cose pulite e organizzate.
Adrian Shum,

1

Cosa ha funzionato in situazioni simili per me:

  • Astrazione a livello di app spostando alcune parti della build in app di build dedicate (nel nostro caso si tratta di script PHP chiamati dalla build principale). Ciò può ridurre alcune decine di linee di passaggi di costruzione in un unico passaggio.
  • Astrazione a livello di build creando script di sub-build che vengono lanciati dallo script di build principale. Non ho idea se sia rilevante per buildbot (nessuna esperienza con quel prodotto).

Suggerirei di trattare la build stessa come un progetto di sviluppo software. Ciò significa che è necessario modulare la base di codice di compilazione e scrivere alcuni test automatici (ad es. Creare un numero di revisione noto e verificare se produce risultati corretti).


0

Vorrei considerare Jenkins come un server CI, puoi vedere le funzionalità qui:

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

Perché? È facile da installare, ha un'interfaccia meravigliosa, è facile da configurare ed estendere e ci sono MOLTI plugin pronti per quasi tutto:

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

Provaci :)


2
Sento che hai letto il titolo della mia domanda, ma non hai letto il corpo ... Non sto cercando un suggerimento sul prodotto. Ho scelto un prodotto Sto cercando come organizzare una configurazione CI complessa.
Nate,

Nate, ho letto tutti i tuoi post e penso che tu abbia scelto il prodotto sbagliato, ecco perché ho suggerito Jenkins. Uso Jenkins al lavoro per vari tipi di test di prodotti C ++ (anche test scritti in più lingue) con clustering su più macchine e funziona molto bene. È molto facile da amministrare e configurare. Davvero, provalo :)
Patrizio Rullo

Guarda anche questo post sul blog: vperic.blogspot.com/2011/05/…
Patrizio Rullo

0

Ora usiamo Go di ThoughtWorks Prima di questo, stavamo usando CruiseControl.Net . È stato utile considerando che abbiamo due squadre distanti metà del mondo l'una dall'altra e c'è una grande differenza di fuso orario tra di noi.

Stiamo gestendo più progetti e la maggior parte delle attività prevede sovrapposizioni tra due sviluppatori in entrambi i lati del globo, quindi dobbiamo tenere presente che tutti i file non devono interrompere la creazione di qualcun altro.

Inoltre, la gestione è più semplice con Go e essendo un irriducibile processo agile, ci ha dato meno mal di testa dopo averlo installato. Anche i test sono stati integrati con Go.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.