Compare commits

..

4 Commits

Author SHA1 Message Date
Gato 7ae0d9f87f docs(watchtower): le PAT de pull doit couvrir Bonsai, plus seulement Luz
Le commentaire disait qu'un PAT read:package sur l'org Luz suffisait — c'était
vrai tant que Luz était seul à publier des images. La stack bonsai tire désormais
bonsai-api du même registre avec les mêmes identifiants : un PAT limité à Luz la
laisserait bloquée sur son ancienne image, sans rien signaler.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 14:03:04 +02:00
Gato 30887c066b feat(bonsai): la prod tire l'image publiée par la CI au lieu de la recompiler
Le service api clonait le dépôt et compilait le JAR sur le serveur à chaque
« docker compose build » (Dockerfile.api + secret ssh_key + CACHEBUST). La prod
rebuildait donc ce que la CI avait déjà buildé, sans garantie que les deux
partent du même commit — et aucune image publiée n'atteignait jamais la prod.

Elle tire maintenant …/bonsai-api:main et porte le label watchtower.enable : le
redéploiement suit tout seul chaque merge sur main, comme pour Luz. Le webapp,
lui, continue d'être buildé depuis les sources — d'où le secret ssh_key conservé.

Prérequis : de quoi tirer les images privées de l'org Bonsai. Le PAT de Watchtower
n'a aujourd'hui le scope read:package que sur Luz ; sans extension à Bonsai, le
pull échoue en silence (/v1/update répond 200, la prod ne bouge pas).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 14:02:24 +02:00
Gato 23ce5c86e1 Merge pull request 'La prod suit à nouveau les images publiées : les stacks passent au tag :main' (#1) from fix/stacks-luz-suivent-le-tag-main into main
Reviewed-on: #1
2026-07-26 22:02:44 +02:00
Gato 5558c206a0 fix(luz): les stacks suivent le tag :main, publié par la CI
La prod servait encore a70e0e4 (25/07) alors que main en etait a 79 commits
plus loin : les stacks luz et dev-luz epinglaient « :develop », un tag que la
CI de l'app ne pousse plus depuis qu'elle ne se declenche que sur main
(images.yml publie sha-<court>, main et latest).

Rien ne le signalait : Watchtower repondait bien 200 a /v1/update, ne trouvait
aucune image plus recente pour « :develop » et ne recreait donc rien. Seule la
verification post-deploiement de la CI a fini par le dire.

Les quatre images (backend + webapp, prod et dev-luz) visent desormais « :main ».
Les en-tetes des deux stacks rappellent que ce tag doit rester celui que publie
images.yml, changer l'un imposant de changer l'autre.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 21:59:37 +02:00
7 changed files with 84 additions and 52 deletions
-20
View File
@@ -1,20 +0,0 @@
# syntax=docker/dockerfile:1
FROM eclipse-temurin:25-jdk AS builder
RUN apt-get update && apt-get install -y git openssh-client && rm -rf /var/lib/apt/lists/*
ARG CACHEBUST
RUN --mount=type=secret,id=ssh_key \
--mount=type=cache,target=/root/.gradle \
mkdir -p /root/.ssh && \
cp /run/secrets/ssh_key /root/.ssh/id_ed25519 && \
chmod 600 /root/.ssh/id_ed25519 && \
ssh-keyscan -p 2222 git.goutailler-olivier.com >> /root/.ssh/known_hosts && \
git clone --depth 1 --branch main \
ssh://git@git.goutailler-olivier.com:2222/Gato/Bonsai-api.git /src && \
cd /src && ./gradlew bootJar --no-daemon -q
FROM eclipse-temurin:25-jre-alpine
WORKDIR /app
COPY --from=builder /src/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
+27 -11
View File
@@ -2,21 +2,25 @@
Stack Docker Compose unifiée pour l'application Bonsai : base de données, API et webapp.
Les images sont **buildées depuis les sources** de la branche `main` de chaque dépôt au moment du build. La clé SSH est transmise au build via un secret BuildKit — elle n'est jamais gravée dans les couches de l'image.
Les deux composants ne se déploient pas de la même façon :
- **`api`** — image **tirée du registre Gitea** (`git.goutailler-olivier.com/bonsai/bonsai-api:main`), publiée par la CI du dépôt `Bonsai-api` (`.gitea/workflows/images.yml`) à chaque merge sur `main`. Le redéploiement est automatique : le conteneur porte le label `com.centurylinklabs.watchtower.enable=true`, donc Watchtower (`../watchtower/`) le recrée dès qu'une nouvelle image est publiée. Rien à builder sur le serveur.
- **`webapp`** — toujours **buildé depuis les sources** de la branche `main` du dépôt `Bonsai-webapp`. La clé SSH est transmise au build via un secret BuildKit — elle n'est jamais gravée dans les couches de l'image.
## Services
| Service | Image de base | Source |
|---------|--------------|--------|
| Service | Image | Provenance |
|---------|-------|------------|
| `db` | `postgres:16-alpine` | — |
| `api` | `eclipse-temurin:25-jdk``25-jre-alpine` | `Bonsai-api` (branche `main`) |
| `webapp` | `node:22-alpine``nginx:alpine` | `Bonsai-webapp` (branche `main`) |
| `api` | `git.goutailler-olivier.com/bonsai/bonsai-api:main` | CI de `Bonsai-api` (merge sur `main`) |
| `webapp` | `node:22-alpine``nginx:alpine` | build local depuis `Bonsai-webapp` (branche `main`) |
## Prérequis
- Docker avec BuildKit activé (Docker 23+)
- Une clé SSH ayant accès au dépôt Gitea (`git.goutailler-olivier.com:2222`)
- Une clé SSH ayant accès au dépôt Gitea (`git.goutailler-olivier.com:2222`) — pour le build du `webapp`
- Réseau Docker externe `proxy` (Traefik)
- De quoi tirer les images privées de l'org **Bonsai** : soit un `docker login git.goutailler-olivier.com` fait une fois en root sur le serveur, soit les `REPO_USER`/`REPO_PASS` de la stack Watchtower, dont le PAT doit porter le scope `read:package` sur l'org Bonsai. Sans cela, Watchtower échoue silencieusement à tirer l'image (`unauthorized: reqPackageAccess`) et la prod reste sur l'ancienne version, alors que l'appel à `/v1/update` répond bien 200.
## Démarrage
@@ -25,7 +29,8 @@ Les images sont **buildées depuis les sources** de la branche `main` de chaque
cp .env.example .env
# Éditer .env : définir POSTGRES_PASSWORD et SSH_KEY_PATH
# 2. Builder les images (clone + compile)
# 2. Tirer l'image de l'API + builder le webapp
docker compose pull
docker compose build
# 3. Démarrer la stack
@@ -34,14 +39,25 @@ docker compose up -d
## Mise à jour
Pour rebuilder depuis le dernier commit de `main` (sans tout reconstruire) :
L'API se met à jour **toute seule** à chaque merge sur `main` de `Bonsai-api` (CI → registre → Watchtower). Aucune action ici.
Pour forcer un redéploiement, ou pour rebuilder le webapp depuis le dernier commit de sa branche `main` :
```bash
CACHEBUST=$(date +%s) docker compose build
docker compose up -d
./deploy.sh
```
`CACHEBUST` invalide uniquement l'étape clone+build. Les couches précédentes (installation des outils, image de base) restent en cache — le build reste rapide.
`CACHEBUST` (posé par le script) invalide uniquement l'étape clone+build du webapp. Les couches précédentes (installation des outils, image de base) restent en cache — le build reste rapide.
### Revenir à une version précédente de l'API
Chaque publication pose aussi un tag immuable `sha-<court>`. Pour épingler un commit :
```yaml
image: git.goutailler-olivier.com/bonsai/bonsai-api:sha-ccc20bf
```
puis `docker compose up -d api`. Watchtower ne touche pas à un tag figé qui ne bouge plus — repasser sur `:main` pour réactiver le suivi automatique.
## Arrêt
+9
View File
@@ -1,4 +1,13 @@
#!/bin/bash
#
# Déploiement manuel de la stack Bonsai.
#
# L'API n'est plus buildée ici : son image est publiée par la CI du dépôt
# Bonsai-api à chaque merge sur main, et Watchtower recrée le conteneur tout seul.
# Ce script sert donc au premier démarrage, à un changement de la stack ou à un
# redéploiement forcé — « pull » va chercher la dernière image de l'API, « build »
# ne concerne plus que le webapp (toujours compilé depuis les sources).
docker compose pull
CACHEBUST=$(date +%s) docker compose build
docker compose up -d
+18 -7
View File
@@ -20,14 +20,24 @@ services:
networks:
- bonsai-net
# Image tirée du registre Gitea, publiée par la CI du dépôt Bonsai-api
# (.gitea/workflows/images.yml) à chaque push sur main. Le tag « main » suit la
# tête de cette branche ; « sha-<court> » permet d'épingler un commit précis en
# cas de rollback.
#
# Avant, ce service clonait le dépôt et compilait le JAR sur le serveur à chaque
# « docker compose build » (Dockerfile.api + secret ssh_key + CACHEBUST) : la prod
# rebuildait ce que la CI avait déjà buildé, et rien ne garantissait que les deux
# partaient du même commit. Le redéploiement est désormais automatique — le label
# watchtower.enable ci-dessous fait recréer ce conteneur dès que la CI publie une
# nouvelle image.
#
# Prérequis : le serveur doit pouvoir tirer les images privées de l'org Bonsai —
# soit via un « docker login git.goutailler-olivier.com » (root), soit via les
# REPO_USER/REPO_PASS de la stack Watchtower, dont le PAT doit avoir le scope
# read:package sur l'org Bonsai (et pas seulement sur Luz).
api:
build:
context: .
dockerfile: Dockerfile.api
secrets:
- ssh_key
args:
CACHEBUST: ${CACHEBUST:-stable}
image: git.goutailler-olivier.com/bonsai/bonsai-api:main
container_name: bonsai-api
restart: unless-stopped
depends_on:
@@ -47,6 +57,7 @@ services:
- bonsai-net
- proxy
labels:
- com.centurylinklabs.watchtower.enable=true
- traefik.enable=true
- traefik.http.routers.bonsai-api.rule=Host(`bonsai.goutailler-olivier.com`) && PathPrefix(`/api`)
- traefik.http.routers.bonsai-api.entrypoints=websecure
+7 -3
View File
@@ -4,12 +4,16 @@
# Backend : https://dev-luz.goutailler-olivier.com/api → backend Spring (:8080)
#
# Déploiement AUTOMATIQUE : la CI Gitea (client/app/.gitea/workflows/images.yml)
# builde et pousse les images « …/luz/luz-*:develop » à chaque push sur develop,
# builde et pousse les images « …/luz/luz-*:main » à chaque push sur main,
# puis appelle l'API HTTP de Watchtower. Watchtower (../watchtower/) ne surveille
# que les conteneurs portant le label watchtower.enable=true (posé ci-dessous) :
# il tire la nouvelle image et recrée le conteneur. Plus de transfert SSH ni de
# « deploy.sh » pour cette stack de test.
#
# ⚠️ La CI ne publie plus « :develop » (elle ne se déclenche que sur main) : une
# stack restée sur ce tag ne reçoit plus jamais de mise à jour, en silence. Le tag
# suivi ici doit toujours être celui que publie images.yml — cf. ../luz/.
#
# Prérequis : le serveur (root) doit être « docker login git.goutailler-olivier.com »
# une fois, sinon Watchtower ne peut pas tirer les images privées de l'org luz.
#
@@ -41,7 +45,7 @@ services:
- dev-luz-net
backend:
image: git.goutailler-olivier.com/luz/luz-backend:develop
image: git.goutailler-olivier.com/luz/luz-backend:main
container_name: dev-luz-backend
restart: unless-stopped
depends_on:
@@ -69,7 +73,7 @@ services:
- traefik.docker.network=proxy
webapp:
image: git.goutailler-olivier.com/luz/luz-webapp:develop
image: git.goutailler-olivier.com/luz/luz-webapp:main
container_name: dev-luz-webapp
restart: unless-stopped
depends_on:
+18 -9
View File
@@ -3,15 +3,24 @@
# Frontend : https://luz.goutailler-olivier.com → webapp (nginx :80)
# Backend : https://luz.goutailler-olivier.com/api → backend Spring (:8080)
#
# Images tirées du registre Gitea (git.goutailler-olivier.com/luz/luz-*:develop),
# publiées par la CI (client/app/.gitea/workflows/images.yml) à chaque push « develop »
# (l'app n'a pas de branche « main »). Prod et dev-luz partagent donc la MÊME image ;
# ce qui les sépare : ici la base est PERSISTANTE et sans profil « dev » (voir plus bas),
# côté dev-luz elle est réinitialisée à chaque démarrage. Le redéploiement est
# automatique : les conteneurs portent le label watchtower.enable=true, donc chaque
# push develop fait recréer AUSSI cette prod par Watchtower (../watchtower/), en même
# Images tirées du registre Gitea (git.goutailler-olivier.com/luz/luz-*:main),
# publiées par la CI (client/app/.gitea/workflows/images.yml) à chaque push « main »
# — c'est-à-dire à chaque mise en production. Prod et dev-luz partagent donc la MÊME
# image ; ce qui les sépare : ici la base est PERSISTANTE et sans profil « dev » (voir
# plus bas), côté dev-luz elle est réinitialisée à chaque démarrage. Le redéploiement
# est automatique : les conteneurs portent le label watchtower.enable=true, donc chaque
# push main fait recréer AUSSI cette prod par Watchtower (../watchtower/), en même
# temps que dev-luz. Plus de transfert SSH ni de deploy.sh.
#
# ⚠️ Le tag suivi ici doit rester celui que la CI publie. L'app a d'abord publié
# « :develop » (elle n'avait pas de branche main), puis est passée à « :main ». Ces
# stacks étant restées sur « :develop », plus rien n'a poussé ce tag : Watchtower
# répondait 200 à /v1/update mais ne trouvait aucune image plus récente, et la prod
# est restée figée sur le dernier build develop (a70e0e4, 25/07) pendant que main
# avançait de 79 commits — sans la moindre erreur visible côté CI hors la
# vérification post-déploiement. Changer le tag de publication dans images.yml
# impose donc de changer ce tag ici, dans le même mouvement.
#
# Prérequis : le serveur (root) doit être « docker login git.goutailler-olivier.com »
# une fois, sinon Watchtower ne peut pas tirer les images privées de l'org luz.
#
@@ -43,7 +52,7 @@ services:
- luz-net
backend:
image: git.goutailler-olivier.com/luz/luz-backend:develop
image: git.goutailler-olivier.com/luz/luz-backend:main
container_name: luz-backend
restart: unless-stopped
depends_on:
@@ -70,7 +79,7 @@ services:
- traefik.docker.network=proxy
webapp:
image: git.goutailler-olivier.com/luz/luz-webapp:develop
image: git.goutailler-olivier.com/luz/luz-webapp:main
container_name: luz-webapp
restart: unless-stopped
depends_on:
+5 -2
View File
@@ -13,10 +13,13 @@ services:
WATCHTOWER_LABEL_ENABLE: "true"
WATCHTOWER_CLEANUP: "true"
# Identifiants de pull du registre Gitea privé (git.goutailler-olivier.com).
# Sans eux, Watchtower échoue à tirer luz-backend/luz-webapp :develop
# Sans eux, Watchtower échoue à tirer luz-backend/luz-webapp :main
# (« unauthorized: reqPackageAccess ») et ne recrée jamais les conteneurs
# — le déploiement reste bloqué en silence (Updated=0, mais /v1/update → 200).
# Un PAT en LECTURE seule (scope read:package) sur l'org Luz suffit.
# Un PAT en LECTURE seule (scope read:package) suffit — mais il lui faut
# accès aux DEUX organisations qui publient des images ici : Luz et, depuis
# que la stack bonsai tire bonsai-api du registre, Bonsai. Un PAT limité à
# Luz laisserait la prod Bonsai bloquée sur son ancienne image, en silence.
# Remplace l'ancien montage de /root/.docker/config.json, qui côté hôte
# ne contenait aucune auth (le docker login du CI a lieu sur le runner).
REPO_USER: ${REGISTRY_USER}