Warning
Prérequis :
- Avoir un compte GitHub et forker ce repository
- Être familier avec TypeScript et YAML
Ce TP est une initiation à la création et configuration de pipelines GitHub dans le cadre de la chaîne d'intégration continue (Continuous Integration Continuous Delivery).
L'intégration continue (de l'anglais : Continuous integration, CI), est un ensemble de pratiques utilisées en génie logiciel consistant à vérifier à chaque modification de code source que le résultat des modifications ne produit pas de régression dans l'application développée.
L'intégration continue repose souvent sur la mise en place d'une brique logicielle permettant l'automatisation de tâches : compilation, tests unitaires et fonctionnels, validation produit, tests de performances… À chaque changement du code, cette brique logicielle va exécuter un ensemble de tâches et produire un ensemble de résultats, que le développeur peut par la suite consulter. Cette intégration permet ainsi de ne pas oublier d'éléments lors de la mise en production et donc ainsi améliorer la qualité du produit.
-- Wikipedia : Intégration continue
GitHub Actions est une plateforme d’intégration continue et livraison continue (CI/CD) qui vous permet d’automatiser votre pipeline de génération, de test et de déploiement. Vous pouvez créer des workflows qui créent et testent chaque demande de tirage (pull request) adressée à votre dépôt, ou déployer des demandes de tirage fusionnées en production.
-- Documentation GitHub : Comprendre GitHub Actions
Un workflow est un processus automatisé configurable qui exécutera un ou plusieurs travaux. Les workflows sont définis par un fichier YAML archivé dans votre dépôt et s’exécutent lorsqu’ils sont déclenchés par un événement dans votre dépôt, ou ils peuvent être déclenchés manuellement ou selon une planification définie.
Les workflows sont définis dans le répertoire
.github/workflowsd’un référentiel, et un référentiel peut avoir plusieurs workflows, chacun pouvant effectuer un ensemble différent de tâches. Par exemple, vous pouvez avoir un workflow pour générer et tester des demandes de tirage, un autre workflow pour déployer votre application chaque fois qu’une version est créée, et encore un autre workflow qui ajoute une étiquette chaque fois que quelqu’un ouvre un nouveau problème.
Note
Quelques liens utiles pour GitHub Actions :
- Cette application Node TypeScript est une API web permettant de réaliser des opérations mathématiques standards (addition, soustraction, multiplication, division).
src/index.ts, point d'entrée de l'application et démarre le serveur websrc/util/compute.util.ts, implémentation des opérations mathématiquestest/util/compute.util.test.ts, tests unitaires.github/workflows/my-workflow.yml, workflow GitHub primitif déclenchable manuellement depuis le repository
Important
- Un simple éditeur de texte suffit puisqu'il n'est pas demandé d'exécuter l'application en local.
- Il est possible de travailler directement sur la branche par défaut (
main). - La visibilité du repository doit être public afin de pouvoir aller jusqu'au bout du TP.
- Des erreurs volontaires ont été introduites dans le TP. La correction ne doit être effectuée qu'une fois explicitement demandée.
- Exécuter manuellement le workflow existant avec GitHub Actions. (Documentation GitHub : Exécution manuelle d’un workflow).
- Mettre à jour le workflow pour lancer un build de l'application (Documentation GitHub : Commandes de workflow).
Tip
L'application se build via la ligne de commande npm run build.
Réponse
my-workflow.ymlname: Manual build
on:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "16"
- name: Install dependencies
run: npm install
- name: Build application
run: npm run build- Le build de l'application est en échec. Consulter les logs du workflow et corriger l'application. Vérifier que le build passe avec GitHub Actions.
Réponse
index.ts...
app.get("/minus/:a/:b", (request: Request, response: Response) => {
...
const b = parseInt(request.params.b);
...
});
...- Mettre à jour le workflow pour lancer les tests de l'application. Les tests sont en échec et doivent le rester pour le moment.
Tip
L'application se teste via la ligne de commande npm test.
Réponse
my-workflow.ymlname: Manual build & test
on:
workflow_dispatch:
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "16"
- name: Install dependencies
run: npm install
- name: Build application
run: npm run build
- name: Test application
run: npm test- Mettre à jour le workflow pour qu'il accepte un paramètre
Run tests?, avecfalseen valeur par défaut, afin que les tests ne soient joués que si la valeur donnée lors de l'exécution du workflow esttrue(Documentation GitHub : on.workflow_dispatch.inputs). Vérifier que le comportement du workflow est différent en fonction de la valeur renseignée à l'exécution du workflow (Documentation GitHub : Utilisations des conditions).
Réponse
my-workflow.ymlname: Manual build & test
on:
workflow_dispatch:
inputs:
run-tests:
description: "Run tests?"
required: true
default: "false"
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "16"
- name: Install dependencies
run: npm install
- name: Build application
run: npm run build
- name: Test application
if: ${{ github.event.inputs.run-tests == 'true' }}
run: npm test- Créer une nouvelle branche Git.
- Mettre à jour le workflow pour qu'il s'exécute automatiquement lorsque le code est poussée sur une Pull Request (Documentation GitHub : Workflow Pull Request).
- Les tests doivent être joués.
- Pousser la branche et créer la Pull Request associée sans la fusionner.
Réponse
my-workflow.ymlname: Manual or PR build & test
on:
workflow_dispatch:
inputs:
run-tests:
description: "Run tests?"
required: true
default: "false"
pull_request:
types: [opened, synchronize, reopened]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "16"
- name: Install dependencies
run: npm install
- name: Build application
run: npm run build
- name: Test application
if: ${{ github.event.inputs.run-tests == 'true' || github.event_name == 'pull_request' }}
run: npm test- La Pull Request précédemment créée peut tout de même être fusionnée alors que les tests unitaires de l'application ne sont pas tous en succès. Mettre en place une règle de sécurité sur la branche pour empêcher cela et bloquer tout push sur la branche de travail. (Documentation GitHub : Création d'un ensemble de règles pour un dépôt).
- Vérifier que la Pull Request ne peut être fusionnée et qu'il n'est pas possible de pousser directement sur la branche de travail.
Tip
La vérification d'état s'attend à un job et non pas à un workflow. Dans l'exemple des corrections fournies plus haut, il s'agit de build-and-test.
Réponse
- Aller dans Settings > Rules > Rulesets.
- Cliquer sur New ruleset > New branch ruleset.
- Nommer la règle et la rendre active.
- Définir la branche sur laquelle les Pull Requests seront fusionnées.
- Cocher Require a pull request before merging.
- Cocher Require status checks to pass.
- Afficher les paramètres additionnels et ajouter la vérification à effectuer.
- Cliquer sur le bouton Create tout en bas.
- Les tests de l'application sont en échec. Consulter les logs du workflow et corriger les tests de l'application.
- Fusionner la Pull Request.
Réponse
compute.util.tsstatic minus(a: number, b: number): number {
return a - b;
}- Créer une nouvelle branche et la Pull Request associée et y ajouter le fichier de test de connexion à la base de données suivant :
import { Client } from "pg";
describe("Database", () => {
test("connection", async () => {
const database = new Client({
host: "127.0.0.1",
port: 5432,
database: "cicd_database",
user: "cicd_user",
password: "cicd_password",
});
const connected = await database
.connect()
.then(() => true)
.catch(() => false)
.finally(async () => await database.end());
expect(connected).toBe(true);
});
});- Ajouter un service PostgreSQL dans le workflow afin que le nouveau test testant la connexion à la base de données ne soit plus en échec (Documentation GitHub : Création de conteneurs de service PostgreSQL). Toutes les variables d'environnement à fournir ne sont pas sur la documentation de GitHub. Pour cela, se référer à la documentation du service Docker à initialiser (Docker Hub : PostgreSQL).
Réponse
my-workflow.yml...
jobs:
build-and-test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:latest
env:
POSTGRES_DB: "cicd_database"
POSTGRES_USER: "cicd_user"
POSTGRES_PASSWORD: "cicd_password"
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
...- Protéger les informations sensibles du workflow en utilisant des secrets (Documentation GitHub : Utilisation de secrets dans GitHub Actions).
Réponse
my-workflow.yml---
POSTGRES_DB: ${{ secrets.CICD_POSTGRES_DB }}
POSTGRES_USER: ${{ secrets.CICD_POSTGRES_USER }}
POSTGRES_PASSWORD: ${{ secrets.CICD_POSTGRES_PASSWORD }}- Aller dans Settings > Secrets and variables > Actions.
- Cliquer sur New repository secret.
- Saisir successivement les 3 secrets :
- CICD_POSTGRES_DB :
cicd_database - CICD_POSTGRES_USER :
cicd_user - CICD_POSTGRES_PASSWORD :
cicd_password
- CICD_POSTGRES_DB :