6 Intégration continue

L’intégration continue consiste à confier à un service externe la tâche de vérifier un package, produire des documents Markdown pour les pages web d’un dépôt GitHub ou tricoter entièrement un site web à partir du code.

Toutes ces tâches peuvent être accomplies localement sur le poste de travail mais prennent du temps et risquent de ne pas être répétées à chaque mise à jour. Dans le cadre de l’intégration continue, elles le sont systématiquement, de façon transparente pour l’utilisateur. En cas d’échec, un message d’alerte est envoyé.

La mise en place de l’intégration continue se justifie pour des projets lourds, avec des mises à jour régulières. plutôt que pour des projets contenant un simple document Markdown rarement modifié.

6.1 Outils

6.1.1 GitHub Actions

L’outil utilisé le plus fréquemment pour des projets R déposés sur GitHub était Travis CI110 mais le service est devenu payant en 2021.

Les Actions GitHub remplacent avantageusement Travis. Ce service est intégré à GitHub.

6.1.2 Codecov

Pour évaluer le taux de couverture du code des packages R, c’est-à-dire la proportion du code testé d’une façon ou d’une autre (exemples, tests unitaires, vignette), le service Codecov111 s’intègre parfaitement à GitHub.

Il faut ouvrir un compte, de préférence en s’authentifiant par GitHub.

6.1.3 GitHub Pages

Les pages web de GitHub peuvent être hébergées dans le répertoire docs de la branche master du projet : c’est la solution retenue quand elle sont produites sur le poste de travail.

Si elles sont produites par intégration continue, elle le seront obligatoirement dans une branche dédiée appelée gh-pages.

6.2 Principes

Un projet de document est traité en exemple. L’objectif est de faire tricoter par GitHub un projet Markdown. Cette pratique est appropriée pour les projets d’ouvrages, qui nécessitent beaucoup de ressources pour leur construction. Dans ce type de projet, le code est tricoté par knitr pour produire plusieurs documents, typiquement aux formats HTML et PDF, accessibles sur les pages GitHub. Quand les documents sont produits localement, ils sont placés dans le dossier docs et poussés sur GitHub.

Pour que GitHub s’en charge, quelques réglages sont nécessaires.

6.2.1 Obtention d’un jeton d’accès personnel

Pour écrire sur GitHub, le service d’intégration continue devra s’authentifier au moyen d’un jeton d’accès personnel (Personal Access Token : PAT) dont la création est décrite en section 1.4.4.

Générer un nouveau jeton, le décrire en tant que “GitHub Actions” et lui donner l’autorisation “repo”, c’est-à-dire modifier tous les dépôts (il n’est pas possible de limiter l’accès à un dépôt particulier).

6.2.2 Secrets du projet

Sur GitHub, afficher les paramètres du projet et sélectionner “Secrets”. Le bouton “New Repository Secret” permet de stocker des variables utilisées dans les scripts des Actions GitHub (visibles publiquement) sans en diffuser la valeur. Le jeton d’accès personnel est indispensable pour que les Actions GitHub puissent écrire leur production dans le projet. Créer un secret nommé “GH_PAT” et saisir la valeur du jeton sauvegardée précédemment. Après avoir cliqué sur “Add Secret”, le jeton ne pourra plus être lu.

Pour permettre l’envoi de messages de succès ou d’échec sans diffuser son adresse de messagerie, créer un secret nommé “EMAIL” qui la contient.

6.2.3 Activation du dépôt sur CodeCov

L’analyse de la couverture du code des packages est utile pour détecter les portions de code non testées. En revanche, l’analyse de la couverture des projets de document n’a pas d’intérêt.

Pour activer un dépôt, il faut d’authentifier sur le site de CodeCov avec son compte GitHub. La liste des dépôts est affichée et peut être actualisée. Si les dépôts à traiter sont hébergés par une organisation, par exemple les dépôts d’une salle de classe GitHub, il faut actualiser la liste des organisations en suivant les instructions (un lien permet de modifier rapidement les options de GitHub pour autoriser la lecture d’une organisation par Codecov) et à nouveau mettre à jour la liste des dépôts. Enfin, quand le dépôt recherché est visible, il faut l’activer. Il est inutile d’utiliser le système de jetons de Codecov.

6.2.4 Scripter les actions de GitHub

Un flux de travail (workflow) de GitHub est une succession de tâches (jobs) comprenant des étapes (steps). Un flux de travail est déclenché par un évènement, généralement chaque push du projet, mais aussi à intervalles réguliers (cron).

Typiquement, les flux créés ici contiennent deux tâches : la première installe R et les composants nécessaires et exécute des scripts R (ce qui constitue ses étapes successives) ; la seconde publie des fichiers obtenus dans les pages GitHub.

Les flux de travail sont configurés dans un fichier au format YAML placé dans le dossier .github/workflows/ du projet. Les différentes parties du script sont présentées ci-dessous. Le script complet est celui de ce document, accessible sur GitHub112.

6.2.4.1 Déclenchement

L’action est déclenchée à chaque fois que des mises à jour sont poussées sur GitHub :

on:
  push:
     branches:
       - master

La branche prise en compte est master (à remplacer par main le cas échéant).

Pour déclencher l’action périodiquement, il faut utiliser la syntaxe de cron (le système de planification des tâches sous Unix) :

on:
  schedule:
    - cron: '0 22 * * 0'  # every sunday at 22:00

Les valeurs successives sont celles des minutes, des heures, du jour (quantième du mois), du mois et du jour de la semaine (0 pour dimanche à 6 pour samedi). Les * permettent d’ignorer une valeur.

Les entrées push et schedule peuvent être utilisées ensemble :

on:
  push:
     branches:
       - master
  schedule:
    - cron: '0 22 * * 0'

Actuellement, la planification n’est prise en compte que dans la branche master.

6.2.4.2 Nom du flux de travail

Le nom du flux est libre. Il sera affiché par le badge qui sera ajouté dans le fichier README.md du projet (voir section 6.4).

name: bookdown

6.2.4.3 Première tâche

Les tâches sont décrites dans la rubrique jobs. renderbook est le nom de la première tâche : il est libre. Ici, l’action principale consistera à produire un ouvrage bookdown avec la fonction render_book(), d’où son nom.

jobs:
  renderbook:
    runs-on: macOS-latest

La déclaration runs-on décrit le système d’exploitation sur lequel la tâche doit s’exécuter. Les choix possibles sont Windows, Ubuntu ou MacOS113. L’intégration continue de R sur GitHub utilise habituellement MacOS qui a l’avantage d’utiliser des packages R compilés donc beaucoup plus simples (certains packages nécessitent des librairies extérieures à R pour leur compilation) et rapides à installer, tout en permettant l’usage de scripts.

6.2.4.4 Premières étapes

Les étapes sont décrites dans la rubrique steps.

    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
      - name: Setup R
        uses: r-lib/actions/setup-r@v2
      - name: Install pandoc
        run: |
          brew install pandoc

Chaque étape est décrite par son nom (libre) et ce qu’elle réalise.

La force de GitHub Actions est de permettre l’utilisation d’actions écrites par d’autres et stockées dans un projet public GitHub. Une action est un script accompagné de métadonnées qui décrivent son usage. Son développement est accompagné par des numéros de version successifs. On appelle une action par l’instruction uses:, le projet GitHub qui la contient et sa version.

Dans leur projet GitHub respectif, les actions existent dans leur version de développement (@master) et dans des versions d’étape (release) accessibles par leur numéro (@v1). Ces versions d’étape sont préférables parce qu’elles sont stables.

Les actions généralistes sont mises à disposition par GitHub dans l’organisation GitHub Actions114. L’action “actions/checkout” permet de se placer dans la branche principale du projet traité par le flux de travail : c’est en général la première étape de tous les flux.

L’action suivante est l’installation de R, mise à disposition par l’organisation R infrastructure115.

L’installation de pandoc (logiciel extérieur à R mais nécessaire à R Markdown) peut être réalisée par une commande exécutée par MacOS. Elle est appelée par run: et peut contenir plusieurs lignes (d’où le |). Ce script dépend du système d’exploitation : brew est le gestionnaire de paquets de MacOS. Pour éviter les spécificités d’un système, il est préférable d’utiliser une action :

      - name: Install pandoc
        uses: r-lib/actions/setup-pandoc@v2

Il est souvent possible de choisir entre une action ou l’écriture du code correspondant. Le choix fait ici est de privilégier les actions pour tout ce qui a trait au système, comme les installations de logiciels, mais d’utiliser des scripts pour les commandes R, comme la vérification des packages. L’objectif est de contrôler précisément le code R et de limiter les dépendances à des packages supplémentaires. La stratégie inverse est développée dans Wickham et Bryan (2023) qui s’appuie entièrement sur des actions pour exécuter les tâches de R.

6.2.4.5 Packages

Cette étape utilise Rscript comme environnement de commande, ce qui lui permet d’exécuter directement des commandes R.

      - name: Install packages
        env:
          GITHUB_PAT: ${{ secrets.GH_PAT }}
        run: |
          options(pkgType = "binary")
          options(install.packages.check.source = "no")
          install.packages(c("remotes", "bookdown", "formatR", "tinytex"))
          tinytex::install_tinytex(bundle = "TinyTeX")
          remotes::install_deps(dependencies = TRUE)
        shell: Rscript {0}

Les packages servant à produire le document sont listés :

  • remotes pour sa fonction install_deps() ;
  • bookdown pour tricoter ;
  • formatR pour la mise en forme des bouts de code (tidy=TRUE) ;
  • tinytex pour disposer d’une distribution LaTeX.

Les autres packages, ceux utilisés par le projet, sont lus dans le fichier DESCRIPTION par la fonction install_deps().

Sous MacOS, les packages sont installés par défaut en version binaire, mais à partir de leur code source s’il est plus récent. La création des packages binaires prend quelques jours à CRAN : cette situation n’est donc pas rare. Les packages ne contenant que du code R ou du code C++ sans référence à des librairies externes s’installent en revanche sans problème. En revanche, si le package nécessite des librairies externes à R ou une compilation de code Fortran, l’installation échoue. Il serait donc nécessaire d’installer préalablement les librairies nécessaires (et éventuellement un compilateur Fortran) à l’ensemble des packages dont le projet dépend : cette solution n’est pas réaliste parce qu’elle implique l’inventaire de l’ensemble des dépendances, qui peuvent changer, et un nombre important d’installations chronophages et inutiles la plupart du temps, quand les packages binaires sont à jour. Une meilleure solution est de forcer l’installation des packages binaires même si le code source est plus récent : c’est l’objet des deux options de R définies avant l’appel à install.packages().

Enfin, le secret GH_PAT est passé à R dans une variable d’environnement, GITHUB_PAT, nécessaire pour installer des packages à partir leur code source sur GitHub. GitHub limite le rythme des accès anonymes pour l’ensemble des actions GitHub (tous comptes confondus) et peut rejeter la demande : en pratique, utiliser install_github() dans une action GitHub n’est envisageable qu’avec cette variable d’environnement.

6.2.4.6 Tricot

La production de l’ouvrage est lancée par une commande R.

      - name: Render pdf book
        run: |
          bookdown::render_book("index.Rmd", "bookdown::pdf_book")
        shell: Rscript {0}
      - name: Render gitbook
        run: |
          bookdown::render_book("index.Rmd", "bookdown::gitbook")
        shell: Rscript {0}

Les paramètres déclarés dans _output.yml sont utilisés.

Le fichier PDF doit être produit avant le format GitBook pour que son lien de téléchargement soit ajouté à la barre de menu du site GitBook. D’autre part, R doit être fermé et rouvert entre les deux rendus faute de quoi les tableaux ne sont pas créés correctement dans le GitBook116. Les deux étapes ne doivent pas être regroupées en une seule.

6.2.4.7 Sauvegarde

Le résultat du tricot, placé dans le dossier docs de la machine virtuelle en charge de l’intégration continue, doit être préservé pour que la tâche suivante puisse l’utiliser.

La dernière étape de la tâche de production utilise l’action upload-artifact pour cela.

      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          name: _book
          path: docs/

Le contenu de docs est sauvegardé en tant qu’artefact nommé “_book”. Les artefacts sont visibles publiquement sur la page des Actions du projet GitHub.

Après sa dernière étape, la machine virtuelle utilisée pour cette étape est détruite.

6.2.4.8 Publication

La publication de l’artefact dans la branche gh-pages du projet nécessite une autre tâche.

  deploy:
    runs-on: ubuntu-latest
    needs: renderbook
    steps:
      - name: Download artifact
        uses: actions/download-artifact@v3
        with:
          # Artifact name
          name: _book
          # Destination path
          path: docs
      - name: Deploy to GitHub Pages
        uses: Cecilapp/GitHub-Pages-deploy@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GH_PAT }}
        with:
          email: ${{ secrets.EMAIL }}
          build_dir: docs
          jekyll: no

La tâche est nommée “deploy” (le nom est libre). Elle s’exécute sur une machine virtuelle sous Ubuntu. Elle ne peut se lancer que si la tâche “renderbook” a réussi. Ses étapes sont les suivantes :

  • Download artifact : Restauration du dossier docs ;
  • Deploy to GitHub Pages : copie du dossier docs dans la branche gh-pages.

Cette dernière étape utiliser l’action GitHub-Pages-deploy mise à disposition par l’organisation Cecilapp . Elle utilise une variable d’environnement, GITHUB_TOKEN, pour s’authentifier et des paramètres :

  • email : l’adresse de messagerie destinataire du rapport d’exécution. Pour ne pas exposer l’adresse publiquement, elle a été stockée dans un secret du projet ;
  • buid_dir : le répertoire à publier ;
  • jekyll:no pour créer un fichier vide nommé .nojekyll qui indique aux pages GitHub de ne pas essayer de traiter leur contenu comme un site web Jekyll.

6.2.5 Données confidentielles dans un dépôt public

Si le projet contient des données confidentielles (section 3.6), GitHub Actions doit utiliser la clé privée du projet pour les extraire de leur coffre-fort.

La clé privée doit être stockée dans un secret du projet, nommé “RSA”. L’étape suivante, à insérer avant l’étape de tricot, écrit la clé dans un fichier pour que le code du projet y ait accès.

      - name: Private key
        run: |
          cat("${{ secrets.rsa }}", file="NomDuProjet.rsa"
        shell: Rscript {0}

6.3 Modèles de scripts

Des modèles de scripts pour tous les types de projets sont présentés ici.

La branche gh-pages est créée automatiquement par les scripts. Vérifier après la première exécution que les pages GitHub sont bien activées sur cette branche (section 3.7). Supprimer ensuite le dossier docs s’il existait, pousser la modification sur GitHub et enfin ajouter la ligne suivante au fichier .gitignore pour pouvoir tricoter localement les projets sans perturber GitHub :

docs/

6.3.1 memoiR

La fonction build_ghworkflow() du package memoiR crée automatiquement les scripts nécessaires à la production des modèles du package. Le script est toujours nommé memoir.yml.

Ces scripts n’ont pas besoin d’un fichier DESCRIPTION pour l’installation des dépendances mais chaque document doit contenir son le bout de code de paramétrage (Options) la liste de tous les packages nécessaires à son tricot (stockés dans la variable Packages).

Tous nécessitent même préparation : les secrets GH_PAT et EMAIL doivent être enregistrés dans le projet GitHub (section 6.2.2).

6.3.1.1 Projet d’ouvrage

Le flux de travail s’appelle rmarkdown ; sa tâche de production render.

on:
  push:
   branches:
     - master

name: rmarkdown

jobs:
  render:
    runs-on: macOS-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
      - name: Setup R
        uses: r-lib/actions/setup-r@v2
      - name: Install pandoc
        uses: r-lib/actions/setup-pandoc@v2
      - name: Install dependencies
        run: |
          options(pkgType = "binary")
          options(install.packages.check.source = "no")
          install.packages(c("distill", "downlit", "memoiR", "rmdformats", "tinytex"))
          tinytex::install_tinytex(bundle = "TinyTeX")
        shell: Rscript {0}
      - name: Render pdf book
        env:
          GITHUB_PAT: ${{ secrets.GH_PAT }}
        run: |
          bookdown::render_book("index.Rmd", "bookdown::pdf_book")
        shell: Rscript {0}
      - name: Render gitbook
        env:
          GITHUB_PAT: ${{ secrets.GH_PAT }}
        run: |
          bookdown::render_book("index.Rmd", "bookdown::gitbook")
        shell: Rscript {0}
      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          name: ghpages
          path: docs
  deploy:
    runs-on: ubuntu-latest
    needs: render
    steps:
      - name: Download artifact
        uses: actions/download-artifact@v3
        with:
          name: ghpages
          path: docs
      - name: Deploy to GitHub Pages
        uses: Cecilapp/GitHub-Pages-deploy@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GH_PAT }}
        with:
          email: ${{ secrets.EMAIL }}
          build_dir: docs
          jekyll: no

6.3.1.2 Articles et présentations

Le flux de travail s’appelle rmarkdown ; sa tâche de production render.

on:
  push:
   branches:
     - master

name: rmarkdown

jobs:
  render:
    runs-on: macOS-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
      - name: Setup R
        uses: r-lib/actions/setup-r@v2
      - name: Install pandoc
        uses: r-lib/actions/setup-pandoc@v2
      - name: Install dependencies
        run: |
          options(pkgType = "binary")
          options(install.packages.check.source = "no")
          install.packages(c("memoiR", "rmdformats", "tinytex"))
          tinytex::install_tinytex(bundle = "TinyTeX")
        shell: Rscript {0}
      - name: Render Rmarkdown files
        env:
          GITHUB_TOKEN: ${{ secrets.GH_PAT }}
        run: |
          RMD_PATH=($(ls | grep "[.]Rmd$"))
          Rscript -e 'for (file in commandArgs(TRUE)) |>
              rmarkdown::render(file, "all")' ${RMD_PATH[*]}
          Rscript -e 'memoiR::build_githubpages()'
      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          name: ghpages
          path: docs
  deploy:
    runs-on: ubuntu-latest
    needs: render
    steps:
      - name: Download artifact
        uses: actions/download-artifact@v3
        with:
          name: ghpages
          path: docs
      - name: Deploy to GitHub Pages
        uses: Cecilapp/GitHub-Pages-deploy@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GH_PAT }}
        with:
          email: ${{ secrets.EMAIL }}
          build_dir: docs
          jekyll: yes

L’étape chargée du tricot utilise un script pour lister tous les fichiers .Rmd, les traiter (tous les formats de sortie listés dans leur entête yaml sont produits). La fonction build_githubpages() (voir section 4.3.2) place les résultat dans docs.

La tâche de déploiement indique aux pages GitHub d’utiliser Jekyll, c’est-à-dire d’utiliser le fichier README.md comme page d’accueil.

6.3.1.3 Internationalisation

Si l’étape de tricot nécessite de modifier la langue utilisée par R, par exemple pour afficher correctement la date de production des documents, elle peut être modifiée comme ceci :

      - name: Render Rmarkdown files
        run: |
          Sys.setlocale("LC_TIME", "fr_FR")
          lapply(list.files(pattern="*.Rmd"), 
                 function(file) rmarkdown::render(file, "all"))
          memoiR::build_githubpages()
        shell: Rscript {0}

La sélection des fichiers est ici réalisée par un script R, qui inclut une commande de localisation, ici en Français.

Cette étape peut être complétée par la sélection d’un thème GitHub Pages pour que la page d’accueil contienne un lien vers le code :

        run: |
          echo 'theme: jekyll-theme-slate' > docs/_config.yml

Le thème est ici “Slate”, un des choix proposés par les pages GitHub.

6.3.1.4 Débogage

Il peut arriver que la compilation du fichier .tex pour produire le fichier PDF échoue, bien que le tricot en HTML ne génère pas d’erreur. Le compilateur LaTeX est en effet plus exigeant que pandoc (qui produit le fichier HTML).

La première vérification consiste à tricoter en PDF localement, sur son poste de travail, le document qui pose problème, avec tinytex. Les packages LaTeX doivent être mis à jour pour être les mêmes que ceux utilisés par les actions GitHub : pour cela, exécuter :

tinytex::tlmgr_update()

Si la compilation fonctionne localement mais pas sur Github, il faut inspecter le fichier .log qui enregistre tous les évènements générés par le compilateur, mais ce fichier n’est pas conservé après l’échec de GitHub Actions. Il faut donc modifier le script pour copier le fichier dans docs puis créer sauvegarder le résultat malgré l’erreur.

L’étape “Upload artifact” est modifiée pour s’exécuter malgré l’échec de l’étape précédente en ajoutant la ligne if: :

      - name: Upload artifact
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: _book
          path: docs/

Une étape est ajoutée avant “Upload artifact” pour copier le résultat du tricot et le fichier .log :

      - name: Move files to docs
        if: always()
        run: |
          Rscript -e 'memoiR::build_githubpages()'
          cp *.log docs

Après l’échec de l’action, l’artéfact sauvegardé peut être téléchargé à partir de GitHub : il se trouve sur la page de résumé de l’action. Il s’agit d’un fichier compressé qui contient tout le dossier docs.

6.3.2 Site web blogdown

Le fichier appelé blogdown.yml est très similaire. Le nom du flux de travail est blogdown et celui de la tâche de production est buildsite.

on:
  push:
     branches:
       - master
  schedule:
    - cron: '0 22 * * 0'

name: blogdown

jobs:
  buildsite:
    runs-on: macOS-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
      - name: Setup R
        uses: r-lib/actions/setup-r@v2
      - name: Install pandoc
        uses: r-lib/actions/setup-pandoc@v2
      - name: Install packages
        env:
          GITHUB_TOKEN: ${{ secrets.GH_PAT }}
        run: |
          options(pkgType = "binary")
          options(install.packages.check.source = "no")
          install.packages(c("remotes", "blogdown", "formatR"))
          remotes::install_deps(dependencies = TRUE)
        shell: Rscript {0}
      - name: Build website
        env:
          GITHUB_TOKEN: ${{ secrets.GH_PAT }}
        run: |
          blogdown::install_hugo(force = TRUE)
          blogdown::build_site(local = TRUE, build_rmd = TRUE)
        shell: Rscript {0}
      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          name: _website
          path: public/
  deploy:
    runs-on: ubuntu-latest
    needs: buildsite
    steps:
      - name: Download artifact
        uses: actions/download-artifact@v3
        with:
          # Artifact name
          name: _website
          # Destination path
          path: public
      - name: Deploy to GitHub Pages
        uses: Cecilapp/GitHub-Pages-deploy@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GH_PAT }}
        with:
          build_dir: public
          email: ${{ secrets.EMAIL }}
          jekyll: no

La tâche Build website utilise le package blogdown pour installer Hugo (le générateur de sites web) et ensuite construire le site.

Si le site web utilise des données en ligne qui justifient de le mettre à jour périodiquement, GitHub Actions peut être lancé tous les jours, toutes les semaines ou tous les mois en plus des reconstruction déclenchées par une modification du dépôt (voir section 6.2.4.1). Ici, le site est reconstruit tous les dimanches à 22h.

Exemple : la page qui affiche la bibliométrie du site web117 de l’auteur interroge Google Scholar pour afficher les citations des publications. Le site est mis à jour toutes les semaines pour que les statistiques soient à jour.

6.3.3 Packages R

Un script optimal pour la vérification d’un package est le suivant :

on:
  push:
    branches:
      - master

name: R-CMD-check

jobs:
  R-CMD-check:
    runs-on: macOS-latest
    steps:
      - name: Pull the repository
        uses: actions/checkout@v4
      - name: Install R
        uses: r-lib/actions/setup-r@v2
      - name: Install pandoc
        uses: r-lib/actions/setup-pandoc@v2
      - name: Install R packages
        run: |
          options(pkgType = "binary")
          options(install.packages.check.source = "no")
          install.packages(c("remotes", "roxygen2", "rcmdcheck", "covr", "pkgdown"))
          remotes::install_deps(dependencies = TRUE)
        shell: Rscript {0}
      - name: Update the documentation
        run: roxygen2::roxygenize()
        shell: Rscript {0}
      - name: Commit and push the repository
        uses: EndBug/add-and-commit@v9
      - name: Check the package
        run: rcmdcheck::rcmdcheck(args = "--no-manual", error_on = "warning")
        shell: Rscript {0}
      - name: Test coverage
        run: covr::codecov(type="all")
        shell: Rscript {0}
      - name: Install the package
        run: R CMD INSTALL .
      - name: Pkgdown
        run: |
          git config --local user.email "actions@github.com"
          git config --local user.name "GitHub Actions"
          Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)'

Le fichier est nommé check.yml. Il ne contient qu’une seule tâche, nommée R-CMD-check comme le flux.

remotes installe les packages nécessaires à partir du fichier DESCRIPTION.

L’étape Roxygenize met à jour la documentation du package. Les fichiers mis à jour sont poussés dans la branche principale du projet par l’action add-and-commit. Ces deux étapes permettent de garantir que le package est dans un état cohérent, même si l’auteur a omis d’exécuter la fonction roxygenize() avant de pousser son code sur GitHub. Pour éviter de déclencher en boucle la vérification du code poussé de cette façon, le jeton d’accès utilisé est obligatoirement celui du script en cours, créé par GitHub à chaque exécution. Ce jeton n’a par défaut pas le droit de modifier le dépôt. Il faut donc le lui donner : sur GitHub, afficher les paramètres du projet et sélectionner “Actions”, “General”. Dans la section “Workflow permissions”, sélectionner “Read and write permissions”.

L’étape Check vérifie le package. Les avertissements sont traités comme des erreurs.

L’étape Test coverage utilise le package covr pour mesurer le taux de couverture et téléverse les résultats sur le site Codecov.

Enfin, les deux dernières étapes installent le package puis utilisent pkgdown pour créer le site de documentation du package et le pousser dans la branche gh-pages du projet.

Ce script ne contient qu’une tâche : le déploiement du site de documentation est directement exécuté par pkgdown. Son succès est affiché par un badge à afficher dans le fichier README.md (voir section 6.4)

Des scripts plus complexes sont proposés par R-lib118, notamment pour exécuter les tests sur plusieurs systèmes d’exploitation et plusieurs versions de R. Ces tests poussés sont à effectuer avant de soumettre à CRAN (section 5.11) mais consomment trop de ressource pour un usage systématique.

6.3.4 Pull requests

Les requêtes de tirage peuvent être testées par des scripts très proches pour vérifier qu’elles ne génèrent pas d’erreurs avant de les fusionner.

Une méthode efficace consiste à créer un nouveau script dans le dossier .github/workflows/, en partant d’une copie du script existant. Le nouveau script sera nommé pr.yml. Le déclenchement doit être modifié : pull_request remplace push :

on:
  pull_request:
     branches:
       - master

6.3.4.1 memoiR

Les scripts de vérification des documents créés par memoiR doivent être coupés après l’étape Render gitbook : l’artefact ne doit pas être sauvegardé et la tâche de déploiement doit être supprimée. Le script est donc le suivant :

on:
  pull_request:
    branches:
      - master

name: rmarkdown

jobs:
  render:
    runs-on: macOS-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
      - name: Setup R
        uses: r-lib/actions/setup-r@v2
      - name: Install pandoc
        uses: r-lib/actions/setup-pandoc@v2
      - name: Install dependencies
        run: |
          options(pkgType = "binary")
          options(install.packages.check.source = "no")
          install.packages(c("distill", "downlit", "memoiR", "rmdformats", "tinytex"))
          tinytex::install_tinytex(bundle = "TinyTeX")
        shell: Rscript {0}
      - name: Render pdf book
        env:
          GITHUB_PAT: ${{ secrets.GH_PAT }}
        run: |
          bookdown::render_book("index.Rmd", "bookdown::pdf_book")
        shell: Rscript {0}
      - name: Render gitbook
        env:
          GITHUB_PAT: ${{ secrets.GH_PAT }}
        run: |
          bookdown::render_book("index.Rmd", "bookdown::bs4_book")
        shell: Rscript {0}
      # Don't upload the artifact and don't deploy

6.3.4.2 Packages R

Les scripts de vérification des packages ne doivent pas pousser les mises à jour de leur documentation par Roxygenize2, ni déployer leur mise à jour du site pkgdown dans les pages GitHub. Le taux de couverture n’a pas à être mesuré. Le script est le suivant :

on:
  pull_request:
    branches:
      - master

name: R-CMD-check

jobs:
  R-CMD-check:
    runs-on: macOS-latest
    steps:
      - name: Pull the repository
        uses: actions/checkout@v4
      - name: Install R
        uses: r-lib/actions/setup-r@v2
      - name: Install pandoc
        uses: r-lib/actions/setup-pandoc@v2
      - name: Install R packages
        run: |
          options(pkgType = "binary")
          options(install.packages.check.source = "no")
          install.packages(c("remotes", "roxygen2", "rcmdcheck", "covr", "pkgdown"))
          remotes::install_deps(dependencies = TRUE)
        shell: Rscript {0}
      - name: Update the documentation
        run: roxygen2::roxygenize()
        shell: Rscript {0}
        # Don't push
      - name: Check the package
        run: rcmdcheck::rcmdcheck(args = "--no-manual", error_on = "warning")
        shell: Rscript {0}
      # Don't test coverage
      - name: Install the package
        run: R CMD INSTALL .
      - name: Pkgdown
        # Build the package site locally
        run: Rscript -e 'pkgdown::build_site()'

Quand des requêtes de tirage sont soumises, le test correspondant est lancé et ses résultats intégrés à la discussion.

6.4 Ajouter des badges

Le succès des Actions GitHub est visible en ajoutant un badge dans le fichier README.md, juste après le titre du fichier. Sur la page du projet, choisir “Actions” puis sélectionner l’action (dans “Workflows”). Cliquer sur le bouton “…” puis sur “Create Status Badge”. Coller le code Markdown :

# Nom du projet
![bookdown](https://github.com/<GitHubID>/<Depot>/workflows/<NomDuFlux>/badge.svg)

Le nom du flux a été déclaré dans l’entrée name: du fichier de configuration des actions GitHub.

Le taux de couverture mesuré par Codecov peut aussi être affiché par un badge :

[![codecov](https://codecov.io/github/<GitHubID>/
  <Depot>/branch/master/graphs/badge.svg)]
  (https://codecov.io/github/<GitHubID>/<Depot>)