-
Notifications
You must be signed in to change notification settings - Fork 34
Workflow
[ Home ][ Workflow ][ Workflow EN ][ Twgit ][ Twgit EN ]
1. Introduction
2. Liens
3. Proposition
3.1. Nouvel usage
3.2. Erreur sur CI
3.3. Erreur sur release
3.4. Release non viable
3.5. Feature réalisée mais abandonnée
3.6. Dépendance entre features en développement
3.7. Hotfix
3.8. Sous-projets
3.9. Ticket trivial
3.10. Transition
4. Autre proposition de workflow
Ce workflow a pour particularités :
- d'avoir une branche stable recensant toutes les versions qualifiées du code
- de permettre de choisir les features (branches de développement) devant constituer la prochaine release à livrer, et de changer d'avis en cas de problèmes d'intégration
- de faire partir chaque nouvelle branche du dernier point stable pour garantir une base de travail pour le développeur avec le moins de bugs possible
- de permettre une intégration continue en avance de phase
- de rester simple
- de posséder une surcouche Git en Bash pour semi-automatiser les manipulations Git tout en opérant un maximum de contrôles et en remontant des informations pertinentes
Nous souhaitons la présence d'une branche stable (en noir sur le schéma ci-dessous). L'idée est que seules les releases (en bleu sur le schéma) testées et approuvées peuvent être mergées dessus.
Dans cette nouvelle approche chaque feature (ticket Redmine ou sous-projet Redmine, cf. sections Sous-projet et Ticket trivial) donne toujours lieu à la création d'une branche qui lui est propre (feature-1
et feature-2
en orange sur le schéma), branche toujours issue du dernier point stable. Mais une fois les tickets résolus, les branches correspondantes sont systématiquement mergées par le développeur sur la branche d'intégration continue (CI
, en vert sur le schéma, étapes a
et b
).
Des tests unitaires à venir pourront ainsi s'assurer de la qualité et de la compatibilité des features.
Cela doit permettre d'identifier un maximum de problèmes avant l'initialisation d'une nouvelle release et de gagner ainsi un temps précieux.
Après merge sur la branche CI
, les features retenues pour constituer la nouvelle release seront mergées sur cette dernière (étape c
du schéma).
Une fois la release qualifée, elle est taguée et mergée sur la branche stable (étape d
du schéma).
Le workflow est prêt pour une nouvelle itération.
Les cas étudiés plus loin ont pour objectif de proposer des solutions aux problèmes de tous les jours.
Il est important de noter qu'il faut impérativement contenir la divergence entre la branche CI
et la branche stable afin que les tests unitaires reflètent le plus possible les releases potentielles.
CI
peut être vue comme la release contenant toutes les fonctionnalités.
Les cas étudiés par la suite tiennent compte de ce paramètre et dans la pratique seul le cas Feature réalisée mais abandonnée laisse transparaître cette contrainte.
Dans cette approche la branche d'intégration continue est optionnelle : elle n'est pas nécessaire à la mise en place de cette nouvelle approche et pourra arriver dans un second temps... Sa raison d'être est la détection potentielle d'erreurs avant la création de chaque nouvelle release. Dans tous les cas les branches releases et la stable sont sous intégration continue.
Une feature est créée à partir du dernier point stable (étape a
du schéma ci-dessous).
Après développement elle est mergée sur la branche d'intégration continue (étape b
).
Comment réagir si après ce commit une erreur est détectée par le processus d'intégration continue ?
La solution consiste à reprendre le développement de la ou les features incriminées (étape c
), puis de merger à nouveau sur CI
(étape d
).
S'il n'y a plus d'erreur et que la feature est retenue pour la prochaine release, nous pouvons la merger avec la branche idoine (étape e
).
Plusieurs features sont réalisées et mergées sur la branche CI
(étape a
du schéma ci-dessous).
Comme aucune erreur n'a été détectée, les features choisies pour la nouvelle release sont mergées sur cette dernière (étape b
).
Comment réagir si l'on s'aperçoit d'un bug ou d'un problème d'intégration sur celle-ci ?
Deux cas se présentent :
- Si les problèmes sont imputables à une ou quelques features en particulier, alors il est préférable d'apporter les correctifs sur celles-ci (étape
c
), puis de merger à nouveau ces features sur la release en cours : si d'aventure la release s'avère non viable, le travail réalisé sur les features est préservé. - Sinon, corriger à même la release (étape
d
).
Une fois la release qualifée, elle est taguée et mergée sur la branche stable (étape e
du schéma).
Le workflow est prêt pour une nouvelle itération.
Des features créées à partir du dernier point stable sont mergées sur CI
, puis sur la nouvelle branche de release (étape a
du schéma ci-dessous).
Comment réagir s'il apparaît que pour des raisons d'incompatiblité ou d'un manque de maturité des développements (ici la feature F2
) la release s'avère non viable (étape b
) ?
La solution passe par la fermeture de la branche release-1.1.0
(étape b
), puis par la création d'une nouvelle release (étape c
) qui ne contiendra qu'un sous-ensemble des tickets sélectionnés pour la release précédente.
Ici la feature feature-2
est écartée et la feature-1
est mergée à la nouvelle release (étape d
).
Une fois la release qualifée, elle est taguée et mergée à la branche stable
(étape e
du schéma).
La branche feature-2
, qui fait toujours partie de la branche CI
, pourra être corrigée par la suite et embarquée dans la prochaine release.
Que faire pour contenir la divergence entre la branche d'intégration continue et la branche stable
lorsque des features commitées sur cette première sont abandonnées ?
C'est-à-dire qu'elles ne seront jamais incluses dans une release, et donc jamais incluses dans la branche stable
.
Si nous les laissons sur CI
, les futures features devront être compatibles avec des fonctionnalités abandonnées, ce qui n'est pas efficient.
CI
et stable
commenceraient à diverger.
Supposons qu'une feature feature-1
mergée sur CI
(étape a
des 2 schémas ci-dessous) est déclarée abandonnée quelques temps plus tard.
Deux solutions :
- Soit un contre-commit est réalisable, c'est-à-dire qu'il est possible d'annuler les évolutions de la branche en reprennant son développement, auquel cas il suffit de la merger à nouveau sur
CI
(étapeb
du schéma ci-dessous).
- Soit le contre-commit semble trop compliqué, et alors nous allons stopper l'utilisation de cette branche d'intégration continue (étape
b'
du schéma ci-dessous), pour en ouvrir une nouvelle à partir du plus récent point stable (brancheCI2
, étapec
). Toutes les features non encore incluses dans une release (comme icifeature-2
) doivent ensuite être mergées sur cette nouvelle branche (étaped
). On peut se permettre de ne pas le faire pour les features présentes dans l'éventuelle release en cours, car d'une part la release est sous intégration continue, et d'autre part toute feature postérieure à cette release reportera les évolutions surCI
.
Est-ce un problème si 2 features créées à partir du même dernier point stable se révèlent corrélées ?
Par exemple une feature feature-1
fait évoluer une partie du noyau tandis qu'une feature feature-2
est lancée en parallèle pour un ajout de fonctionnalité se reposant sur cette même partie du noyau.
- Le développeur en charge de
feature-2
peut choisir de réaliser son ticket sans se soucier defeature-1
. Le second des 2 à merger surCI
et sur la prochaine release devra résoudre les conflits... - Que
F1
soit finie ou non, le développeurfeature-2
peut avoir l'envie de mergerfeature-1
dans sa propre branche et ainsi pouvoir récupérer les avancées defeature-1
ou effectuer des tests plus représentatifs sur son poste.
Si la dépendance est prévisible et qu'il est possible de ne pas lancer ces 2 features en parallèle, alors ne pas s'en priver.
Que faire si un bug critique passé à travers les tests arrive en prod ?
Dans un premier cas de figure, hors phase de test d'une nouvelle release (le cas a
du schéma ci-dessous), nous décidons de corriger en urgence le bug à l'aide d'un hotfix.
Cela consiste à créer une branche dédiée à la correction de ce problème, pour ensuite la merger sur la branche stable
en créant un nouveau tag, et ceci juste avant (cas b
) ou juste après (cas b'
) avoir mergé le fix sur CI
: ce choix dépend de l'urgence de la situation, un compromis entre rapidité et fiabilité...
Le second cas concerne le besoin de correctif pendant le test d'une release (cas c
).
Alors la branche de fix devra être mergée sur celle de la release en cours, puis sur CI
.
Comme précédemment, le merge sur la branche stable
pourra être effectué avant ou après ces merges selon l'urgence de la situation.
Attention, réaliser un hotfix peut potentiellement porter atteinte à la stabilité de la branche stable
.
Un hotix ne doit concerner que quelques lignes.
La plus grande vigilance doit être de mise, et la prochaine release (ou celle éventuellement en cours) se doit de tester rigoureusement ces fix, hors cas triviaux.
Parfois la notion de sous-projet apparaît. Redmine capture cette notion. Dès lors, au lieu de créer une branche par ticket du sous-projet, nous pouvons choisir de n'en créer qu'une seule au nom du sous-projet en question et d'effectuer les développements associés aux tickets de celui-ci dans cette branche. Cela ne change pas l'approche Git proposée. C'est plus une histoire de convention de nommage de branche.
Idem pour les sous-tâches Redmine.
Faut-il vraiment prendre la peine de traiter les tickets triviaux genre "correction orthographique" de la même façon que les autres ? Ne pourrait-on les gérer comme des hotfixes ?
C'est tentant... Mais l'un des points forts de Git est sa gestion des branches ! En effet sous Git :
- créer une branche est simple et rapide, et l'enchaînement des 2 ou 3 commandes pour en créer une distante et l'importer en local pourrait tout a fait faire l'objet d'un script (si ce n'est déjà fait) ;
- le merge de branches est réputé plus simple et rapide que sous SVN ;
Dans la pratique il n'est pas réellement coûteux de traiter ce genre de ticket de la même façon que les autres.
Le schéma ci-dessous se veut un exemple de transition possible entre une utilisation où tout le monde commit sur la branche master
et celle proposée dans la section Nouvel usage :
- idéalement le basculement interviendrait tout juste après une mise en production (MEP) afin d'être le plus proche possible d'un point stable
- la branche master serait dérivée en la branche stable afin de minimiser les risques de mauvais commit, et l'on réaliserait un tag de cette MEP (étape
b
) - on créerait la branche d'intégration continue (étape
c
) - les éventuelles branches de dév non mergées sur master (comme ici
dev3
) seraient dérivées en features (icifeature-2450
) pour embrasser la nouvelle convention de nommage (étaped
). L'idéal étant d'y merger le nouveau tag pour s'intégrer au mieux dans le nouveau processus.
À la suite de cela, le processus décrit plus haut dans la section Nouvel usage (étape e
) peut prendre le pas.
À titre d'exemple voici typiquement l'arborescence Git d'un projet avant et après transition :
Après transition :
Le site nvie.com propose un autre modèle de développement ici : A successful Git branching model. Voici un schéma extrait de ce lien :
Analyse :
- (+) Pour faciliter l'usage de cette approche, un ensemble de scripts a été développé : git-flow.
- (–) Les branches "feature for future release" peuvent bénéficier de l'intégration continue de manière isolée, mais si l'on souhaite aller plus loin et en bénéficier dans un contexte général où le code de cette feature serait testé avec le code des autres features, alors c'est impossible tant que les releases précédant celles pour lesquelles ces features sont destinées ne sont pas validées, taguées et mergées avec "master" et "develop".
- (+/–) Les branches "feature" et "feature for future release" sont créées à partir de la branche dev : le code servant à leur création est plus récent que celui du dernier tag, mais non garanti stable, ce qui peut ralentir le développement à cause de bugs/instabilités (parfois lourdement car la personne confrontée au bug n'a pas forcément la connaissance du code concerné).
- (+) La convention de nommage des branches contribue à la simplicité du workflow.
- (–) Les branches "features" n'existent que dans les dépôt locaux de dev (je cite : "Feature branches typically exist in developer repos only, not in origin."), tandis que nous nous souhaitons les avoir in fine également dans celui distant pour permettre le choix des tickets pour la prochaine release. Cela explique le fait que les scripts git-flow ne savent pas gérer les features sur le dépôt distant.
- (–) Si une release est non viable (feature immature, merge des features instable), nous sommes obligés de corriger le problème : pas moyen de fermer cette release pour en créer une autre avec un sous-ensemble des features précédemment sélectionnées et laisser la correction pour la release suivante (cf. Release non viable).
- (+) Si le développement d'une feature dure, les développeurs sont incités à merger régulièrement la branche dev dans leur feature.