Nouveau : Datasets open source gratuits disponibles !Decouvrir →
⚙️
Intermediaire 25 min GitHub Actions

GitHub Actions : guide pratique

Pourquoi GitHub Actions : guide pratique ?

Contexte réel : pourquoi un dev a besoin de ca au quotidien

GitHub Actions est une outil puissant et flexible qui permet aux développeurs d'automatiser leurs workflows, des tests automatisés jusqu'à la mise en production. C'est essentiel pour garantir que le code fonctionne correctement à chaque étape du processus de développement. Un cas d'utilisation concret serait : vous avez un projet open source et vous voulez s'assurer que toutes les nouvelles contributions passent par des tests automatisés avant d'être fusionnées dans la branche principale.

Prerequis

  • Connaissances en gestion de projets avec Git
  • Familiarité avec le langage de programmation du projet (ex: JavaScript, Python)
  • Compréhension de l'environnement de développement local et cloud

Outils à installer (versions)

  • Git : 2.34.1 ou ultérieur
  • Node.js : 16.14.0 ou ultérieur (pour les projets JavaScript)
  • Python : 3.9.7 ou ultérieur (pour les projets Python)

Concepts fondamentaux

Workflow

Un workflow est une série d'étapes définies par un développeur qui sont exécutées dans le cloud à chaque événement spécifique déclenché, comme la création d'une pull request ou le push d'un commit.

## workflows/hello-world.yml
name: Hello World

on:
  push:
    branches: [ main ]

Job

Un job est un ensemble de tâches qui sont exécutées en parallèle et sont associés à un événement spécifique. Par exemple, vous pouvez avoir deux jobs : l'un pour les tests locaux et l'autre pour la mise en production.

## workflows/hello-world.yml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

Step

Une étape est une tâche spécifique qui peut être exécutée dans un job. Par exemple, vous pouvez installer des dépendances avec npm install ou exécuter des tests avec npm test.

## workflows/hello-world.yml
      - name: Install dependencies
        run: npm install

Action

Une action est une composante réutilisable qui peut être ajoutée à un workflow. Par exemple, l'action actions/checkout permet de récupérer le code du dépôt.

## workflows/hello-world.yml
      - name: Checkout code
        uses: actions/checkout@v2

Mise en pratique : projet fil rouge

Mini-projet complet et réaliste : un gestionnaire de tâches basé sur Node.js

  1. Initialisation du projet

    mkdir task-manager
    cd task-manager
    npm init -y
    npm install express body-parser dotenv
    
  2. Création des fichiers et dossiers

    /task-manager
      ├── .env
      ├── server.js
      └── routes/
          └── tasks.js
    
  3. Configuration de l'environnement (/.env)

    PORT=3000
    DB_URL=mongodb://localhost:27017/task-manager
    
  4. Création du serveur (/server.js)

    // server.js
    const express = require('express');
    const bodyParser = require('body-parser');
    const tasksRouter = require('./routes/tasks');
    
    const app = express();
    const port = process.env.PORT || 3000;
    
    app.use(bodyParser.json());
    app.use('/api/tasks', tasksRouter);
    
    app.listen(port, () => {
      console.log(`Server is running on port ${port}`);
    });
    
  5. Création des routes pour les tâches (/routes/tasks.js)

    // routes/tasks.js
    const express = require('express');
    const router = express.Router();
    const Task = require('../models/task');
    
    router.get('/', async (req, res) => {
      try {
        const tasks = await Task.find();
        res.json(tasks);
      } catch (error) {
        res.status(500).json({ message: error.message });
      }
    });
    
    router.post('/', async (req, res) => {
      const task = new Task({
        title: req.body.title,
        description: req.body.description
      });
    
      try {
        const newTask = await task.save();
        res.status(201).json(newTask);
      } catch (error) {
        res.status(400).json({ message: error.message });
      }
    });
    
    module.exports = router;
    
  6. Création du modèle de tâche (/models/task.js)

    // models/task.js
    const mongoose = require('mongoose');
    
    const taskSchema = new mongoose.Schema({
      title: {
        type: String,
        required: true,
        trim: true
      },
      description: {
        type: String,
        trim: true
      }
    });
    
    const Task = mongoose.model('Task', taskSchema);
    module.exports = Task;
    
  7. Création du workflow (/.github/workflows/nodejs.yml)

    # .github/workflows/nodejs.yml
    name: Node.js CI
    
    on:
      push:
        branches: [ main ]
      pull_request:
        branches: [ main ]
    
    jobs:
      build:
    
        runs-on: ubuntu-latest
    
        strategy:
          matrix:
            node-version: [14.x, 16.x]
    
        steps:
          - uses: actions/checkout@v2
          - name: Use Node.js $matrix.node-version
            uses: actions/setup-node@v2
            with:
              node-version: $matrix.node-version
    
          - run: npm install
          - run: npm test
    

Mise en pratique : workflow pour une API de blog

  1. Initialisation du projet

    mkdir blog-api
    cd blog-api
    npm init -y
    npm install express body-parser mongoose
    
  2. Création des fichiers et dossiers

    /blog-api
      ├── .env
      ├── server.js
      └── routes/
          └── posts.js
    
  3. Configuration de l'environnement (/.env)

    PORT=3000
    DB_URL=mongodb://localhost:27017/blog-api
    
  4. Création du serveur (/server.js)

    // server.js
    const express = require('express');
    const bodyParser = require('body-parser');
    const postsRouter = require('./routes/posts');
    
    const app = express();
    const port = process.env.PORT || 3000;
    
    app.use(bodyParser.json());
    app.use('/api/posts', postsRouter);
    
    app.listen(port, () => {
      console.log(`Server is running on port ${port}`);
    });
    
  5. Création des routes pour les articles (/routes/posts.js)

    // routes/posts.js
    const express = require('express');
    const router = express.Router();
    const Post = require('../models/post');
    
    router.get('/', async (req, res) => {
      try {
        const posts = await Post.find();
        res.json(posts);
      } catch (error) {
        res.status(500).json({ message: error.message });
      }
    });
    
    router.post('/', async (req, res) => {
      const post = new Post({
        title: req.body.title,
        content: req.body.content
      });
    
      try {
        const newPost = await post.save();
        res.status(201).json(newPost);
      } catch (error) {
        res.status(400).json({ message: error.message });
      }
    });
    
    module.exports = router;
    
  6. Création du modèle d'article (/models/post.js)

    // models/post.js
    const mongoose = require('mongoose');
    
    const postSchema = new mongoose.Schema({
      title: {
        type: String,
        required: true,
        trim: true
      },
      content: {
        type: String,
        required: true,
        trim: true
      }
    });
    
    const Post = mongoose.model('Post', postSchema);
    module.exports = Post;
    
  7. Création du workflow (/.github/workflows/nodejs.yml)

    # .github/workflows/nodejs.yml
    name: Node.js CI
    
    on:
      push:
        branches: [ main ]
      pull_request:
        branches: [ main ]
    
    jobs:
      build:
    
        runs-on: ubuntu-latest
    
        strategy:
          matrix:
            node-version: [14.x, 16.x]
    
        steps:
          - uses: actions/checkout@v2
          - name: Use Node.js $matrix.node-version
            uses: actions/setup-node@v2
            with:
              node-version: $matrix.node-version
    
          - run: npm install
          - run: npm test
    

Erreurs fréquentes et debugging

Erreur 1 : Error: ENOENT: no such file or directory, open '/path/to/file.js'

Code incorrect :

// server.js
const express = require('express');
const bodyParser = require('body-parser');
const tasksRouter = require('./routes/tasks');

const app = express();
const port = process.env.PORT || 3000;

app.use(bodyParser.json());
app.use('/api/tasks', tasksRouter);

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Code correct :

// server.js
const express = require('express');
const bodyParser = require('body-parser');
const tasksRouter = require('./routes/tasks');

const app = express();
const port = process.env.PORT || 3000;

app.use(bodyParser.json());
app.use('/api/tasks', tasksRouter);

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Erreur 2 : Error: EACCES: permission denied, open '/path/to/file.js'

Code incorrect :

// server.js
const express = require('express');
const bodyParser = require('body-parser');
const tasksRouter = require('./routes/tasks');

const app = express();
const port = process.env.PORT || 3000;

app.use(bodyParser.json());
app.use('/api/tasks', tasksRouter);

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Code correct :

// server.js
const express = require('express');
const bodyParser = require('body-parser');
const tasksRouter = require('./routes/tasks');

const app = express();
const port = process.env.PORT || 3000;

app.use(bodyParser.json());
app.use('/api/tasks', tasksRouter);

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Erreur 3 : Error: Cannot find module 'express'

Code incorrect :

// server.js
const express = require('express');
const bodyParser = require('body-parser');
const tasksRouter = require('./routes/tasks');

const app = express();
const port = process.env.PORT || 3000;

app.use(bodyParser.json());
app.use('/api/tasks', tasksRouter);

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Code correct :

// server.js
const express = require('express');
const bodyParser = require('body-parser');
const tasksRouter = require('./routes/tasks');

const app = express();
const port = process.env.PORT || 3000;

app.use(bodyParser.json());
app.use('/api/tasks', tasksRouter);

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Pour aller plus loin

  1. Travailler avec des environnements de développement cloud : Vous pouvez utiliser GitHub Actions pour exécuter vos workflows sur des environnements de développement cloud tels que AWS, Azure ou Google Cloud.

  2. Utiliser des actions personnalisées : Créez vos propres actions pour répondre à des besoins spécifiques de votre projet et les partager avec la communauté GitHub.

  3. Intégrer des tests d'intégration et de bout en bout : Ajoutez des workflows pour exécuter des tests d'intégration et de bout en bout sur chaque push ou pull request.

Défi pratique : Créer un scraper simple avec GitHub Actions

  1. Initialisation du projet

    mkdir web-scraper
    cd web-scraper
    npm init -y
    npm install axios cheerio
    
  2. Création des fichiers et dossiers

    /web-scraper
      ├── index.js
    
  3. Scraping avec Axios et Cheerio (/index.js)

    // index.js
    const axios = require('axios');
    const cheerio = require('cheerio');
    
    async function scrape() {
      try {
        const { data } = await axios.get('https://example.com');
        const $ = cheerio.load(data);
        const title = $('title').text();
        console.log(`Title: ${title}`);
      } catch (error) {
        console.error(error);
      }
    }
    
    scrape();
    
  4. Création du workflow (/.github/workflows/nodejs.yml)

    # .github/workflows/nodejs.yml
    name: Node.js CI
    
    on:
      push:
        branches: [ main ]
      pull_request:
        branches: [ main ]
    
    jobs:
      build:
    
        runs-on: ubuntu-latest
    
        strategy:
          matrix:
            node-version: [14.x, 16.x]
    
        steps:
          - uses: actions/checkout@v2
          - name: Use Node.js $matrix.node-version
            uses: actions/setup-node@v2
            with:
              node-version: $matrix.node-version
    
          - run: npm install
          - run: node index.js
    

Explication des concepts :

  • Workflow : Un workflow est une série d'étapes définies par un développeur qui sont exécutées dans le cloud à chaque événement spécifique déclenché, comme la création d'une pull request ou le push d'un commit.
  • Job : Un job est un ensemble de tâches qui sont exécutées en parallèle et sont associés à un événement spécifique. Par exemple, vous pouvez avoir deux jobs : l'un pour les tests locaux et l'autre pour la mise en production.
  • Step : Une étape est une tâche spécifique qui peut être exécutée dans un job. Par exemple, vous pouvez installer des dépendances avec npm install ou exécuter des tests avec npm test.
  • Action : Une action est une composante réutilisable qui peut être ajoutée à un workflow. Par exemple, l'action actions/checkout permet de récupérer le code du dépôt.

Utilisation des actions personnalisées

  1. Création d'une action personnalisée (/.github/actions/hello-world-action) :

    /web-scraper
      ├── index.js
      └── .github/
          └── actions/
              └── hello-world-action
                  ├── entrypoint.sh
                  └── Dockerfile
    
  2. Contenu de entrypoint.sh :

    #!/bin/bash
    echo "Hello, GitHub Actions!"
    
  3. Contenu de Dockerfile :

    FROM ubuntu:latest
    
    RUN apt-get update && \
        apt-get install -y curl
    
    COPY entrypoint.sh /entrypoint.sh
    RUN chmod +x /entrypoint.sh
    
    ENTRYPOINT ["/entrypoint.sh"]
    
  4. Utilisation de l'action personnalisée dans un workflow (/.github/workflows/nodejs.yml) :

    name: Node.js CI
    
    on:
      push:
        branches: [ main ]
      pull_request:
        branches: [ main ]
    
    jobs:
      build:
    
        runs-on: ubuntu-latest
    
        steps:
          - uses: actions/checkout@v2
          - name: Use Node.js $matrix.node-version
            uses: actions/setup-node@v2
            with:
              node-version: $matrix.node-version
    
          - run: npm install
          - run: node index.js
    
          - name: Run Hello World Action
            uses: ./.github/actions/hello-world-action
    

Intégration des tests d'intégration et de bout en bout

  1. Création des fichiers pour les tests (/tests) :

    /web-scraper
      ├── index.js
      └── tests/
          ├── integration.test.js
          └── e2e.test.js
    
  2. Contenu de integration.test.js :

    const axios = require('axios');
    
    describe('Integration Tests', () => {
      it('should get a valid response from the server', async () => {
        const { status } = await axios.get('http://localhost:3000/api/posts');
        expect(status).toBe(200);
      });
    });
    
  3. Contenu de e2e.test.js :

    const axios = require('axios');
    
    describe('End-to-End Tests', () => {
      it('should create a new post and get it back', async () => {
        const response = await axios.post('http://localhost:3000/api/posts', {
          title: 'Test Post',
          content: 'This is a test post.'
        });
        expect(response.status).toBe(201);
        const { data } = response;
        const getResponse = await axios.get(`http://localhost:3000/api/posts/${data._id}`);
        expect(getResponse.status).toBe(200);
      });
    });
    
  4. Ajout des tests au workflow (/.github/workflows/nodejs.yml) :

    name: Node.js CI
    
    on:
      push:
        branches: [ main ]
      pull_request:
        branches: [ main ]
    
    jobs:
      build:
    
        runs-on: ubuntu-latest
    
        strategy:
          matrix:
            node-version: [14.x, 16.x]
    
        steps:
          - uses: actions/checkout@v2
          - name: Use Node.js $matrix.node-version
            uses: actions/setup-node@v2
            with:
              node-version: $matrix.node-version
    
          - run: npm install
          - run: npm test
    

Explication des concepts :

  • Workflow : Un workflow est une série d'étapes définies par un développeur qui sont exécutées dans le cloud à chaque événement spécifique déclenché, comme la création d'une pull request ou le push d'un commit.
  • Job : Un job est un ensemble de tâches qui sont exécutées en parallèle et sont associés à un événement spécifique. Par exemple, vous pouvez avoir deux jobs : l'un pour les tests locaux et l'autre pour la mise en production.
  • Step : Une étape est une tâche spécifique qui peut être exécutée dans un job. Par exemple, vous pouvez installer des dépendances avec npm install ou exécuter des tests avec npm test.
  • Action : Une action est une composante réutilisable qui peut être ajoutée à un workflow. Par exemple, l'action actions/checkout permet de récupérer le code du dépôt.

Utilisation des actions personnalisées

  1. Création d'une action personnalisée (/.github/actions/hello-world-action) :

    /web-scraper
      ├── index.js
      └── .github/
          └── actions/
              └── hello-world-action
                  ├── entrypoint.sh
                  └── Dockerfile
    
  2. Contenu de entrypoint.sh :

    #!/bin/bash
    echo "Hello, GitHub Actions!"
    
  3. Contenu de Dockerfile :

    FROM ubuntu:latest
    
    RUN apt-get update && \
        apt-get install -y curl
    
    COPY entrypoint.sh /entrypoint.sh
    RUN chmod +x /entrypoint.sh
    
    ENTRYPOINT ["/entrypoint.sh"]
    
  4. Utilisation de l'action personnalisée dans un workflow (/.github/workflows/nodejs.yml) :

    name: Node.js CI
    
    on:
      push:
        branches: [ main ]
      pull_request:
        branches: [ main ]
    
    jobs:
      build:
    
        runs-on: ubuntu-latest
    
        steps:
          - uses: actions/checkout@v2
          - name: Use Node.js $matrix.node-version
            uses: actions/setup-node@v2
            with:
              node-version: $matrix.node-version
    
          - run: npm install
          - run: node index.js
    
          - name: Run Hello World Action
            uses: ./.github/actions/hello-world-action
    

Intégration des tests d'intégration et de bout en bout

  1. Création des fichiers pour les tests (/tests) :

    /web-scraper
      ├── index.js
      └── tests/
          ├── integration.test.js
          └── e2e.test.js
    
  2. Contenu de integration.test.js :

    const axios = require('axios');
    
    describe('Integration Tests', () => {
      it('should get a valid response from the server', async () => {
        const { status }
    

Besoin d'aide sur GitHub Actions ?

Besoin d'aide sur un projet technique ? Decrivez-le pour des conseils personnalises.

Recevoir des conseils

Questions frequentes

Qu'est-ce que GitHub Actions ?
GitHub Actions est un outil d'intégration continue et de déploiement automatisés qui permet aux développeurs de configurer des workflows pour construire, tester et déployer leur code directement sur GitHub.
Comment créer un workflow GitHub Actions ?
Pour créer un workflow GitHub Actions, vous devez créer un fichier YAML dans le répertoire `.github/workflows` de votre dépôt. Ce fichier définit les étapes du processus d'intégration continue et de déploiement que vous souhaitez automatiser.
Quels sont les triggers disponibles pour GitHub Actions ?
GitHub Actions propose plusieurs types de triggers, tels que des événements sur la création ou la fusion de pull requests, des déclenchements manuels via l'interface utilisateur, et des horizons spécifiques comme le déclenchement à chaque push sur une branche.

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.