Comment utiliser Node.js et Github Webhooks pour garder les projets distants synchronisés

introduction

Lorsque vous travaillez sur un projet avec plusieurs développeurs, il peut être frustrant lorsqu'une personne insère dans un référentiel puis qu'une autre commence à apporter des modifications à une version obsolète du code. Des erreurs comme celles-ci coûtent du temps, ce qui rend intéressant la mise en place d'un script pour synchroniser vos référentiels. Vous pouvez également appliquer cette méthode dans un environnement de production pour envoyer rapidement les correctifs et autres modifications.

Tandis que d'autres solutions existent pour effectuer cette tâche spécifique, l'écriture de votre propre script est une option flexible qui laisse de la place pour la personnalisation à l'avenir.

GitHub vous permet de configurerwebhooks pour vos référentiels, qui sont des événements qui envoient des requêtes HTTP lorsque des événements se produisent. Par exemple, vous pouvez utiliser un Webhook pour vous avertir lorsque quelqu'un crée une demande d'extraction ou envoie un nouveau code.

Dans ce guide, vous développerez un serveurNode.js qui écoute une notification de webhook GitHub chaque fois que vous ou quelqu'un d'autre transmettez du code à GitHub. Ce script met automatiquement à jour un référentiel sur un serveur distant avec la version la plus récente du code, éliminant ainsi la nécessité de se connecter à un serveur pour extraire de nouveaux commits.

Conditions préalables

Pour compléter ce tutoriel, vous aurez besoin de:

  • Un serveur Ubuntu 16.04 configuré en suivantthe Ubuntu 16.04 initial server setup guide, y compris un utilisateur non root avec les privilègessudo et un pare-feu.

  • Git installé sur votre machine locale. Vous pouvez suivre le tutorielContributing to Open Source: Getting Started with Git pour installer et configurer Git sur votre ordinateur.

  • Node.js and npm installed on the remote server using the official PPA, as explained explained in How To Install Node.js on Ubuntu 16.04. L'installation de la version distro-stable est suffisante car elle nous fournit la version recommandée sans configuration supplémentaire.

  • Un référentiel sur Github qui contient votre code de projet. Si vous n’avez pas de projet en tête, n'hésitez pas à utiliserfork this example que nous utiliserons dans le reste du didacticiel.

[[step-1 -—- setting-up-a-webhook]] == Étape 1 - Configuration d'un Webhook

Nous allons commencer par configurer un Webhook pour votre référentiel. Cette étape est importante car sans elle, Github ne sait pas quels événements envoyer lorsque des événements se produisent, ni où les envoyer. Nous allons d’abord créer le Webhook, puis le serveur qui répondra à ses demandes.

Connectez-vous à votre compte GitHub et accédez au référentiel que vous souhaitez surveiller. Cliquez sur l'ongletSettings dans la barre de menu supérieure de la page de votre référentiel, puis cliquez surWebhooks dans le menu de navigation de gauche. Cliquez surAdd Webhook dans le coin droit et entrez le mot de passe de votre compte si vous y êtes invité. Vous verrez une page qui ressemble à ceci:

Webhooks Page

  • Dans le champPayload URL, entrezhttp://your_server_ip:8080. C’est l’adresse et le port du serveur Node.js que nous écrirons bientôt.

  • Changez leContent type enapplication/json. Le script que nous écrirons attendra des données JSON et ne pourra pas comprendre d’autres types de données.

  • PourSecret, entrez un mot de passe secret pour ce webhook. Vous utiliserez ce secret dans votre serveur Node.js pour valider les requêtes et vous assurer qu'elles proviennent de GitHub.

  • PourWhich events would you like to trigger this webhook, sélectionnezjust the push event. Nous n'avons besoin que de l'événement push car c'est à ce moment que le code est mis à jour et doit être synchronisé avec notre serveur.

  • Cochez la caseActive.

  • Vérifiez les champs et cliquez surAdd webhook pour le créer.

Le ping échouera au début, mais soyez assuré que votre Webhook est maintenant configuré. Maintenant, clonons le référentiel sur le serveur.

[[step-2 -—- cloning-the-repository-to-the-server]] == Étape 2 - Clonage du référentiel sur le serveur

Notre script peut mettre à jour un référentiel, mais il ne peut pas configurer initialement le référentiel. Nous le ferons donc maintenant. Connectez-vous à votre serveur:

ssh sammy@your_server_ip

Assurez-vous d'être dans votre répertoire personnel. Ensuite, utilisez Git pour cloner votre référentiel. Assurez-vous de remplacersammy par votre nom d'utilisateur GitHub ethello_hapi par le nom de votre projet Github.

cd
git clone https://github.com/sammy/hello_hapi.git

Cela créera un nouveau répertoire contenant votre projet. Vous utiliserez ce répertoire à l’étape suivante.

Avec votre projet cloné, vous pouvez créer le script webhook.

[[step-3 -—- creating-the-webhook-script]] == Étape 3 - Création du script Webhook

Créons notre serveur pour écouter les requêtes Webhook de GitHub. Nous allons écrire un script Node.js qui lance un serveur Web sur le port8080. Le serveur écoute les demandes du Webhook, vérifie le secret que nous avons spécifié et extrait la dernière version du code de GitHub.

Accédez à votre répertoire personnel:

cd ~

Créez un nouveau répertoire pour votre script webhook appeléNodeWebhooks:

mkdir ~/NodeWebhooks

Ensuite, accédez au nouveau répertoire:

cd ~/NodeWebhooks

Créez un nouveau fichier appeléwebhook.js dans le répertoireNodeWebhooks.

nano webhook.js

Ajoutez ces deux lignes au script:

webhook.js

var secret = "your_secret_here";
var repo = "/home/sammy/hello_hapi";

La première ligne définit une variable qui contient le secret que vous avez créé à l'étape 1 et qui vérifie que les demandes proviennent de GitHub. La deuxième ligne définit une variable qui contient le chemin complet du référentiel que vous souhaitez mettre à jour sur votre disque local. Cela devrait pointer vers le référentiel que vous avez extrait à l'étape 2.

Ensuite, ajoutez ces lignes qui importent les bibliothèqueshttp etcrypto dans le script. Nous allons les utiliser pour créer notre serveur Web et hacher le secret afin que nous puissions le comparer à ce que nous recevons de GitHub:

webhook.js

let http = require('http');
let crypto = require('crypto');

Ensuite, incluez la bibliothèquechild_process afin de pouvoir exécuter des commandes shell à partir de votre script:

webhook.js

const exec = require('child_process').exec;

Ensuite, ajoutez ce code pour définir un nouveau serveur Web qui gère les demandes de Webhook GitHub et extrait la nouvelle version du code s'il s'agit d'une demande authentique:

webhook.js

http.createServer(function (req, res) {
    req.on('data', function(chunk) {
        let sig = "sha1=" + crypto.createHmac('sha1', secret).update(chunk.toString()).digest('hex');

        if (req.headers['x-hub-signature'] == sig) {
            exec('cd ' + repo + ' && git pull');
        }
    });

    res.end();
}).listen(8080);

La fonctionhttp.createServer() démarre un serveur Web sur le port8080 qui écoute les requêtes entrantes de Github. Pour des raisons de sécurité, nous validons que le secret inclus dans la demande correspond à celui que nous avons spécifié lors de la création du WebHook à l'étape 1. Le secret est passé dans l'en-têtex-hub-signature sous la forme d'une chaîne hachée SHA1, donc nous hachons notre secret et le comparons à ce que GitHub nous envoie.

Si la demande est authentique, nous exécutons une commande shell pour mettre à jour notre référentiel local en utilisantgit pull.

Le script terminé ressemble à ceci:

webhook.js

const secret = "your_secret_here";
const repo = "~/your_repo_path_here/";

const http = require('http');
const crypto = require('crypto');
const exec = require('child_process').exec;

http.createServer(function (req, res) {
    req.on('data', function(chunk) {
        let sig = "sha1=" + crypto.createHmac('sha1', secret).update(chunk.toString()).digest('hex');

        if (req.headers['x-hub-signature'] == sig) {
            exec('cd ' + repo + ' && git pull');
        }
    });

    res.end();
}).listen(8080);

Si vous avez suivi le guide de configuration initiale du serveur, vous devrez autoriser ce serveur Web à communiquer avec le Web extérieur en autorisant le trafic sur le port8080:

sudo ufw allow 8080/tcp

Maintenant que notre script est en place, assurons-nous qu’il fonctionne correctement.

Étape 4 - Tester le Webhook

Nous pouvons tester notre webhook en utilisantnode pour l'exécuter dans la ligne de commande. Lancez le script et laissez le processus ouvert dans votre terminal:

cd ~/NodeWebhooks
nodejs webhook.js

Revenez à la page de votre projet surGithub.com. Cliquez sur l'ongletSettings dans la barre de menu supérieure de la page de votre référentiel, puis cliquez surWebhooks dans le menu de navigation de gauche. Cliquez surEdit à côté du webhook que vous avez configuré à l'étape 1. Faites défiler vers le bas jusqu'à ce que vous voyiez la sectionRecent Deliveries, comme indiqué dans l'image suivante:

Edit Webhook

Appuyez sur les trois points à l'extrême droite pour afficher le boutonRedeliver. Avec le serveur de nœuds en cours d'exécution, cliquez surRedeliver pour renvoyer la demande. Une fois que vous confirmez que vous souhaitez envoyer la demande, vous verrez une réponse réussie. Ceci est indiqué par un code de réponse200 OK après avoir renvoyé le ping.

Nous pouvons maintenant nous assurer que notre script s'exécute en arrière-plan et commence au démarrage. L'utilisation deCTRL+C arrête le serveur Webhook du nœud.

[[step-5 -—- Installing-the-webhook-as-a-systemd-service]] == Étape 5 - Installation du Webhook en tant que service Systemd

systemd est le gestionnaire de tâches qu'Ubuntu utilise pour contrôler les services. Nous allons configurer un service qui nous permettra de démarrer notre script webhook au démarrage et d'utiliser les commandes systemd pour le gérer, comme nous le ferions avec n'importe quel autre service.

Commencez par créer un nouveau fichier de service:

sudo nano /etc/systemd/system/webhook.service

Ajoutez la configuration suivante au fichier de service qui indique à systemd comment exécuter le script. Cela indique à Systemd où trouver notre script de noeud et décrit notre service.

Assurez-vous de remplacersammy par votre nom d'utilisateur.

/etc/systemd/system/webhook.service

[Unit]
Description=Github webhook
After=network.target

[Service]
Environment=NODE_PORT=8080
Type=simple
User=sammy
ExecStart=/usr/bin/nodejs /home/sammy/NodeWebhooks/webhook.js
Restart=on-failure

[Install]
WantedBy=multi-user.target

Activez le nouveau service pour qu’il démarre au démarrage du système:

sudo systemctl enable webhook.service

Maintenant démarrez le service:

sudo systemctl start webhook

Assurez-vous que le service est démarré:

sudo systemctl status webhook

La sortie suivante indique que le service est actif:

Output● webhook.service - Github webhook
   Loaded: loaded (/etc/systemd/system/webhook.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2018-08-17 19:28:41 UTC; 6s ago
 Main PID: 9912 (nodejs)
    Tasks: 6
   Memory: 7.6M
      CPU: 95ms
   CGroup: /system.slice/webhook.service
           └─9912 /usr/bin/nodejs /home/sammy/NodeWebhooks/webhook.js

Vous pouvez maintenant envoyer de nouveaux commits dans votre référentiel et voir les modifications apportées sur votre serveur.

Sur votre ordinateur de bureau, clonez le référentiel:

git clone https://github.com/sammy/hello_hapi.git

Apportez une modification à l’un des fichiers du référentiel. Ensuite, validez le fichier et envoyez votre code à GitHub.

git add index.js
git commit -m "Update index file"
git push origin master

Le Webhook se déclenchera et vos modifications apparaîtront sur votre serveur.

Conclusion

Vous avez configuré un script Node.js qui déploiera automatiquement les nouveaux commits dans un référentiel distant. Vous pouvez utiliser ce processus pour configurer des référentiels supplémentaires que vous souhaitez surveiller. Vous pouvez même le configurer pour déployer un site Web ou une application en production lorsque vous poussez votre référentiel.