Livraison continue
La chaîne de livraison sépare trois responsabilités : GitHub Actions valide et publie, GHCR conserve les artefacts immuables, Dokploy exécute les applications et protège leurs volumes. Staging est continu ; la production reste une promotion explicitement approuvée.
feature/* → PR develop → CI → image sha-… → staging continu ↓ release/x.y.z → PR main → CI → staging du candidat ↓ approbation production → sauvegarde ↓ version → backend → web → smoke → changelog → notificationArtefacts déployés
Section intitulée « Artefacts déployés »Chaque CI réussie sur develop ou main publie une image identifiée par le SHA complet :
ghcr.io/chris589/harmony-backend:sha-<40 caractères>ghcr.io/chris589/harmony-web:sha-<40 caractères>Ne déployez jamais latest en production. Le SHA est également embarqué dans l’image et exposé par
X-Harmony-Revision sur /api/hello et /healthz. Le workflow attend la fin du déploiement Dokploy,
puis refuse la livraison si l’URL publique ne renvoie pas la révision attendue.
Staging définit SEO_INDEXING_ENABLED=false et laisse SEO_INDEXING_ALLOWED_HOSTS vide. Le workflow
staging vérifie le header X-Robots-Tag: noindex. La promotion vérifie la politique déclarée dans la
variable GitHub SEO_INDEXING_ENABLED de l’environnement production : false exige noindex,
true refuse noindex. En l’absence de cette variable, le contrôle attend false, comme le réglage
applicatif par défaut. Une valeur différente de true ou false ou une réponse HTTP autre que 200
fait échouer le contrôle. Le contrôle ne modifie ni les variables Dokploy ni l’indexation du site.
Gardez cette variable GitHub alignée avec SEO_INDEXING_ENABLED dans Dokploy. Pour activer
l’indexation, configurez les deux à true et autorisez l’hôte public dans SEO_INDEXING_ALLOWED_HOSTS.
Cloudflare Access protège le domaine de recette ; seul /healthz possède une règle Bypass ciblée
pour permettre le contrôle de révision sans ouvrir le reste du site. Les previews restent en noindex grâce à la liste exacte
SEO_INDEXING_ALLOWED_HOSTS, limitée à www.leclosdeseucalyptus.com dans la configuration publique
cible. La bascule du domaine exige aussi d’aligner les URL de déploiement et de health check sur
ce nouvel hôte. Voir Domaines et indexation.
Préparer Dokploy
Section intitulée « Préparer Dokploy »Créez quatre applications Docker distinctes : backend staging, web staging, backend production et
web production. Configurez leurs domaines, réseaux et variables d’exécution dans Dokploy. Les deux
backends doivent utiliser des volumes nommés différents montés sur /app/pb_data ; aucune preview ni
application staging ne doit monter le volume de production.
Pour les images privées GHCR, créez un jeton durable limité à read:packages et associez-le au
registre Dokploy. N’utilisez pas le GITHUB_TOKEN d’un run comme identifiant persistant : il expire à
la fin du job.
Avant d’automatiser les sauvegardes de volume, utilisez Dokploy 0.29.13 ou plus récent. Les versions antérieures contiennent une vulnérabilité critique dans cette fonction. Configurez ensuite une destination S3, un backup du volume PocketBase par environnement et l’option d’arrêt du conteneur pendant la copie pour obtenir un snapshot SQLite cohérent.
Environnement GitHub staging
Section intitulée « Environnement GitHub staging »Créez un environnement staging dans chacun des deux dépôts.
Variables communes :
| Nom | Contenu |
|---|---|
DOKPLOY_URL |
origine HTTPS de Dokploy, sans / final |
DOKPLOY_APPLICATION_ID |
application staging du dépôt |
DEPLOY_URL |
URL publique principale |
DEPLOY_HEALTH_URL |
web : /healthz ; backend : /api/hello |
Le backend reçoit aussi DOKPLOY_VOLUME_BACKUP_ID. Cette variable est obligatoire pour activer le
déploiement staging : chaque livraison sauvegarde ainsi le volume avant que les migrations de
l’image ne démarrent.
Secrets communs :
| Nom | Contenu |
|---|---|
DOKPLOY_API_TOKEN |
jeton API Dokploy dédié à la CI |
DOKPLOY_REGISTRY_USERNAME |
utilisateur autorisé à lire les packages GHCR |
DOKPLOY_REGISTRY_TOKEN |
jeton GHCR limité à la lecture |
Tant que DOKPLOY_APPLICATION_ID n’est pas défini, l’image est publiée mais le job de déploiement est
ignoré. Pour le backend, le même garde-fou s’applique à DOKPLOY_VOLUME_BACKUP_ID. Cela permet
d’intégrer la chaîne sans diriger accidentellement une première exécution vers la mauvaise
application ou lancer une migration sans sauvegarde.
Environnement GitHub production
Section intitulée « Environnement GitHub production »L’orchestrateur de production se trouve dans harmony-web, workflow Promote images to
production. Configurez un environnement production avec approbateur obligatoire, auto-approbation
désactivée et accès limité à main. Si le forfait GitHub ne permet pas les approbateurs sur un dépôt
privé, conservez la confirmation PRODUCTION du workflow et faites valider la release hors bande
avant son lancement.
Variables :
| Nom | Contenu |
|---|---|
DOKPLOY_URL |
origine HTTPS de Dokploy |
DOKPLOY_BACKEND_APPLICATION_ID |
application backend production |
DOKPLOY_WEB_APPLICATION_ID |
application web production |
DOKPLOY_BACKEND_VOLUME_BACKUP_ID |
backup du volume PocketBase production |
BACKEND_HEALTH_URL |
URL absolue terminant par /api/hello |
WEB_HEALTH_URL |
URL absolue terminant par /healthz |
WEB_URL |
URL publique utilisée pour le smoke test |
SEO_INDEXING_ENABLED |
true ou false, identique au réglage Dokploy ; défaut false |
BACKEND_RELEASE_NOTIFICATION_URL |
URL absolue /api/internal/releases |
Ajoutez les trois mêmes secrets que pour staging, puis :
| Secret | Usage |
|---|---|
HARMONY_RELEASE_GITHUB_TOKEN |
crée les tags/releases et pousse les images versionnées des deux repos |
HARMONY_RELEASE_NOTIFICATION_TOKEN |
authentifie l’appel interne qui avertit les utilisateurs du back-office |
GitHub Packages impose actuellement un PAT classic pour GHCR : limitez-le aux scopes repo,
read:packages et write:packages (ajoutez workflow si une révision de release modifie les
workflows), donnez-lui une durée courte et faites-le tourner régulièrement. Utilisez exactement la
même valeur pour HARMONY_RELEASE_NOTIFICATION_TOKEN dans
l’environnement du backend production sur Dokploy et dans le secret GitHub production. Les
identifiants staging et production doivent être différents, même lorsque les applications sont sur
le même serveur Dokploy.
Préparer une release GitFlow
Section intitulée « Préparer une release GitFlow »Une release produit coordonne les deux dépôts, même si un seul contient des changements fonctionnels.
Les commits et titres de PR suivent Conventional Commits 1.0.0. Les PR de feature et de hotfix sont
squash-mergées avec leur titre comme message final. Les PR de release utilisent un merge commit dont
le titre reprend aussi celui de la PR, afin de conserver dans main tous les commits fonctionnels de
la version. Utilisez notamment feat(scope): …, fix(scope): … et
chore(release): prepare Le Clos des Eucalyptus x.y.z; marquez une incompatibilité avec ! et un footer
BREAKING CHANGE:.
Pour la version x.y.z :
- vérifiez que les versions de
developsouhaitées sont vertes et validées en staging ; - créez
release/x.y.zdepuisdevelopdansharmony-backendetharmony-web; - n’acceptez sur ces branches que les corrections de stabilisation et la documentation de release ;
- ouvrez une PR
release/x.y.zversmainintituléechore(release): prepare Le Clos des Eucalyptus x.y.zdans chaque dépôt et attendez toute la CI ; - fusionnez les deux PR avec Create a merge commit, sans squash ; le titre validé devient celui
du merge commit et les commits fonctionnels restent visibles dans
main; - attendez le redéploiement staging des deux commits de
mainet relevez leurs tagssha-…; - si la branche de release contient des corrections absentes de
develop, fusionnez-la aussi versdevelopavant de supprimer la branche.
Une correction urgente part de main sur hotfix/<description>, revient par PR vers main, passe
par staging, puis est fusionnée dans develop. Les commits directs sur main et develop sont
interdits.
Déploiement staging
Section intitulée « Déploiement staging »Après une CI verte sur develop ou main, deploy-staging.yml :
- reconstruit le commit exact dans une image et publie
sha-…dans GHCR ; - sauvegarde le volume backend si le backup est configuré ;
- affecte l’image immuable à l’application Dokploy ;
- suit le déploiement jusqu’à son statut final ;
- contrôle la révision publique et un smoke test représentatif.
Pour un changement cross-stack, fusionnez et observez d’abord le backend rétrocompatible, puis le web. N’introduisez pas une rupture qui exige une bascule parfaitement simultanée.
Promotion production
Section intitulée « Promotion production »Depuis la branche main dans Actions sur harmony-web, lancez Promote images to production,
renseignez la version SemVer sans préfixe (0.2.0) et les deux tags sha-… produits après la fusion
des PR de release. Saisissez PRODUCTION, contrôlez les deux révisions, puis approuvez
l’environnement lorsque cette protection est disponible.
Le workflow exécute dans cet ordre :
- validation stricte de la version, du lancement depuis
mainet des tags immuables ; - pour une nouvelle version, vérification que chaque SHA est exactement la tête de
mainde son dépôt ; pour un redéploiement, vérification que les tagsv…existants ciblent les mêmes SHA ; - ajout d’une couche de métadonnées aux images validées et publication des tags
v…; - sauvegarde PocketBase et attente du statut
done; - backend, contrôle de
X-Harmony-RevisionetX-Harmony-Version; - web, même contrôle sur
/healthz; - smoke tests backend et site public ;
- génération des notes GitHub catégorisées pour les deux dépôts et publication du changelog combiné ;
- événement
system.release_published, dédupliqué par version et diffusé en temps réel au back-office.
Le code applicatif n’est pas recompilé pendant cette phase : Dockerfile.release ajoute uniquement
HARMONY_RELEASE_VERSION et PUBLIC_HARMONY_RELEASE_VERSION au-dessus des images déjà exercées en
staging. Le SHA reste donc la preuve de contenu, tandis que la version est lisible par les humains.
Les catégories du changelog proviennent de .github/release.yml, tandis que chaque entrée reprend
le titre Conventional Commit de la PR. Étiquetez les PR avec feature ou
enhancement, bug ou fix, security, documentation, dependencies ou maintenance. Le label
skip-changelog exclut une PR. Relancer le workflow avec la même version met à jour les releases
existantes et ne crée pas une seconde notification.
Migrations PocketBase
Section intitulée « Migrations PocketBase »Les migrations sont compilées dans le binaire backend et s’exécutent au démarrage. Une release doit
suivre expand → migrate → contract :
- ajouter des collections ou champs optionnels en conservant l’ancien contrat ;
- déployer le backend et effectuer le backfill idempotent ;
- déployer le web qui consomme le nouveau contrat ;
- retirer l’ancien schéma dans une release ultérieure seulement.
PocketBase utilise SQLite : maintenez une seule instance en écriture par volume et ne faites pas de
rolling deployment avec deux versions écrivant simultanément dans la même base. make seed est
strictement réservé aux données locales ou jetables.
Rollback
Section intitulée « Rollback »Le même workflow peut redéployer une ancienne version en fournissant sa version et la paire exacte
de tags sha-… déjà associée. Le garde-fou interdit de réaffecter une version existante à d’autres
commits. Pour le web, cette opération est normalement sans état. Pour le backend :
- sans migration, redéployez l’ancienne image ;
- après une migration additive vérifiée compatible, l’ancienne image peut être envisagée ;
- après une migration destructive ou de nouvelles écritures, préférez une correction en avant ;
- en cas de corruption, restaurez le snapshot dans un volume isolé, validez-le, puis basculez.
Une sauvegarde réussie n’autorise pas un rollback aveugle. Notez le SHA, l’instant du snapshot et la dernière migration dans le compte rendu de release.
Diagnostic rapide
Section intitulée « Diagnostic rapide »- image publiée mais aucun déploiement : vérifier les variables de l’environnement GitHub ;
401/403Dokploy : renouveler le jeton API sans l’afficher dans les logs ;- timeout avant
done: consulter le déploiement et les logs Dokploy portant le titre du run GitHub ; - révision inattendue : vérifier le routage du domaine et l’image réellement affectée à l’application ;
- backup bloqué : interrompre la promotion et résoudre le stockage S3 avant de relancer ;
- backend sain mais web en erreur : vérifier
API_BASE_URLdepuis le conteneur web.
Diagnostic des appels Dokploy
Section intitulée « Diagnostic des appels Dokploy »Le script scripts/dokploy-deploy.sh, utilisé pour le staging et la promotion de
production du backend puis du web, journalise la méthode, l’endpoint et le numéro
de tentative de chaque appel Dokploy. En cas d’échec, il affiche le statut HTTP et
le code de sortie de curl, sans exposer les corps de réponse ni les identifiants.
Seules les lectures de statut (GET deployment.all) sont réessayées après une
erreur temporaire HTTP 408, 429 ou 5xx, ou une erreur de connexion. Elles sont
limitées à trois tentatives, espacées de cinq secondes, avec un délai de connexion
de dix secondes et une durée maximale de trente secondes par requête. Les erreurs
permanentes, dont les refus d’authentification, arrêtent immédiatement le script.
Les POST de configuration et de déploiement ne sont jamais réessayés : une réponse
en erreur peut suivre une opération déjà effectuée. Dans ce cas, consulter
l’historique Dokploy et la révision servie avant de relancer le workflow.
Les contrôles de résultat du déploiement, de révision et de version restent requis.
Les tests de ce comportement utilisent uniquement des commandes simulées, via
make release-script-test, sans contacter Dokploy.