Github Actions qu’est-ce que c’est ?

Github Actions est une solution d’intégration continue et déploiement automatisé (CI/CD). C’est une extension de Github. Son fonctionnement repose sur des évènements Github tel qu’un push de commits.

On écrit un processus à lancer en rapport avec son projet. Il sera exécuté en fonction des critères définis dans l’action à réaliser. Cela a été conçu de manière à effectuer des processus de manière continue en fonction du travail collaboratif sur Git. Il faut savoir que Github Actions se base sur Docker, il est donc recommandé d’avoir des notions dessus pour l’utiliser assez simplement.

Ecrire son action

Une action comporte plusieurs éléments nécessaires pour effectuer un processus :
l’événement on qui correspond à l’événement Github : push, pull request etc.

Cependant, pour la bêta seul le push et le pull request sont utilisables.

on: [push, pull_request]

Il est bien évidemment possible de définir plus précisément le cadre de votre action sur certaines branches de votre repository pour effectuer l’action en fonction de cas précis : effectuer l’action uniquement sur la branche master par exemple.


on: 
  push:
  - master

Ensuite, nous avons les jobs. Concrètement, un job est une suite d’étapes effectuées de manière ordonnée.
Il comporte 2 éléments importants : l’environnement et les étapes à effectuer.

Le paramètre runs-on défini l’environnement dans lequel l’action est effectuée :


runs-on: ubuntu-latest

Il existe plusieurs environnements virtuels possibles pour l’exécution de votre action, cela peut être utile pour que cela correspondent aux spécificités de chacun. Du dernier Ubuntu à Windows 2016, vous avez le choix 😉

Il est à noter que chaque job est effectué dans une nouvelle instance de l’environnement virtuel.

Donc si vous voulez faire une suite d’actions avec un lien entre elles, il faudra penser à le faire dans le même job.

Mais il est possible de faire des dépendances de jobs avec le paramètre need :


jobs:
 job1:
 job2:
  need: job1

Dans ce cas, job2 ne s’exécutera uniquement lorsque job1 aura été effectué.

Ensuite, nous avons l’ensemble des étapes à réaliser lors du processus. Dans une optique d’intégration continue, le but est de lancer les tests automatiquement. Néanmoins, il faut définir un certain nombre d’éléments avant de pouvoir exécuter les tests !

Cet ensemble est défini par le mot-clé steps.
Chaque étape requiert au minimum un élément à exécuter, c’est-à-dire l’un de ses éléments :

  • le mot-clé uses pour utiliser un élément spécifique :
    • une action publique
    • une action dans le même repository
    • une action sur le Docker Hub ou sur le registre public Docker

ou

  • le mot-clé run pour lancer une commande en bash ( sachant qu’il y a plusieurs moyen d’exécuter une commande si nécessaire)

Enfin, on associe le paramètre name pour savoir quelle étape cela représente.

Un exemple étant toujours plus parlant, alors voici un job pour tester un projet Django :


jobs:
 tests:
  runs-on: ubuntu-latest
 
  steps:
  - uses: actions/checkout@v1
  - name: Set up Python 3.5.7
    uses: actions/setup-python@v1
    with:
     python-version: 3.5.7
  - name: Install dependencies
    run: pip install -r requirements.txt
  - name: Run tests
    run: python manage.py test

Ici le job tests consiste à :

  • installer Python 3.5.7 sur l’environnement Ubuntu
  • installer les dépendances
  • lancer les tests

Pour les tests en condition réelle, on a besoin d’une base de données, d’où la mise en place de services.

Ajouter un service à son action

Comment fait-on ?

Très simple, cela se rapproche de la définition d’un service dans un docker-compose.yml mais avec beaucoup moins de paramètres.

On utilise le mot-clé services avec :

  • la définition de l’image Docker à utiliser
  • la liste des ports à exposer du service
  • une liste de variables d’environnement
  • une liste de volumes

L’ensemble de ces éléments ne sont pas tous requis mais bon à savoir.
Concrètement, cela ressemble à cela, pour un service Postgres :


services:
 image: postgres
 env:
 - POSTGRES_USER: postgres
 - POSTGRES_PASSWORD: postgres
 - POSTGRES_BD: postgres
 ports:
 - 5432/tcp
 options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5

On a besoin de l’image Docker à lancer, du port à exposer et du nom du service. Il existe un moyen de transmettre des valeurs d’environnement ou des options Docker comme on peut le voir dans l’exemple ci-dessus. Les valeurs sensibles peuvent être définis avec les secrets key.
De plus, il est parfois nécessaire d’avoir besoin d’un retour de l’état du service pour établir une connexion. Par conséquent, on définit un élément qui vérifie la santé du service pour savoir lorsqu’il est opérationnel (également appelé health check). J’ai repris l’exemple fait par Chris PATTERSON et Mike COUTERMASH pour PostgreSQL.

Pour le cas ou vous n’avez pas de health check à disposition, on met en pause l’exécution du processus avec la commande sleep, oui ce n’est pas terrible, je vous l’accorde…

Tout cela c’est très bien, mais il faut bien faire le lien entre le service et notre application. Nous allons le faire grâce au port et pour l’hôte ce sera localhost étant donné que le service est exécuté directement dans la machine Ubuntu dans notre exemple. Sinon cela aurait été le nom du service.

Dans l’exemple précédent, on attribue un port libre de manière aléatoire au port 5432 de la machine et on y accède de la manière suivante :

${{ job.services.postgres.ports[5432] }}

Néanmoins, on peut l’attribuer de manière fixe :


ports:
- 5432:5432

Ici, nous avons le port 5432 du service lié au port 5432 de la machine définie dans l’action.

On obtient le workflow suivant :


name: Test Workflow

on: push



jobs:
  tests:
    runs-on: ubuntu-latest
 
  services:
   image: postgres
   env:
   - POSTGRES_USER: postgres
   - POSTGRES_PASSWORD: postgres
   - POSTGRES_BD: postgres
   ports:
   - 5432:5432
   options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
  
  steps:
   - uses: actions/checkout@v1
   - name: Set up Python 3.5.7
     uses: actions/setup-python@v1
     with:
      python-version: 3.5.7
    - name: Install dependencies
     run: pip install -r requirements.txt
    - name: Run tests
      run: python manage.py test

Il reste à bien changer les valeurs du service dans le fichiers settings.py de Django pour la connexion à la base de données PostgreSQL dans le cadre de notre exemple.

Puis le tour est joué, plus qu’à pousser du code et espérer que le tout fonctionne 😉

Traduction anglaise : https://hackernoon.com/how-to-create-github-actions-to-run-tests-with-docker-services-ive-been-digging-github-actions-si-bk1133ye