Pipeline de déploiement uniforme pour tous les projets du VPS, quelle que soit leur stack (Symfony, Astro, WordPress, statique…).
GitHub (push main)
│ POST webhook + signature HMAC (X-Hub-Signature-256)
▼
https://deploy.labault.dev/hooks/deploy → Caddy → conteneur `webhook`
│ vérifie le secret + ref == main
▼
deploy/dispatch.sh (résout repo → dossier, git pull, lance deploy.sh)
▼
/srv/<projet>/deploy.sh ← MÊME contrat partout (build + up + healthcheck)
Trois piliers :
- Une deploy key read-only par repo (le webhook ne fait que lire — aucune écriture possible).
- Un contrat
deploy.shidentique dans chaque projet (interface uniforme, implémentation libre). - Un listener webhook unique dans
push-to-deploy, exposé surdeploy.labault.dev.
Le webhook ne fait que git pull → il n'a besoin que de lecture. Le modèle
retenu : une deploy key GitHub en lecture seule par repo, générée sans
passphrase, stockée dans deploy/keys/<repo>.key (gitignoré) et montée en lecture
seule dans le conteneur sous /keys. dispatch.sh choisit la bonne clé d'après le
nom du repo (/keys/<repo-en-minuscules>.key).
Pourquoi par repo et read-only ? Une deploy key GitHub est unique à un repo et peut être limitée à la lecture. Résultat : compromettre le webhook ne donne que la lecture d'un seul repo — jamais d'écriture, jamais accès aux autres. C'est plus sûr qu'une clé personnelle (write sur tout) ou qu'une clé de compte machine partagée.
Génération + ajout en read-only (via gh, authentifié) :
mkdir -p deploy/keys && chmod 700 deploy/keys
NAME=monrepo ; REPO=owner/MonRepo # NAME = basename en minuscules
ssh-keygen -t ed25519 -N "" -C "vps-webhook-ro-$NAME" -f deploy/keys/$NAME.key
gh repo deploy-key add deploy/keys/$NAME.key.pub -R "$REPO" --title vps-webhook-ro
# (read-only par défaut ; surtout PAS --allow-write)cd ~/push-to-deploy
cp .env.exemple .env # si pas déjà fait
# Édite .env :
# LETSENCRYPT_EMAIL=<ton email>
# WEBHOOK_SECRET=<openssl rand -hex 32>docker compose up -d --build webhook
docker compose logs -f webhook # doit afficher "serving hooks on ..."Ajoute un enregistrement deploy.labault.dev → IP du VPS (comme tes autres
sous-domaines). Caddy obtiendra le certificat tout seul au premier accès.
Pour chaque projet à passer en déploiement auto :
-
Cloner sur le VPS dans
/srv/<repo>(si pas déjà fait) :git clone git@github.com:<owner>/<repo>.git /srv/<repo>
À chaque déploiement,
dispatch.shfait ungit reset --hard origin/mainpuischowndu clone vers l'utilisateur applicatif (propriété stable, pas de mélange root).Puis génère sa deploy key read-only (cf. §1.a) :
N=$(basename "<repo>" | tr 'A-Z' 'a-z') ssh-keygen -t ed25519 -N "" -C "vps-webhook-ro-$N" -f ~/push-to-deploy/deploy/keys/$N.key gh repo deploy-key add ~/push-to-deploy/deploy/keys/$N.key.pub -R "<owner>/<repo>" --title vps-webhook-ro
-
Ajouter le contrat de déploiement au repo :
cp ~/push-to-deploy/deploy/deploy.sh.template /srv/<repo>/deploy.sh # adapte le bloc "ÉTAPES SPÉCIFIQUES PROJET" si besoin (migrations…) # commit + push : deploy.sh est versionné avec le projet
Vérifie aussi que le
docker-compose.ymldu projet :- rejoint le réseau externe
web, - n'expose pas de port public,
- porte un
container_namequi matche lereverse_proxyduCaddyfile.
(Optionnel) un
.deploy.envnon versionné pour activer le healthcheck :HEALTHCHECK_URL=https://<projet>.labault.dev/ - rejoint le réseau externe
-
Enregistrer la route dans
push-to-deploy/deploy/projects.conf:<owner>/<repo> = /srv/<repo>(sinon fallback automatique sur
/srv/<repo>). Le matching est insensible à la casse :Owner/MonRepocorrespond àowner/monrepo. -
Créer le webhook GitHub (repo → Settings → Webhooks → Add webhook) :
- Payload URL :
https://deploy.labault.dev/hooks/deploy - Content type :
application/json - Secret : la valeur de
WEBHOOK_SECRET - Events : Just the push event
- Payload URL :
-
Tester :
git commit --allow-empty -m "test deploy" && git push, puis sur le VPS :docker compose logs -f webhook # réception + déclenchement tail -f ~/push-to-deploy/deploy/logs/<repo>.log # déroulé du build/up
Le contrat reste utilisable en SSH, sans webhook :
cd /srv/<repo> && git pull && ./deploy.sh- Le conteneur
webhookmonte le socket Docker : il a de fait les droits root sur l'hôte. C'est nécessaire pour piloter les stacks, mais ça veut dire que le secret HMAC et l'accès réseau àdeploy.labault.devdoivent rester sérieux (le secret protège déjà l'endpoint : sans signature valide, rien ne s'exécute). - Seuls les push sur
maindéclenchent un déploiement (règle danshooks.json). - Le
WEBHOOK_SECRETet la clé privée ne sont jamais commités (.gitignore).
La logique de dispatch.sh est volontairement simple et déterministe. C'est
le point d'extension naturel pour, plus tard, brancher un agent (rollback
automatique si le healthcheck échoue, notification, déploiement par tag…).