Chapitre 2 Contrôle de version avec git
et GitHub: historique de changement, développement collaboratif et intégration continue
Nous nous intéressons ici aux solutions proposées par RStudio et GitHub pour le contrôle de version de projets, l’hébergement de vos projets et l’automatisation d’un certain nombre de vérifications garantissant un partage facile de votre code.
2.1 Principe du contrôle de version
Le principe du contrôle de version est d’enregistrer les changements successifs apportés à des fichiers (notamment des fichiers .R
).
RStudio propose 2 solutions intégrées pour le contrôle de version :
git
svn
(“subversion”)
2.1.1 git
git
est un logiciel de contrôle de version (c’est-à-dire un outils qui va
enregistrer l’histoire des changements successifs de votre code et permettre de
partager ces changements avec d’autres personnes). git
est un logiciel en
ligne de commande, et sa prise en main n’est pas nécessairement intuitive.
git
fonctionne de la façon suivante : sur un serveur distant (par exemple dans le cloud), une version à jour du code est disponible. À tout moment il est possible d’accéder à cette version du code en ligne. Chaque contributeur peut télécharger cette dernière version à jour (dans une action que l’on dénomme pull), avant de l’éditer localement. Une fois ses changements effectués, le contributeur peut alors mettre à jour la version en ligne du code afin que ses changements soient disponibles pour tout le monde (dans une action que l’on dénomme push)
NB : git
a été pensé pour des fichiers légers (comme par exemple des
fichiers texte) et est loin d’être optimisé pour des fichiers trop lourds et/où compressés.
2.1.2 subversion
subversion
est l’autre solution disponible dans RStudio. Elle fonctionne de manière similaire à git, mais avec des fonctionnalités un peu plus réduites
que nous détaillons pas ici (la différence majeure est que tout les contributeurs travaillent simultanément sur la même version du code).
2.2 Utiliser git
localement depuis RStudio
👉 À vous de jouer !
Commencer par activer
git
depuis l’onglet “Git/SVN” de “Project Options” situé dans le menu “Tools” et suivre les instructions. Vous pouvezaussiplutôt utiliserusethis::use_git()
À partir de l’onglet “Git” maintenant apparu à côté de l’onglet “Build”, enregistrer l’état actuel de votre package en réalisant votre premier “commit” :
2a. sélectionner les fichiers à suivre (ne pas sélectionner le fichier .Rproj)
2b. écrire un message informatif (pour vos collaborateurs - ce qui inclut votre futur vous)
2c. cliquer sur “Commit”
Ajouter une ligne “*.Rproj” au fichier “.gitignore” et effectuez un nouveau commit
Visualiser les changements et leur historique à l’aide des outils de visualisation “Diff” et “History” accessible depuis l’onglet “Git”
2.2.1 Bonnes pratiques du commit
Idéalement, chaque commit ne devrait régler qu’un seul problème. Il devrait le régler dans son intégralité (être complet) et ne contenir des changements relatifs qu’uniquement à ce problème (être minimal). Il est alors important d’écrire des messages de commit informatifs (pensez à vos collaborateur, qui incluent votre futur vous). Il faut également être concis, et décrire les raisons des changements plutôt que
les changements eux-mêmes (visibles dans le Diff).
NB : Il est parfois difficile de respecter ces directives à la lettre, et celles-ci ne sont qu’un guide et ne doivent pas vous empêcher d’effectuer des commits réguliers.
Par ailleurs, la tentation d’avoir un historique de changements “propre” et bien ordonné est naturelle, mais se révèle une source de problèmes inutiles. Elle entre en contradiction avec l’objectif de traçabilité du contrôle de version. Le développement de code étant généralement un processus intellectuel complexe et non linéaire, il est normal que l’enregistrement des changements reflète ce cheminement. En pratique, votre futur-vous sera le premier utilisateur de votre historique de changements et la priorité est donc de vous faciliter la tache dans le futur lors de la résolution de bug où l’extension de fonctionnalités.
2.3 GitHub
GitHub est un site internet proposant une solution
d’hébergement de code en ligne, et s’appuyant sur git
. Il existe de nombreux
sites web et services (GitLab, Bitbucket, …) permettant d’héberger du code et
s’appuyant sur git
. GitHub est très populaire dans la communauté des
développeurs , et est relativement facile à utiliser, même pour un
utilisateur novice.
Les avantages d’utiliser GitHub sont :
une interface graphique simple pour suivre l’historique des changements de votre code
la dernière version de développement de votre code est disponible en ligne et vous pouvez la référencer (on peut même référencer un numéro de commit précis pour geler une version spécifique du code)
les utilisateurs disposent d’un canal clair et transparent pour signaler les bugs/difficultés
cela facilite grandement le développement collaboratif
2.3.1 Mettre son package sur GitHub
👉 À vous de jouer !
- Sur le site https://github.com/, se créer un compte GitHub (si vous hésitez, une convention courante est d’utiliser prénomnom comme nom d’utilisateur)
- Vous pouvez ensuite executer
usethis::use_github()
dans la console et vous laisser guider.- Ajouter un fichier “README.Rmd” à votre package afin de disposer d’une belle page d’accueil sur GitHub :
- 3a. dans , executez la commande
usethis::use_readme_rmd()
- 3b. à l’aide de l’outils “Diff” de l’onglet “Git” de RStudio, étudier les changements opérés par la commande précédente
- 3c. éditez le fichier “README.Rmd” créé, puis créer le fichier README.md correspondant en executant
knitr
(cliquer su la pelotte de laine “Knit” en haut à gauche dans Rstudio), avant d’effectuer un 3e commit contenant ces changements- 3d. à ce stade, si vous visitez la page de votre répertoire sur GitHub, votre 3e commit n’apparait pour l’instant pas. Il faut synchroniser le répertoire GitHub en ligne avec votre dossier local. Pour cela, directement depuis RStudio, cliquer sur “Push” depuis l’onglet “Git”. Maintenant, les changements du 3e commit sont visibles en ligne sur GitHub.
2.4 Collaboration pour la production du code
git
et GitHub sont particulièrement efficaces lorsque plusieurs personnes collaborent pour développer un code. En effet, chacun peut effectuer des pull et push successifs pour apporter des changements au code, de manière simultanée et en étant sûr de toujours travailler sur la dernière version. Nous allons voir différents concepts utiles dans le cas d’un tel travail
collaboratif.
👉 À vous de jouer !
En formant des groupes de 2, vous allez chacun ajouter votre binome comme “collaborator” à votre repertoire GitHub à partir de l’onglet “Settings” (sur GitHub).
Quelques instants plus tard le collaborateur ainsi ajouté reçoit un email l’invitant à accepter l’ajout. Cliquer sur le lien et accepter.
Dans RStudio, créer un nouveau projet “Version control” à partir de l’url
https
du répertoire de votre binôme sur GitHub.
2.4.1 Branches
Une des fonctionnalités assez utile de git
est les branches. Cela permet d’opérer des changements importants dans le code sans perturber le fonctionnement actuel. C’est notamment utile pour explorer une piste de développement dont on ne sait pas si elle sera concluante au final.
D’ailleurs, vous utilisez déjà les branches depuis le depuis de cette partie. En effet, la branche par défaut est appelé “main” (ou parfois “master”).
Grâce à ce système de branches, on obtient un arbre des différents commits au cours du temps (où les nœuds correspondent à la séparations des branches).
2.4.2 Merge
Un pull se décompose en 2 actions de la part de git
:
tout d’abord un fetch, qui correspond au téléchargement du code en ligne
suivi d’un merge qui fusionne la version locale avec les changements.
Après avoir conduit un développement expérimental dans une branche, on peut vouloir merger ces changements dans la branche “master” par exemple, après que l’expérience se soit révélée concluante.
Si un les changements concernent des parties distinctes du code, alors le merge peut s’effectuer sans problème. En revanche si les 2 versions à merger comportent des changements depuis leur dernier commit commun qui concerne les mêmes lignes de codes, alors on va rencontrer un (ou des ) conflit(s), qu’il va falloir résoudre.
2.4.3 Les conflits
Prenons l’exemple suivant : le développeur \(D_1\) et le développeur \(D_2\) on tous les 2 pullé la version v0.1 du code à l’instant \(t\) sur leur machine respective. Ils travaillent chacun indépendamment pour apporter des changements au code. Au moment de pusher ses changements, le développeur \(D_2\) reçoit un message d’erreur :
"Sync Error.
Please resolve all conflicted files, commit, then try syncing again."
Chaque fichier à la source source d’un conflit a alors été automatiquement édité comme suit :
<<<<<<< HEAD
code dans votre version local
=======
code en ligne
>>>>>>> remote
Pour résoudre le conflit, il faut alors éditer chaque fichier un à un en choisissant s’il faut conserver la version locale ou bien celle en ligne, avant de pouvoir commiter à nouveau et enfin de pusher vos changements avec succès.
👉 À vous de jouer !
- Modifiez le fichier
README.Rmd
de votre binome, puis commitez votre changement et pushez le.- une fois que votre binôme a modifié votre
README.Rmd
, modifiez à votre tour le fichier à la même ligne, SANS puller les changements de votre binôme au préalable ! Commitez et essayez de pusher ces changements.- Résolvez le conflit.
NB: Dans la vraie vie, on cherche à éviter cette situation et donc on va toujours puller avant de pusher. Ici, nous faisons volontairement le contraire pour donner un exemple et démystifier les conflits.
2.4.4 Fork
L’action fork permet de créer une copie qui vous appartient à partir d’un code disponible. Ainsi le code original ne sera pas impacté par vos changements. Cela revient à créer une branche, et la séparer de l’arbre pour pouvoir en assumer la propriété. On aurait aussi pu appeler cette action une “bouture”…
Cette action est principalement utile dans le cadre des pull requests.
2.4.5 Pull request
Il s’agit du moyen le plus facile de proposer des changements dans un code dont vous n’êtes pas collaborateur. GitHub propose une interface graphique facilitant leur traitement.
👉 À vous de jouer !
Modifiez le
README.Rmd
de votre voisin qui n’est pas votre binôme après avoir forké son package.Proposez votre changement sous la forme d’une pull request depuis l’onglet “Pull requests” sur la page GitHub du répertoire de votre voisin.
Acceptez la pull request de votre voisin sur la page GitHub de votre répertoire, puis faire le merge.
2.4.6 Issues
Pour n’importe quel répertoire GitHub, vous pouvez poster un commentaire sous forme d’issue afin d’alerter les développeurs sur un éventuel bug, ou une question sur l’utilisation du package, ou encore demander une fonctionnalité supplémentaire…
L’idéal est de proposer vous-même une pull request qui résout votre issue lorsque vous le pouvez (i.e. en avez les capacités et le temps).
👉 À vous de jouer !
Utilisez
usethis::use_github_links()
afin d’ajouter les 2 lignes suivantes au fichierDESCRIPTION
de votre packageURL: http://github.com/*prenom.nom*/mypkg
BugReports: http://github.com/*prenom.nom*/mypkg/issues
Visualisez les nouveau changements, puis commitez les.
Créez une issue sur le projet de votre binome
2.5 Intégration continue
À chaque changement, à chaque commit donc, il y a la possibilité d’introduire 1 (ou plusieurs) bugs qui vont empêcher le package de passer le CRAN check. Si l’on accumule trop de ces bugs, au moment de soumettre la nouvelle version, il peut y avoir beaucoup de corrections à apporter. C’est d’autant plus frustrant si le package passait le CRAN check auparavant…
Les services d’intégration continue permettent de checker votre package automatiquement après chaque commit ! En cas d’échec, vous recevez un mail qui vous en informe. Un certain nombre de ces services proposent une offre limitée gratuite pour les projets open-source.
Une autre raison d’utiliser l’intégration continue est qu’elle permet de tester votre package sur des infrastructures différentes de la votre (e.g. Windows, Ubuntu, Mac OS) et pour différentes versions de (current, devel…)
2.5.1 GitHub Actions
Les GitHub Actions permettent de lancer des actions automatiquement à chaque fois que vous pushez sur GitHub La commande usethis::use_github_action("check-standard")
permet d’initialiser les Github Actions, et d’ajouter l’action R CMD CHECK
du package.
👉 À vous de jouer !
Executer la commande
usethis::use_github_action("check-standard")
et commiter les changements et regardez ce qu’il se passe sur la page GitHubAjouter un badge à votre README.md grâce au code obtenu dans la console et commiter ces changements
N’hésitez pas à consulter la page suivantes qui renseigne sur les différentes GitHub Actions disponibles pour les pacakges : https://github.com/r-lib/actions/blob/v2-branch/examples/README.md
2.6 Construire et déployer simplement un site web pour accompagner son package
Le package pkgdown
permet de générer automatiquement un site web attrayant rassemblant la documentation de votre package (y compris les Vignettes).
👉 À vous de jouer !
Executer la commande
usethis::use_pkgdown()
dans la console .Commiter et pusher les changements. Rendez-vous sur l’onglet Actions de votre répertoire sur GitHub et constater les nouveautés.
👉 À vous de jouer !
Examiner les changement du fichier
DESCRIPTION
suite à l’execution des différentes commandesusethis::use_...
précedemment.Modifier le champ URL du fichier
DESCRIPTION
.
2.7 Références additionnelles
Happy Git With R by Jenny Bryan.
Jennifer Bryan (2018). Excuse Me, Do You Have a Moment to Talk about Version Control? The American Statistician 72 (1):20–27.
DOI: 10.1080/00031305.2017.1399928
Annexe 2.1 : R-hub
Le R consortium met à disposition le R-hub builder, – et a pour ambition de pouvoir un jour proposer un service d’intégration continue spécialement dédié aux packages .
R-hub builder utilise exactement la même infrasrtructure que les serveurs du CRAN
, ce qui a l’avantage de pouvoir reproduire exactement le R CMD CHECK
tel qu’effectué par le CRAN
. Il est possible de l’utiliser grâce à la fonction devtools::check_rhub()
.
Annexe 2.2 : couverture du code
Le package covr
propose une solution pour mesurer la couverture des tests unitaires associés à un package. La couverture de test détermine la proportion du code source qui est effectivement utilisée lors de l’exécution des tests unitaires. La mesure de la couverture du code renforce la fiabilité d’un code et donne confiance à ses utilisateurs potentiels.
👉 À vous de jouer !
- Executer la commande
usethis::use_coverage()
, ajouter un joli badge à votre README.md grâce au code obtenu dans la console .
- Commiter ces changements.
Pour plus d’information n’hésitez pas à consulter la vignette de covr
.