La ramificazione interrompe l'integrazione continua?


18

Penso che questo articolo, A Successful Git Branching Model , sia molto noto tra gli utenti DVCS esperti.

Uso hgprincipalmente, ma direi che questa discussione va bene per qualsiasi DVCS.

Il nostro flusso di lavoro attuale prevede che ogni sviluppatore cloni il repository principale. Scriviamo codice sul nostro repository locale, eseguiamo test e, se tutto va bene, spinge al master.

Quindi vogliamo configurare server CI come Jenkins e migliorare il nostro flusso di lavoro con il futuro sistema di provisioning (chef, burattino, ansible, ecc.).

Parte reale

Bene, il modello presentato sopra funziona bene ma i rami possono rompere CI. Il ramo delle caratteristiche dovrebbe sincronizzarsi con l'origine (secondo l'articolo, sarebbe il developmentramo) per rendere CI e l'unione fluidi, giusto?

Supponiamo che Alice e Bob stiano lavorando a due funzioni. Ma Alice ha finito il giorno successivo. La funzionalità di Bob richiede una settimana. Quando Bob ha terminato, le sue modifiche sono obsolete (forse Alice ha riformattato / rinominato alcune classi).

Una soluzione è ogni mattina che gli sviluppatori devono tirare master/originper verificare se ci sono cambiamenti. Se Alice si impegna, Bob dovrebbe estrarre e fondersi nel suo spazio di lavoro in modo che il suo ramo di funzionalità sia aggiornato.

  1. È un buon modo?
  2. Questi rami dovrebbero esistere nel repository principale (non clone locale?) Significato ogni sviluppatore dovrebbe avere il privilegio di eseguire il repository principale su GitHub / Bitbucket in modo da poter creare un nuovo ramo? O questo è fatto localmente?
  3. Infine, il modello presentato dall'articolo dovrebbe interrompere CI se i rami non sono sincronizzati con origin/master. Dal momento che vogliamo creare build notturne, gli sviluppatori dovrebbero estrarre e unire prima di lasciare il lavoro e avere anche CI in esecuzione su ciascun ramo delle funzionalità?

Risposte:


12

Innanzitutto, l'uso dei rami delle caratteristiche (per isolare il lavoro svolto su una caratteristica) e CI (per trovare problemi di integrazione non appena vengono commessi) sono leggermente in contrasto.

A mio avviso, l'esecuzione di CI su rami di funzioni è una perdita di tempo. Man mano che i rami delle funzioni vanno e vengono frequentemente, gli strumenti di configurazione integrata dovrebbero essere riconfigurati più volte. E quello per un ramo che molto probabilmente ottiene solo aggiornamenti da una o due fonti che coordinano i loro check-in per evitare i problemi che un sistema CI deve rilevare.
Pertanto, non ha senso utilizzare i rami delle funzionalità sul server del repository principale.

Per quanto riguarda le domande 1 e 3: è responsabilità dello sviluppatore assicurarsi che il build sul ramo di sviluppo principale non si interrompa quando uniscono il ramo di funzionalità in esso. Il modo in cui lo fanno è il loro problema, ma due modi possibili sono:

  • Inserisce le modifiche apportate al ramo di sviluppo principale nel ramo di funzionalità su base regolare (ad es. Quotidianamente)
  • Al termine della funzionalità, unire il ramo di sviluppo principale nel ramo di funzionalità e spingere il risultato di unione sul ramo di sviluppo principale.

In entrambi i casi, gli ovvi problemi di integrazione (ad esempio classi / file rinominati) vengono rilevati e risolti per primi nel ramo delle funzionalità. I problemi più sottili si trovano probabilmente solo quando viene eseguita la build notturna e dovrebbero essere risolti lì e poi.


Sono d'accordo che l'uso dei rami delle caratteristiche sia (leggermente) in contrasto con il concetto di elemento della configurazione. Tuttavia, è possibile creare un sistema CI che non richiede la riconfigurazione per essere eseguito sui rami delle caratteristiche. (L'ho fatto in passato con alcuni semplici script Python), e può essere utile quando i tuoi rami "feature" vengono effettivamente utilizzati come rami di stabilizzazione del rilascio, dove CI è assolutamente necessario.
William Payne,

1
In realtà, penso che abbiamo bisogno di due rami "centrali" - uno come un ramo "throwaway_integration" che esiste puramente come un rapido controllo di unione e test per funzionalità attivamente in fase di sviluppo, e un altro ramo "master" o "stabile" che contiene funzionalità dopo che hanno raggiunto un certo livello di stabilità / maturità. Gli sviluppatori estraggono il codice stabile su cui lavorare dal secondo ramo "stabile" / "principale" e uniscono e spingono le modifiche frequentemente al primo ramo "instabile" / "lancio_integrazione". I test CI dovrebbero essere eseguiti su entrambi i rami, ovviamente.
William Payne,

@WilliamPayne: credo che un ramo così "instabile" sia spesso chiamato "sviluppo"
Bart van Ingen Schenau

5

In risposta a 1)

Qualsiasi modo di funzionare è un buon modo. Tuttavia : l'intera premessa dell'integrazione continua è quella di integrarsi continuamente . L'idea è quella di catturare i bug di integrazione non solo il più presto possibile, ma all'interno del ciclo di feedback sullo sviluppo, vale a dire mentre tutti i dettagli per il codice sotto test sono nella memoria a breve termine dello sviluppatore che apporta le modifiche. Ciò significa che, in circostanze normali e quotidiane, il lavoro deve essere integrato tra i rami delle caratteristiche di ogni commit, forse una volta ogni 15 minuti circa. Per ribadire: lo scopo principale dell'integrazione continua è quello di esporre i bug di integrazione mentre tutti i dettagli sono nella memoria a breve termine degli sviluppatori che apportano le modifiche.

2)

Principalmente, le filiali vengono create in Mercurial clonando i repository, quindi non è necessario assegnare a tutti gli sviluppatori i privilegi di commit per il repository principale. Probabilmente, tuttavia, si desidera dare agli sviluppatori la possibilità di creare repository clonati sul server di integrazione continua, poiché non è sempre possibile eseguire test localmente. (Una volta avevo un sistema CI in cui i test unitari richiedevano 8 ore per essere eseguiti su un cluster 128 core) - Inutile dire che gli sviluppatori non potevano, non potevano eseguire test localmente.

3)

Se disponi delle risorse di calcolo per questo, sì, gli sviluppatori dovrebbero essere sempre aggiornati con la principale linea di sviluppo, in particolare prima di lasciare il lavoro, e dovresti eseguire test notturni su tutte le linee di sviluppo (sebbene la maggior parte dei sistemi di CI non renderlo facile).


1
"Principalmente, i rami sono creati in Mercurial clonando repository" - questo potrebbe essere vero nel 2013, ma oggigiorno i segnalibri Mercurial sono funzionalmente equivalenti ai rami Git in tutti tranne che nel nome.
Kevin,

@Kevin: Molto probabilmente hai ragione. Uso git (quasi) esclusivamente dal febbraio '13 - circa un mese dopo aver scritto la risposta di cui sopra ... quindi non sono particolarmente aggiornato su quali cambiamenti sono avvenuti da Mercurial da allora.
William Payne,

1

Ecco come puoi farlo: branching delle funzionalità.

  1. Per qualsiasi nuova attività (correzione di bug, funzionalità, ecc.) Avviare un nuovo ramo (ad esempio bugfix-ticket123-the_thingie_breaks)
  2. Mentre lavori, aggiorna e unisci continuamente i valori predefiniti (o master in git) nel tuo ramo delle caratteristiche . Questo ti aiuta ad avere il tuo ramo aggiornato senza dover lavorare nel ramo predefinito
  3. Quando la funzione è pronta e i test unitari superano , quindi estrarre e unire nuovamente l'impostazione predefinita nel ramo, chiudere il ramo e spingerlo senza unire , l'integratore noterà il nuovo capo e che è un ramo chiuso, quindi lui / lei si occuperà di integrarlo. Se non si dispone di un integratore, passare al valore predefinito e unire il ramo della funzione al valore predefinito .

La cosa importante qui è che avrai 0 conflitti nel ramo predefinito quando unirai il ramo della caratteristica in esso e tutti i test verranno superati .

Con questo semplice flusso di lavoro avrai sempre un ramo predefinito incontaminato e stabile, ora fai lo stesso per i rami di rilascio, ma si integra di default . Se è necessario integrare gli aggiornamenti rapidi direttamente nei rami di rilascio, è possibile farlo saltando il ramo predefinito, ma ancora una volta, selezionando solo i rami che si sono appena aggiornati dal ramo di destinazione e non presentano conflitti e i relativi test unitari superano.


Mescoli e sostituisci cose piuttosto ortogonali. 0 unisci conflitto! = 0 unità-test difettosa, unione riuscita! = Codice riuscito
Lazy Badger

Aggiunti alcuni chiarimenti,
ho
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.