Ripristino automatico di commit che non riescono a creare


44

Un mio collega mi ha detto che sta pensando di ripristinare il commit del nostro server CI che ha fallito la build, quindi HEADin masterè sempre stabile (come almeno nel passare la build).

È una buona pratica o può essere più problematico che lasciarlo masterrotto finché lo sviluppatore non lo risolve?

Il mio pensiero è che il ripristino del commit renderà più complesso il compito di leggere il commit e la correzione (lo sviluppatore dovrà ripristinare il ripristino e quindi eseguire il commit della correzione, che ingombrerà anche il git log) e dovremmo semplicemente lasciare il commit e quindi eseguire il commit fix. Anche se vedo alcuni vantaggi nell'avere masterstabile, questo ripristino dei fallimenti non mi convince.

modifica: non importa se lo è mastero qualsiasi altro ramo di sviluppo, ma la domanda rimane la stessa: il sistema CI dovrebbe ripristinare un commit che ha fallito la compilazione?

un'altra modifica (prolungata): Ok, stiamo usando gitin un modo strano. Riteniamo che il concetto di filiali vada contro la vera CI, perché impegnarsi in una filiale ti isola dagli altri sviluppatori e dai loro cambiamenti e aggiunge tempo quando devi reintegrare la tua filiale e affrontare possibili conflitti. Se tutti si impegnano in masterquesto conflitto si riducono al minimo e ogni commit supera tutti i test.

Naturalmente, questo ti costringe a spingere solo stabile (o interrompi la compilazione) e programma più attentamente per non rompere la compatibilità all'indietro o attivare / disattivare le funzionalità quando si introducono nuove funzionalità.

Ci sono compromessi quando si eseguono operazioni di configurazione in questo modo o in quel modo, ma questo non rientra nel campo di applicazione della domanda (vedere la domanda correlata per questo). Se preferisci, potrei riformulare la domanda: un piccolo team di sviluppatori lavora insieme in un ramo di funzionalità. Se uno sviluppatore commette qualcosa che interrompe la generazione per quel ramo, il sistema CI dovrebbe ripristinare il commit o no?


38
Le build non riuscite non avrebbero mai dovuto masteriniziare. Ecco a cosa servono i rami di sviluppo e funzionalità. Tali modifiche vanno quindi in qualcosa come un ramo di integrazione in cui è possibile verificare se tutte le nuove funzionalità di diversi sviluppatori lavoreranno insieme e solo se questo è testato può diventare master. O almeno questo è un possibile flusso di lavoro.
Thorsten Müller,

1
@ thorstenmüller ben immagina quindi che è in un ramo di sviluppo che usano tutti gli sviluppatori. Il sistema CI dovrebbe ripristinare i commit che non riescono a costruire?
Carlos Campderrós,

7
Sembra che tu stia usando git in un modo strano. In generale, le persone dovrebbero lavorare sui propri repository nelle proprie filiali e fare clic su Main solo una volta che CI per la loro build personale ha verificato che le modifiche sono OK.
Wilbert,

4
> "questi conflitti sono ridotti al minimo"; ottieni meno conflitti al momento della fusione, ma ottieni molto di più problemi con le cattive fusioni. La soluzione è quella di unire continuamente dal master al tuo ramo come parte del tuo processo, non al ramo.
Deworde,

2
... Perché non farlo in modo che le build non riuscite non vengano accettate nel master per cominciare?
user253751

Risposte:


56

Sarei contrario a farlo per i seguenti motivi:

  • Ogni volta che imposti uno strumento automatizzato per modificare il codice per tuo conto , c'è il rischio che lo sbagli o che si verifichi una situazione in cui ne hai bisogno per interrompere la modifica (ad esempio l'ultima versione di Google Mock conteneva un bug, quindi non è un errore del codice) e devi perdere tempo a riconfigurarlo. Inoltre, c'è sempre un leggero rischio che la compilazione fallisca a causa di un bug nel sistema di compilazione, piuttosto che di un bug nel tuo codice. Per me, CI riguarda la sicurezza che il mio codice sia corretto; questo non farebbe che trasformarlo in un'altra fonte di potenziali problemi di cui potrei preoccuparmi.

  • I tipi di bug che interrompono "la build" dovrebbero essere errori sciocchi che richiedono pochissimo tempo per essere risolti (come hai indicato in un commento, questo è vero per te). Se i bug più sottili e complicati vengono regolarmente inseriti nel master, la soluzione corretta non è quella di "correggerlo più velocemente", è meglio prestare attenzione quando si esaminano i rami delle funzionalità prima che vengano uniti.

  • Lasciare il maestro inaffidabile per alcuni minuti mentre il bug viene corretto correttamente non fa male a nessuno. Non è come se il CEO controllasse personalmente il master e pubblicasse il codice direttamente sui clienti in qualsiasi momento casuale (almeno, si spera non senza il tuo coinvolgimento). Nel caso altamente improbabile che è necessario rilasciare qualcosa prima che sia possibile risolvere il bug, allora si può facilmente prendere la decisione di ripristinare manualmente prima della pubblicazione.


1
Inoltre, solo le build di successo dovrebbero innescare la creazione di un "drop di build" che potrebbe essere distribuito. Se una compilazione fallisce, non dovrebbe esserci alcun calo di compilazione distribuibile, quindi non ci dovrebbe essere il rischio che qualcuno pubblichi codice errato per i client.
Mark Freedman,

1
c'è sicuramente un'altra opzione che è usata e migliore di questa penso e in uso intenso (la usiamo su Twitter). non mettere build fallite su errori master e sciocchi sono facili da correggere anche ancora. Vedi la mia risposta completa di seguito.
Dean Hiller,

26

Concordiamo prima i termini.

Uso personalmente i termini Build continuo e Integrazione continua per distinguere due diversi scenari:

  • Build continuo: uno strumento che controlla periodicamente se il repository è cambiato dall'ultima build e build / test se lo ha fatto.
  • Integrazione continua: uno strumento che accetta le richieste pull e le convalida rispetto all'ultima testa prima di renderle visibili.

Quest'ultima, Integrazione continua, significa che il repository che protegge è sempre verde 1 : è strettamente migliore.

La tua domanda ha davvero senso solo per la generazione continua, quindi risponderò supponendo che questa sia la tua configurazione.

1 : Le cause ambientali possono anche rovinare una build, ad esempio un test con un anno hardcoded (2015) potrebbe iniziare a fallire gennaio 2016, un disco può riempirsi, ... E ovviamente c'è la piaga dell'instabile test. Qui ignoro altezzosamente quelle questioni; altrimenti non arriveremo mai da nessuna parte.


Se si dispone di una configurazione di generazione continua, è possibile automatizzare l'inversione dei commit che potrebbero aver interrotto la creazione, tuttavia esistono diverse sottigliezze.

  • Non puoi effettivamente eliminare i commit: altri colleghi potrebbero averli già controllati e li respingeranno la prossima volta che tenteranno di impegnarsi. Invece, un'inversione dovrebbe commettere una differenza inversa . Oh, e i colleghi ti odieranno per aver ripristinato il loro lavoro quando era corretto in quanto dovranno trovare un modo per respingerlo di nuovo ...
  • In realtà non puoi solo eliminare l'ultimo commit (è una fusione), ma devi eliminare tutti i commit ... fino a un certo punto. Ad esempio, l'ultimo commit valido noto (fare attenzione durante l'avvio del sistema).
  • È necessario pensare a cause esterne (problemi ambientali) ed evitare un'installazione che ripristini tutto al giorno 0. Per fortuna, il ripristino dell'ultimo commit noto noto elimina questo problema.
  • È necessario pensare che l'ultima build nota potrebbe non essere più build (problemi di ambiente), nel qual caso è probabile che tutti gli ulteriori commit vengano annullati. Idealmente, in caso di errore, e prima di ripristinare, è necessario verificare l'ultimo build noto e testarlo nuovamente. Se passa, ripristina, altrimenti, genera un avviso.

Si noti che con questo sistema, in caso di test instabili o di un collega che spesso commette schifezze, molti buoni impegni verranno annullati. I tuoi colleghi ti odieranno.


Spero che la mia storia dell'orrore abbia messo in luce i problemi di consentire un repository non funzionante e ora implementerete una corretta pipeline di integrazione continua in cui le PR non vengono mai inviate direttamente al repository ma invece messe in coda per la fusione in una coda di lavoro e integrate una alla volta ( o dai roll-up):

  • recupera il capo del repository localmente
  • applica richiesta pull
  • costruire e testare
  • in caso di successo, spingere nel repository, altrimenti contrassegnare come non riuscito
  • passa alle richieste successive

Dopo aver provato entrambi, questo è strettamente migliore.


2
Questa è davvero la risposta giusta: la soluzione giusta è impedire che cambiamenti brutti non raggiungano mai il ramo principale, non lasciarli atterrare e quindi dover affrontare il loro ritorno.
Daniel Pryden,

Penso che il problema qui sia che l'interrogante ritiene che i costi dichiarati di "isolarti dagli altri sviluppatori e le loro modifiche" siano superiori ai vantaggi. I costi dichiarati sono il crescente pericolo di fusioni non banali e più le due persone divergono. Il vantaggio dell'IMO di essere isolato dal codice non funzionante è evidente. L'interrogante vuole adottare una strategia "ottimista", in cui il codice non funzionante è brevemente disponibile masterper essere estratto, e quindi risolvere questa situazione una volta falliti i test. Tutti gli altri adottano una strategia "pessimistica" come da loro consigliato e rendono disponibile solo il passaggio del codice.
Steve Jessop,

(dove per "disponibile per il pull" intendo "pull from master", che idealmente è qualcosa che gli sviluppatori possono fare volenti o nolenti, ma per raggiungere questo obiettivo devi ritardare gli impegni in arrivo masterprima che vengano testati e superati. Se uno sviluppatore vuole cherry-pick codice non testato o testato e fallito va bene lo stesso, e il codice è "disponibile" in questo senso, non è proprio quello a cui mi riferisco)
Steve Jessop,

La soluzione giusta è impedire che cambiamenti non possano raggiungere QUALSIASI ramo. I commit non riusciti non dovrebbero mai essere resi pubblici, mai.
Miles Rout,

5

È una buona pratica o può essere più problematica che lasciare il master rotto finché lo sviluppatore non lo risolve?

È problematico. Una persona che decide "il HEAD principale è rotto; ripristinerò la modifica principale" è completamente diverso dal sistema CI che fa lo stesso.

Ecco alcuni svantaggi:

  • Errori nel processo di inversione automatica rovinano il repository;

  • questo presuppone che un singolo changeset (il più in alto) abbia rovinato la build (che non è realistico)

  • I manutentori avranno più lavoro da fare per risolvere il problema, piuttosto che indagare e impegnarsi (dovranno anche guardare alla cronologia inversa)

Riteniamo che il concetto di filiali vada contro la vera CI, perché impegnarsi in una filiale ti isola dagli altri sviluppatori e dai loro cambiamenti e aggiunge tempo quando devi reintegrare la tua filiale e affrontare possibili conflitti.

Questa convinzione (rami vs. CI) non è corretta. Prendi in considerazione di mantenere un ramo stabile, in cui esegui il commit solo dei changeset testati dall'unità . Il resto (rami delle funzioni e rami locali) dovrebbe essere la responsabilità di ogni sviluppatore e non far parte in alcun modo della politica dell'IC.

Nei rami delle funzionalità vuoi essere isolato da altri sviluppatori. Questo ti permette di:

  • eseguire la codifica esplorativa

  • sperimentare con la base di codice

  • eseguire commit parziali (effettivamente impegnare il codice non funzionante) per impostare punti di backup (nel caso in cui si rovini), per creare una cronologia delle modifiche più significativa (tramite messaggi di commit) e per eseguire il backup del lavoro e passare completamente a qualcos'altro (in il tempo impiegato per scrivere "git commit && git checkout")

  • eseguire attività a bassa priorità che richiedono molto tempo (ad esempio, si desidera eseguire un refactoring che altera tutte le 80 classi del livello dati: si cambiano due al giorno, fino a quando non si cambiano tutte insieme al codice compilato (ma è possibile farlo senza influenzare nessuno fino a quando non puoi effettuare un singolo commit).

Se uno sviluppatore commette qualcosa che interrompe la generazione per quel ramo, il sistema CI dovrebbe ripristinare il commit o no?

Non dovrebbe. Commettere un codice stabile sul proprio ramo CI è una responsabilità del committer, non di un sistema automatizzato.


2

Suggerirei di utilizzare un ambiente Gerrit + Jenkins per mantenere sempre in forma il tuo ramo principale. Le persone inviano il loro nuovo codice a Gerrit che avvia un lavoro Jenkins per estrarre quella patch, build, test e così via. Se altri sviluppatori come la tua patch e Jenkins completano il suo lavoro con successo, Gerrit unirà quel pezzo di codice al tuo ramo principale.

È un ambiente simile descritto da @ brian-vandenberg

Oltre a mantenere in buone condizioni la tua filiale, aggiungi anche una fase di revisione del codice che migliora la qualità del codice e la condivisione delle conoscenze sul tuo software.

[1] Jenkins https://jenkins-ci.org/

[2] Gerrit https://www.gerritcodereview.com/


1

L'IC non dovrebbe mai modificare la cronologia di commit del repository.

La soluzione corretta qui è quella di non aggiungere commit al ramo principale se non sono stati testati e verificati.

Lavori sui rami delle caratteristiche, fai eseguire automaticamente l'elemento della configurazione su quelli e, se le build falliscono, non unirle in master.

È possibile disporre di una build aggiuntiva che testa le fusioni se ciò è un problema, eseguendo il ramo della funzione e durante la generazione fondendo master / integrazione / qualunque cosa nel ramo locale, quindi eseguendo i test.


1
Questo non risponde in alcun modo alla domanda. Se la generazione non riesce in un ramo di funzionalità, l'IC dovrebbe ripristinare il commit?
Carlos Campderrós,

Cosa succede se la compilazione ha esito positivo sul ramo della funzione, ma non riesce dopo l'unione?
Matthieu M.,

@MatthieuM. l'unione è un commit, il passaggio CI che unisce unisce la build?
Carlos Campderrós,

@ CarlosCampderrós: ​​personalmente non avrei mai avuto una configurazione che tenti di ripristinare i commit; troppo complicato.
Matthieu M.,

Ho affrontato i commenti.
Daenyth,

1

Usiamo Jenkins per il nostro server di build e utilizziamo il modello gatekeeper per eseguire il push dei commit - in cui una combinazione di Jenkins e trigger di commit (che assicurano che i revisori dei peer abbiano fatto il loro lavoro) è il gatekeeper.

I commit vengono spinti indirettamente attraverso un ricciolo verso Jenkins, dove clona il repository principale, quindi inserisce i commit da unire ed esegue tutte le build richieste (per Linux / solaris). Se tutte le build sono complete, viene eseguito il commit.

Questo evita molti, se non tutti, i problemi discussi finora:

  • alterazione della storia
  • ottenere la cronologia corretta se sei lo sviluppatore che deve riparare la rottura
  • l'instabilità (sotto forma di build rotte) non viene mai introdotta

Ci consente inoltre di applicare direttamente altri requisiti come il completamento di test unitari.


ri: il downvote, ti dispiacerebbe commentare ciò che non ti è piaciuto della mia risposta?
Brian Vandenberg,

0

Quante volte hai ricevuto quell'e-mail automatica che diceva che il tuo ultimo commit ha rotto la build? Quante volte è sbagliato? Ma ora devi andare a vedere se sei stato davvero tu, o qualcun altro che ha fatto un altro impegno nello stesso momento. O forse era qualcosa di ambientale.

Se il sistema non lo sa per certo, allora sicuramente non voglio automatizzarlo.


0

La domanda posta è difettosa. Rispetto questa affermazione però

"Riteniamo che il concetto di filiali vada contro la vera CI, perché impegnarsi in una filiale ti isola dagli altri sviluppatori e dai loro cambiamenti"

Quello che dovresti fare sono questi passaggi

  • se vuoi master se vuoi (va bene e continua a tirare le modifiche da tutti) MA NON impegnarti a master localmente
  • SOLO PRIMA che eseguirai il commit delle modifiche al master, crea una succursale con submit_XXXXXX
  • fai in modo che la tua build automatizzata raccolga tutte le filiali di submit_XXX costruite
  • Opzione 1: costruire interruzioni o unire interruzioni ... modifica rifiutata e non arriva mai al master
  • Opzione 2: costruire opere, jenkins spinge master e lo aggiorna

POI, ciò che facciamo è mettere un git commit hook per impedire a TUTTI di impegnarsi veramente a padroneggiare. Funziona alla grande ... NESSUNA build rotta mai e NESSUN ripristino ripristinante da parte del master.

più tardi, Dean

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.