alpinux.presentations/deploy/webhook.md
Cédrix 9ecf1973b8 Suivre le nom réel du dépôt : alpinux.presentations
Le dépôt a été créé sur la forge sous le nom alpinux.presentations, et non
alpinux-presentations : adresses de clonage, lien de la page d'accueil et
consignes d'installation sont alignés.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M713yNBL8cssBA1Zri5QLT
2026-10-10 22:06:47 +02:00

181 lines
9.2 KiB
Markdown

# 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 <https://nodejs.org/fr/about/previous-releases> et changer
`NODE_VERSION` de temps en temps.