Article
· Fév 15, 2023 8m de lecture

Intégration continue avec le gestionnaire de paquets ObjectScript, GitHub Actions et Docker

Introduction

Dans un article précédent, j'ai abordé les modèles d'exécution des tests unitaires via le gestionnaire de paquets ObjectScript. Cet article va un peu plus loin, en utilisant les actions GitHub pour piloter l'exécution des tests et la création de rapports. Le cas d'utilisation qui nous motive est l'exécution du CI pour l'un de mes projets Open Exchange, AppS.REST (voir l'article d'introduction à ce projet ici). Vous pouvez voir l'implémentation complète dont les extraits de cet article ont été tirés sur GitHub ; elle pourrait facilement servir de modèle pour l'exécution de l'IC pour d'autres projets utilisant le gestionnaire de paquets ObjectScript.

Les fonctionnalités dont la mise en œuvre a été démontrée comprennent :

  • Compilation et test d'un paquet ObjectScript
  • Rapport sur la mesure de la couverture des tests (en utilisant le paquet TestCoverage) via codecov.io
  • Téléchargement d'un rapport sur les résultats des tests en tant qu'artefact de comppilation.

L'environnement de compilation

Il existe une documentation complète sur les actions GitHub ici. Dans le cadre de cet article, nous nous contenterons d'explorer les aspects présentés dans cet exemple.

Un flux de travail dans les actions GitHub est déclenché par un ensemble configurable d'événements et consiste en un certain nombre de tâches qui peuvent être exécutées séquentiellement ou en parallèle. Chaque tâche comporte un ensemble d'étapes - nous allons entrer dans le détail des étapes de notre exemple d'action plus tard. Ces étapes consistent en références à des actions disponibles sur GitHub, ou peuvent simplement être des commandes shell. Un extrait du modèle initial de notre exemple ressemble à ceci :

# Flux de travail d'intégration continue
name: CI

# Contrôle le moment où l'action sera exécutée. Déclenche le flux de travail sur les événements push ou pull request
# événements dans toutes les branches
on: [push, pull_request]

# Un flux de travail est composé d'une ou plusieurs tâches qui peuvent être exécutées séquentiellement ou en parallèle.
jobs:
  # Ce flux de travail contient une seule tâche appelé "build".
  build :
    # Le type d'exécuteur sur lequel le travail sera exécuté
    runs-on : ubuntu-latest

    env:
    # Variables d'environnement utilisables tout au long de la tâche de "compilation", par exemple dans les commandes au niveau du système d'exploitation.
package: apps.rest
    container_image : intersystemsdc/iris-community:2019.4.0.383.0-zpm
    # D'autres éléments seront abordés plus tard...

    # Les étapes représentent une séquence de tâches qui seront exécutées dans le cadre du travail.
     steps:
          # Ceux-ci seront montrés plus tard...

Dans cet exemple, un certain nombre de variables d'environnement sont utilisées. Pour appliquer cet exemple à d'autres paquets utilisant le gestionnaire de paquets ObjectScript, la plupart d'entre elles n'auront pas besoin d'être modifiées, alors que certaines le seront.

    env:
      # ** POUR UN USAGE GÉNÉRAL, IL FAUDRA PROBABLEMENT CHANGER : **
      package: apps.rest
      container_image: intersystemsdc/iris-community:2019.4.0.383.0-zpm

      # ** POUR UN USAGE GÉNÉRAL, IL FAUDRA PEUT-ÊTRE CHANGER : **
      build_flags: -dev -verbose # Télécharger en mode -dev pour obtenir le code de test unitaire préchargé
      test_package: UnitTest

      # ** POUR UN USAGE GÉNÉRAL, IL NE FAUDRA PAS CHANGER : **
      instance: iris
      # Remarque : la valeur test_reports est dupliquée dans la variable d'environnement test_flags.
      test_reports: test-reports
      test_flags: >-
       -verbose -DUnitTest.ManagerClass=TestCoverage.Manager -DUnitTest.JUnitOutput=/test-reports/junit.xml
       -DUnitTest.FailuresAreFatal=1 -DUnitTest.Manager=TestCoverage.Manager
       -DUnitTest.UserParam.CoverageReportClass=TestCoverage.Report.Cobertura.ReportGenerator
       -DUnitTest.UserParam.CoverageReportFile=/source/coverage.xml

Si vous voulez adapter cela à votre propre paquet, il suffit de déposer votre propre nom de paquet et votre image de conteneur préférée (doit inclure zpm - voir https://hub.docker.com/r/intersystemsdc/iris-community). Vous pourriez également vouloir changer le paquet de tests unitaires pour qu'il corresponde à la convention de votre propre paquet (si vous devez charger et compiler les tests unitaires avant de les exécuter pour gérer toutes les dépendances de chargement/compilation ; j'ai eu quelques problèmes bizarres spécifiques aux tests unitaires pour ce paquet, donc cela pourrait même ne pas être pertinent dans d'autres cas).

Le nom de l'instance et le répertoire test_reports ne devraient pas être modifiés pour d'autres utilisations, et les test_flags fournissent un bon ensemble de valeurs par défaut - ils permettent de faire en sorte que les échecs des tests unitaires signalent l'échec de la compilation, et gèrent également l'exportation des résultats des tests au format jUnit et un rapport de couverture de code.

Étapes de compilation

Vérification des référentiels GitHub

Dans notre exemple de motivation, deux dépôts doivent être vérifiés - celui qui est testé, et aussi mon fork de Forgery (parce que les tests unitaires en ont besoin).

    # Vérifie ce référentiel sous $GITHUB_WORKSPACE, afin que votre tâche puisse y accéder.
    - uses: actions/checkout@v2

    # Il faut aussi vérifier le timleavitt/forgery jusqu'à la version officielle installable via ZPM
    - uses: actions/checkout@v2
      with:
        repository: timleavitt/forgery
        path: forgery

$GITHUB_WORKSPACE est une variable d'environnement très importante, représentant le répertoire racine où tout cela fonctionne. Du point de vue des permissions, vous pouvez faire à peu près tout ce que vous voulez dans ce répertoire ; ailleurs, vous pouvez rencontrer des problèmes.

Exécution du conteneur IRIS d'InterSystems

Après avoir configuré un répertoire où nous finirons par placer nos rapports de résultats de tests, nous allons exécuter le conteneur InterSystems IRIS Community Edition (+ZPM) pour notre compilation.

    - name: Run Container
      run: |
        # Créer le répertoire test_reports pour partager les résultats des tests avant l'exécution du conteneur.
        mkdir $test_reports
        chmod 777 $test_reports
        # Lancer l'instance InterSystems IRIS
        docker pull $container_image
        docker run -d -h $instance --name $instance -v $GITHUB_WORKSPACE:/source -v $GITHUB_WORKSPACE/$test_reports:/$test_reports --init $container_image
        echo halt > wait
        # Attendez que l'instance soit prête
        until docker exec --interactive $instance iris session $instance < wait; do sleep 1; done

Il y a deux volumes partagés avec le conteneur - l'espace de travail GitHub (pour que le code puisse être chargé ; nous y rapporterons également des informations sur la couverture des tests), et un répertoire séparé où nous placerons les résultats des tests jUnit.

Après la fin de "docker run", cela ne signifie pas que l'instance est complètement démarrée et prête à être commandée. Pour attendre que l'instance soit prête, nous continuerons à essayer d'exécuter une commande "halt" via la session iris ; cela échouera et continuera à essayer une fois par seconde jusqu'à ce que cela réussisse (éventuellement), indiquant que l'instance est prête.

Installation des bibliothèques liées aux tests

Pour notre cas d'utilisation motivant, nous utiliserons deux autres bibliothèques pour les tests - TestCoverage et Forgery. TestCoverage peut être installé directement via le Community Package Manager ; Forgery (actuellement) doit être chargé via zpm "load" ; mais les deux approches sont valables.

    - name: Install TestCoverage
      run: |
        echo "zpm \"install testcoverage\":1:1" > install-testcoverage
        docker exec --interactive $instance iris session $instance -B < install-testcoverage
        # Solution aux problèmes de permissions dans TestCoverage (création d'un répertoire pour l'exportation des sources)
        chmod 777 $GITHUB_WORKSPACE

    - name: Install Forgery
      run: |
        echo "zpm \"load /source/forgery\":1:1" > load-forgery
        docker exec --interactive $instance iris session $instance -B < load-forgery

L'approche générale consiste à écrire les commandes dans un fichier, puis à les exécuter en session IRIS. Le ":1:1" supplémentaire dans les commandes ZPM indique que la commande doit quitter le processus avec un code d'erreur si une erreur se produit, et s'arrêter à la fin si aucune erreur ne se produit ; cela signifie que si une erreur se produit, elle sera signalée comme une étape de compilation ayant échoué, et nous n'avons pas besoin d'ajouter une commande "halt" à la fin de chaque fichier.

Compilation et test du paquet

Enfin, nous pouvons effectivement compiler et exécuter des tests pour notre paquet. C'est assez simple - remarquez l'utilisation des variables d'environnement $build_flags/$test_flags que nous avons définies plus tôt.

    # Exécute un ensemble de commandes en utilisant l'exécuteur runners
    - name: Build and Test
      run: |
        # Exécution de compilation
        echo "zpm \"load /source $build_flags\":1:1" > build
        # Le paquet de test est compilé en premier comme solution de contournement pour certains problèmes de dépendance.
        echo "do \$System.OBJ.CompilePackage(\"$test_package\",\"ckd\") " > test
        # Exécution des tests
        echo "zpm \"$package test -only $test_flags\":1:1" >> test
        docker exec --interactive $instance iris session $instance -B 

Cela suit le même schéma que nous avons vu, écrire des commandes dans un fichier puis utiliser ce fichier comme entrée de la session iris.

La dernière partie de la dernière ligne télécharge les résultats de la couverture du code sur codecov.io. Super facile !

Téléchargement des résultats des tests unitaires

Supposons qu'un test unitaire échoue. Il serait vraiment ennuyeux de devoir revenir en arrière dans le journal de compilation pour trouver ce qui n'a pas fonctionné, bien que cela puisse toujours fournir un contexte utile. Pour nous faciliter la vie, nous pouvons télécharger nos résultats formatés par jUnit et même exécuter un programme tiers pour les transformer en un joli rapport HTML.

    # Générer et télécharger le rapport HTML xUnit
    - name: XUnit Viewer
      id: xunit-viewer
      uses: AutoModality/action-xunit-viewer@v1
      if: always()
      with:
        # Avec -DUnitTest.FailuresAreFatal=1, un test unitaire qui échoue fera échouer la compilation avant ce point.
        # Cette action pourrait autrement mal interpréter notre sortie de style xUnit et faire échouer la compilation même si
        # tous les tests sont passés.
        fail: false
    - name: Atacher le rapport
      uses: actions/upload-artifact@v1
      if: always()
      with:
        name: ${{ steps.xunit-viewer.outputs.report-name }}
        path: ${{ steps.xunit-viewer.outputs.report-dir }}

Ces informations sont principalement tirées du fichier readme à l'adresse https://github.com/AutoModality/action-xunit-viewer.

Le résultat final

Si vous voulez voir les résultats de ce flux de travail, regardez :

Les journaux pour le job CI sur intersystems/apps-rest (y compris les artefacts de compilation) : https://github.com/intersystems/apps-rest/actions?query=workflow%3ACI
Rapports de couverture de test : https://codecov.io/gh/intersystems/apps-rest

N'hésitez pas à me faire savoir si vous avez des questions !

Discussion (0)3
Connectez-vous ou inscrivez-vous pour continuer