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 :
- Les tests :
composer run test
. - La compilation des assets via NodeJS et webpack:
composer run materialstyle
. - La purge des caches avec wp-cli en tâche post-install et post-update.
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
.
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 met également à disposition des badges pour afficher sur le README.md :
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.
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.
De Cyril Rohr le 11 mai 2020
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 !