GitHub Actions pour déployer son WordPress avec Deployer

Les GitHub Actions

GitHub a depuis quelques semaines mis à disposition pour tous son système de pipelines : les GitHub Actions.

Dans un précédent article je vous avais décrit comment je déploie ce blog via Deployer. Jusqu’à présent, même si mon code était bien hébergé chez GitHub, je poussais encore en prod’ en lançant la commande depuis mon poste :

php vendor/bin/dep deploy production -v

J’ai profité de la fonctionnalité de pipeline intégrée à GitHub, pour remédier à cela et avoir un système 100% automatisé avec déploiement automatique de mon code poussé sur GitHub.

WordPress & Composer

Tout d’abord rappelons que mon blog a quelques particularités comme par exemple celle de gérer les dépendances depuis composer (sauf les extensions afin de permettre leur mise à jour automatique) :

{
  "name": "llaumgui/blog.kulakowski-fr",
  "description": "Project to build https://blog.kulakowski.fr/",
  "type": "project",
  "license": "proprietary",
  "keywords": [],
  "minimum-stability": "dev",
  "authors": [{
      "name": "Guillaume Kulakowski",
      "email": "guillaume@kulakowski.fr",
      "homepage": "https://blog.kulakowski.fr/"
  }],
  "repositories": [
    {
      "type": "composer",
      "url": "https://wpackagist.org"
    },
    {
      "type": "package",
      "package": {
        "name": "WordPress/WordPress",
        "version": "5.4.1",
        "dist": {
          "url": "https://github.com/WordPress/WordPress/archive/5.4.1.zip",
          "type": "zip"
        },
        "source": {
          "url": "https://github.com/WordPress/WordPress.git",
          "type": "git",
          "reference": "5.4.1"
        },
        "type": "wordpress-core",
        "require": {
          "johnpbloch/wordpress-core-installer": "^1.0"
        },
        "extra": {
          "wordpress-install-dir": "web/wp"
        }
      }
    }
  ],
  "require": {
    "php": ">=7.2",
    "composer/installers": "^1.9",
    "wordpress/wordpress": "^5.4.1",
    "timber/timber": "^1",
    "wpackagist-theme/twentytwenty": "^1.2",
    "wp-cli/wp-cli-bundle": "^2.1@dev"
  },
  "require-dev": {
    "squizlabs/php_codesniffer": "3.*",
    "deployer/deployer": "^6.7",
    "deployer/recipes": "dev-master"
  },
  "config": {
    "vendor-dir": "vendor",
    "preferred-install": "dist"
  },
  "extra": {
    "installer-paths": {
      "web/wp-content/themes/{$name}/": ["type:wordpress-theme"]
    },
    "patches": {
    }
  },
  "scripts": {
    "post-install-cmd": "@wordpress-scripts",
    "post-update-cmd": "@wordpress-scripts",
    "wordpress-scripts": [
      "Llaumgui\\WpBootstrap\\ScriptHandler::wpCleanup",
	    "@cache:clear"
    ],
    "cache:clear": [
      "@wpcache:timber"
  	],
    "wpcache:flush": [
      "vendor/bin/wp --path=web/wp cache flush"
  	],
    "wpcache:timber": [
      "vendor/bin/wp --path=web/wp eval 'Timber\\Integrations\\Command::clear_cache();'"
  	],
    "materialstyle": [
      "cd web/wp-content/themes/materialstyle && npm run-script build"
    ],
    "test:phpcs": "vendor/bin/phpcs",
    "test:twig": "vendor/bin/twig-lint lint web/wp-content/themes/materialstyle/templates",
    "test": [
      "@test:phpcs"
    ]
  },
  "autoload": {
    "psr-4": {
      "Llaumgui\\MaterialStyle\\": "web/wp-content/themes/materialstyle/lib",
      "Llaumgui\\WpBootstrap\\": "src"
    }
  }
}

On remarque que composer gère également :

Gestion des secrets pour GitHub Actions

Pour déployer à partir de GitHub, il va falloir lui donner quelques informations « secrètes« . Ceci passe par l’onglet Secrets de la configuration de votre projet. Les secrets dans mon cas vont concerner :

  • Les chemins vers mon blog : DEP_PATH.
  • Le hash de mon hook pour les notifications Slack : DEP_SLACK_WEBHOOK.
  • Les différentes clefs pour s’authentifier à mon serveur : SSH_PRIVATE_KEY & SSH_KNOWN_HOSTS.
Exemple de secret dans GitHub

Notons qu’avant, toutes ces informations étaient portées dans le fichier hosts.yml de mon projet.

Pour rappel: pour déterminer le SSH_KNOWN_HOSTS, il suffit de taper la commande :

ssh-keyscan rsa -t SERVERNAME

Modification de la configuration de Deployer

Comme évoqué plus haut, avant, toutes les informations sur la cible étaient portées par le fichier host.yml. Maintenant, il faut aller chercher les information à partir des secrets GitHub. J’ai donc modifié ma configuration de deployer pour ne plus passer par un fichier de configuration mais pas des variables d’environnement :

set('deploy_path', getenv('DEP_PATH'));

Au complet ça donne ceci, pour plus d’explications, voir mon précédent article.

<?php
namespace Deployer;

require 'recipe/wordpress.php'; // Use WordPress task.
require 'recipe/slack.php';
require 'recipe/cachetool.php';
require 'recipe/rsync.php';

// Project name
set('application', 'blog.kulakowski.fr');

// Project repository
set('repository', 'git@github.com:llaumgui/blog.kulakowski-fr.git');

// Shared files/dirs between deploys
set('shared_files', [
    'web/wp-config.php',
    'web/wp-content/advanced-cache.php',
    'web/wp-content/object-cache.php',
    'web/wp-content/wp-cache-config.php'
]);
set('shared_dirs', [
    'web/wp-content/cache',
    'web/wp-content/languages',
    'web/wp-content/plugins',
    'web/wp-content/public',
    'web/wp-content/uploads'
]);

// Writable dirs by web server
set('writable_dirs', [
    'web/wp-content',
    'web/wp-content/uploads'
]);

// Set path
set('materialstyle_path', 'web/wp-content/themes/materialstyle');

// Set docker container
set('container_name', 'php');
set('container_uid', '82');
set('container_gid', '82');

// Add composer in more of wordpress default tasks
after('deploy:shared', 'deploy:vendors');

// Hosts
set('slack_webhook', getenv('DEP_SLACK_WEBHOOK'));
set('cachetool', '127.0.0.1:9000');
set('deploy_path', getenv('DEP_PATH'));
host('yorktown.kulakowski.fr')
  ->user('deployer')
  ->port(22)
  ->stage('production');


// [Optional] If deploy fails automatically unlock.
after('deploy:failed', 'deploy:unlock');


// ----------------------------------------------------------------- Task: build
desc('Build assets with webpack');
task('build', function () {
    run('composer run-script materialstyle');
    run('echo "Build done !"');
})->local();
after('deploy:prepare', 'build');


// ---------------------------------------------- Override task: deploy:writable
task('deploy:writable', function () {
    $dirs = get('writable_dirs');
    foreach ($dirs as $dir) {
        run('docker exec -i {{container_name}} chown {{container_uid}}:{{container_gid}} {{ release_path }}/' . $dir);
    }
});


// ------------------------------------------------------------- Task: cachetool
set('bin/cachetool', 'cachetool.phar');
after('deploy:symlink', 'cachetool:clear:opcache');

// ----------------------------------------------------------------- Task: slack
before('deploy', 'slack:notify');
after('success', 'slack:notify:success');
after('deploy:failed', 'slack:notify:failure');

// ----------------------------------------------------------------- Task: rsync
set('rsync_src', __DIR__ . '/{{ materialstyle_path }}/dist');
set('rsync_dest', '{{ release_path }}/{{ materialstyle_path }}/dist');
after('deploy:update_code', 'rsync');

Le fichier de configuration de GitHub Actions

Maintenant qu’on a modifié la configuration de Deployer pour récupérer les secrets de GitHub et que toutes les tâches sont centralisées dans le composer.json, il ne reste plus qu’à rédiger l’action au format YAML. Cette action devra :

  • Lancer les tests quelque soit la branche,
  • Pousser en production lorsque la branche est master.

Pour cela je me suis basé sur des actions déjà existantes. En effet, l’une des forces des GitHub Actions est de pouvoir utiliser des actions réutilisables, personnelles ou mises à disposition par la communauté.

  • actions/checkout: cette action sert à récupérer le code source depuis le dépôt GIT.
  • shivammathur/setup-php : permet de mettre en place un environnement PHP. Dans mon cas, j’utilise PHP 7.4.
  • actions/setup-node : permet de mettre en place un environnement NodeJS. C’est cet environnement qui va me servir à construire les CSS et JS du site via webpack.
  • atymic/deployer-php-action : cette action permet de mettre en place l’environnement Deployer. Attention elle ne fait pas le déploiement, c’est à faire ensuite.
name: Blog CI/CD

on:
  [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - name: Git checkout
      uses: actions/checkout@v2

    - name: Setup PHP
      uses: shivammathur/setup-php@master
      with:
        php-version: 7.4

    - name: Validate composer.json and composer.lock
      run: composer validate

    - name: Install dependencies
      run: composer install --prefer-dist --no-progress --no-suggest --no-scripts

    - name: Run test suite
      run: composer run-script test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/master'
    steps:
    - name: Git checkout
      uses: actions/checkout@v2

    - name: Setup PHP
      uses: shivammathur/setup-php@master
      with:
        php-version: 7.4
        
    - name: Install PHP dependencies
      run: composer install --prefer-dist --no-progress --no-suggest --no-scripts
    
    - name: Use Node.js
      uses: actions/setup-node@v1
      with:
        node-version: '12.x'
    - name: Install NodeJS dependencies
      run: cd web/wp-content/themes/materialstyle && npm install
      env:
        CI: true
        
    - name: Setup deployment on production
      uses: atymic/deployer-php-action@0.1.0
      with:
        ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
        ssh-known-hosts: ${{ secrets.SSH_KNOWN_HOSTS }}
        
    - name: Deploy
      env:
        DEP_SLACK_WEBHOOK: ${{ secrets.DEP_SLACK_WEBHOOK }}
        DEP_PATH: ${{ secrets.DEP_PATH }}
      run: php vendor/bin/dep deploy production -v
    

Au final

On a une vue avec les Actions lancées depuis le projet et la possibilité de vérifier que tout s’est bien passé. Il est aussi possible de relancer des tâches. Pour illustrer cela, on voit ci-dessous que les modifications de ma branche master sont bien poussées en production :

GitHub Action
GitHub Action CI/C sur le blog

GitHub met également à disposition des badges pour afficher sur le README.md :

La page GitHub du blog
Le fichier README de la page GitHub du blog

Mon avis sur les GitHub Actions VS les pipelines de GitLab

L’une des forces de GitHub Actions par rapport à d’autres systèmes de pipeline et la ré-utilisabilité et le partage d’actions via la syntaxe uses. Il y a même une market place pour les échanger. Comme vous pouvez le voir, j’utilise des actions officielles (namespace actions) mais aussi issues de la communauté.

Malheureusement les forces s’arrêtent là, si on compare aux pipelines de GitLab, le principal concurrent :

  • GitLab permet d’utiliser des containers Docker pris directement depuis le hub, là où GitHub se limite à quelques containers déjà établis. C’est super limitant.
  • L’interface de GitHub manque de clarté en mon sens surtout si on la compare à celle de GitLab.
Pipeline Gitlab de Fedora-Fr
Exemple de pipeline Gitlab sur Fedora-Fr

Les GitHub Actions sont disponibles en bêta depuis l’année dernière mais officiellement pour tous depuis quelques mois. Je pense que cette fonctionnalité devrait accueillir pas mal d’évolutions et de nouveautés dans les mois à venir pour mettre GitHub au niveau des concurrents.

Commentaires

Cyril Rohr

Très intéressant, et en français c'est cool ! J'ai un peu le même retour que toi sur les GitHub Actions: très prometteur, mais encore pas mal de choses à fignoler côté UI et documentation. Comme tout le monde réinvente plus ou moins son système de déploiement à base d'actions, j'ai aussi fait le mien sur https://pullpreview.com.

C'est open-source (mais pas gratuit) et ca permet de déployer des environnements de preview pour n'importe quel type d'application simplement en appliquant un label sur une pull request. Le déploiement de branches spécifiques est aussi supporté.

Je serais intéressé par ton retour si tu as l'occasion de regarder un peu !

Les commentaires pour ce poste sont fermés.

Réseaux sociaux