# Déploiement automatique : mise en place côté serveur Cette page s'adresse aux mainteneurs qui ont un accès SSH à `alpinux.org`. Les contributeurs n'ont rien à faire ici : leur travail s'arrête à la pull request. La chaîne est la même que celle du wiki et de www.alpinux.org : ``` push sur main → webhook Gitea → https://presentations.alpinux.org/deploy │ Apache relaie vers 127.0.0.1:9878 ▼ service d'écoute (signature HMAC vérifiée) ▼ deploy-presentations.sh : git pull → npm ci → build dans le staging → rsync ▼ https://presentations.alpinux.org ``` Si le build échoue — front matter invalide, image manquante — rien n'est copié : le site en ligne reste celui du dernier build réussi. --- ## Un second service plutôt qu'une nouvelle route Le service d'écoute du wiki (`/opt/wiki-mkdocs/webhook.py`) ne connaît qu'un secret et qu'un script. Y ajouter une route aurait demandé de le réécrire, de faire cohabiter deux secrets dans la même unité systemd, et de redémarrer le déploiement du wiki à chaque retouche de celui des présentations. Ce dépôt apporte donc **son propre service**, comme www.alpinux.org l'a fait avant lui. Les trois sont des copies du même petit programme : | Site | Service systemd | Port | Secret | |----------------|--------------------------|-------|-----------------------------------------| | wiki | `wiki-webhook` | 9876 | dans l'unité systemd | | www | `www-webhook` | 9877 | `/etc/default/www-webhook` | | présentations | `presentations-webhook` | 9878 | `/etc/default/presentations-webhook` | Chaque dépôt a **son** secret : celui des présentations n'ouvre pas le déploiement du wiki, et inversement. --- ## Ce qui est installé, et où | Quoi | Où | |-----------------------------------|----------------------------------------------------------| | Clone du dépôt | `/opt/presentations-alpinux/repo` | | Répertoire de staging | `/opt/presentations-alpinux/staging` | | Node.js (LTS, version figée) | `/opt/presentations-alpinux/node` | | Script de déploiement (copie) | `/opt/presentations-alpinux/deploy-presentations.sh` | | Service d'écoute (copie) | `/opt/presentations-alpinux/webhook.py` | | Unité systemd | `/etc/systemd/system/presentations-webhook.service` | | Secret HMAC (`0600`, root) | `/etc/default/presentations-webhook` | | Clé de déploiement, lecture seule | `~abonnelc/.ssh/id_ed25519_presentations_deploy` | | DocumentRoot | `/var/www/clients/client1/web22/web` | | Journal | `/var/log/presentations-deploy.log` | Fichiers fournis par ce dossier : [`installer.sh`](installer.sh), [`webhook.py`](webhook.py), [`presentations-webhook.service`](presentations-webhook.service), [`apache-vhost.exemple.conf`](apache-vhost.exemple.conf), et [`../scripts/deploy-presentations.sh`](../scripts/deploy-presentations.sh). **Aucun secret n'est dans le dépôt.** Le secret du webhook est tiré au hasard sur le serveur par `installer.sh` ; la clé privée de déploiement y est créée et n'en sort pas. --- ## Installation Tout tient dans `installer.sh`, à lancer en root. Il est sans danger de le relancer : chaque étape vérifie d'abord ce qui existe. ```bash sudo bash deploy/installer.sh ``` 1. **Node.js** — téléchargé depuis nodejs.org, somme SHA-256 vérifiée, déposé dans `/opt/presentations-alpinux/node`. Rien n'est ajouté aux dépôts apt du serveur, et pas de Chromium : le build HTML de Marp n'en a pas besoin. 2. **Clé de déploiement** — une clé SSH propre à ce dépôt, et un alias `gitea-presentations` dans `~abonnelc/.ssh/config`. 3. **Clone du dépôt.** La première fois, le script s'arrête ici et affiche la clé publique : la déclarer dans Gitea, *Paramètres → Clés de déploiement* du dépôt, **sans** l'accès en écriture, puis relancer. 4. **Scripts** — copie de `deploy-presentations.sh` et de `webhook.py` dans `/opt/presentations-alpinux`, création du journal. 5. **Droit d'écriture** sur le DocumentRoot, par une ACL pour `abonnelc`. 6. **Secret et service** — secret tiré au hasard s'il n'existe pas, unité systemd installée et démarrée. 7. **Relais Apache** — les deux lignes `ProxyPass` dans le vhost, puis rechargement d'Apache si la configuration est valide. ### Déclarer le webhook dans Gitea Dans le dépôt `alpinux-presentations` : *Paramètres → Webhooks → Ajouter un webhook → Gitea*. | Champ | Valeur | |---------------------|-----------------------------------------------------| | URL cible | `https://presentations.alpinux.org/deploy` | | Méthode HTTP | `POST` | | Type de contenu | `application/json` | | Secret | la valeur lue par `sudo cat /etc/default/presentations-webhook` | | Déclencheur | *Push* seulement | | Filtre de branche | `main` | Le filtre de branche évite des appels inutiles ; le service ignore de toute façon tout ce qui ne vise pas `refs/heads/main`. ### Rendre le relais Apache durable Le vhost est écrit par ISPConfig, qui le **régénère** à chaque modification du site (certificat, alias, version de PHP…). Les deux lignes posées par `installer.sh` disparaissent ce jour-là. Pour qu'elles tiennent : dans ISPConfig, *Sites → presentations.alpinux.org → Options → Directives Apache*, coller les deux lignes de [`apache-vhost.exemple.conf`](apache-vhost.exemple.conf). ISPConfig les réécrira alors avec le reste du vhost. --- ## Vérifier ```bash # Le service tourne et n'écoute que sur la boucle locale systemctl status presentations-webhook ss -ltn | grep 9878 # Sans signature valable, le relais répond 403 — c'est la bonne réponse curl -s -o /dev/null -w '%{http_code}\n' -X POST -d '{}' https://presentations.alpinux.org/deploy # Le dernier déploiement tail -20 /var/log/presentations-deploy.log ``` Une exécution réussie encadre le build par une ligne `Deploy started` et une ligne `Deploy done`. Une ligne `ABANDON` signale un échec : la cause est juste au-dessus, et le site en ligne n'a pas bougé. Dans Gitea, la page du webhook liste les envois récents avec la réponse reçue, et son bouton *Tester l'envoi* rejoue un push. --- ## Au quotidien | Je veux… | Je fais… | |--------------------------------------------|--------------------------------------------------------------------------| | Savoir pourquoi ce n'est pas en ligne | `tail -30 /var/log/presentations-deploy.log` | | Republier sans nouveau commit | `sudo -u abonnelc /opt/presentations-alpinux/deploy-presentations.sh` | | Appliquer une modification de `deploy/` | `sudo bash /opt/presentations-alpinux/repo/deploy/installer.sh` | | Changer de version de Node.js | modifier `NODE_VERSION` dans `installer.sh`, pousser, relancer `installer.sh` | | Changer le secret | supprimer `/etc/default/presentations-webhook`, relancer `installer.sh`, mettre à jour le webhook dans Gitea | --- ## Les pièges à connaître **Le serveur exécute des copies.** `deploy-presentations.sh` et `webhook.py` sont copiés dans `/opt/presentations-alpinux` par `installer.sh`. Les modifier dans le dépôt ne change rien tant que `installer.sh` n'a pas été relancé. C'est voulu : une pull request fusionnée ne peut pas, à elle seule, changer ce que le service exécute. **Mais le build, lui, exécute le dépôt.** `scripts/build.mjs` et les dépendances de `package-lock.json` tournent sur le serveur à chaque push, sous le compte du service. Une pull request qui touche à `scripts/`, à `package.json` ou à `package-lock.json` se relit donc comme du code qui s'exécutera sur le serveur — pas comme une diapo. **`rsync --delete` épargne deux dossiers.** `error/` et `stats/` appartiennent à ISPConfig et sont exclus de la synchronisation. Tout autre fichier déposé à la main dans le DocumentRoot est supprimé au déploiement suivant. **Le droit d'écriture tient à une ACL.** Si ISPConfig réinitialise les droits du site, le déploiement échoue sur une erreur de permission : relancer `installer.sh` la repose. **Node.js ne se met pas à jour tout seul.** Il est installé hors d'apt. Suivre les versions LTS sur et changer `NODE_VERSION` de temps en temps.