diff --git a/docs/OPS-DEPLOYMENT.md b/docs/OPS-DEPLOYMENT.md index f5bfd3a..4f6a6c1 100644 --- a/docs/OPS-DEPLOYMENT.md +++ b/docs/OPS-DEPLOYMENT.md @@ -25,6 +25,19 @@ Objectif : déployer une nouvelle version du site sur le NAS (DS220+) sans jamai ➡️ Déploiement = `docs/DEPLOY_PROD_SYNOLOGY_DS220.md` (procédure détaillée, à jour). +## Mise à jour (2026-03-03) — Gate CI de déploiement (SKIP / HOTPATCH / FULL) + preuves A/B + +La procédure de déploiement “vivante” est désormais pilotée par **Gitea Actions** via le workflow : +- `.gitea/workflows/deploy-staging-live.yml` + +Ce workflow décide automatiquement : +- **FULL** (rebuild + restart blue + green) dès qu’un changement impacte le build (ex: `src/content/`, `src/pages/`, `scripts/`, `src/anchors/`, etc.) +- **HOTPATCH** (patch JSON + copie media) quand le changement ne concerne que `src/annotations/` et/ou `public/media/` +- **SKIP** sinon + +Les preuves et la procédure de test reproductible A/B sont documentées dans : +➡️ `docs/runbooks/DEPLOY-BLUE-GREEN.md` → section “CI Deploy gate (merge-proof) + Tests A/B + preuve alias injection”. + ## Schéma (résumé, sans commandes) - Ne jamais toucher au slot live. diff --git a/docs/OPS_COCKPIT.md b/docs/OPS_COCKPIT.md index d89cbb5..a3267f5 100644 --- a/docs/OPS_COCKPIT.md +++ b/docs/OPS_COCKPIT.md @@ -202,4 +202,33 @@ docker compose logs --tail=200 web_blue docker compose logs --tail=200 web_green # Si tu veux suivre en live : -docker compose logs -f web_green \ No newline at end of file +docker compose logs -f web_green + + +## Historique synthétique (2026-03-03) — Stabilisation CI/CD “zéro surprise” + +### Problème initial observé +- Déploiement parfois lancé en “hotpatch” alors qu’un rebuild était nécessaire. +- Sur merge commits, la détection de fichiers modifiés pouvait être ambiguë. +- Résultat : besoin de `force=1` manuel pour éviter des incohérences. + +### Correctif appliqué +- Gate CI rendu **merge-proof** : + - lecture de `BEFORE` et `AFTER` depuis `event.json` + - calcul des fichiers modifiés via `git diff --name-only BEFORE AFTER` + +- Politique de décision stabilisée : + - FULL auto dès qu’un changement impacte build/runtime (content/pages/scripts/anchors/etc.) + - HOTPATCH auto uniquement pour annotations/media + +### Preuves +- Test A (touch src/content) : + - Gate flags: HAS_FULL=1 HAS_HOTPATCH=0 → MODE=full +- Test B (touch src/annotations) : + - Gate flags: HAS_FULL=0 HAS_HOTPATCH=1 → MODE=hotpatch + +### Audit post-déploiement (preuves côté NAS) +- 8081 + 8082 répondent HTTP 200 +- `/para-index.json` + `/annotations-index.json` OK +- Aliases injectés visibles dans HTML via `.para-alias` quand alias présent + diff --git a/docs/runbooks/DEPLOY-BLUE-GREEN.md b/docs/runbooks/DEPLOY-BLUE-GREEN.md index 4e14ba4..26afbde 100644 --- a/docs/runbooks/DEPLOY-BLUE-GREEN.md +++ b/docs/runbooks/DEPLOY-BLUE-GREEN.md @@ -199,4 +199,125 @@ Ne jamais modifier dist/ “à la main” sur NAS. Si un hotfix prod est indispensable : documenter et backporter via PR Gitea. -Le canonical dépend du build : PUBLIC_SITE doit être injecté (voir runbook ENV-PUBLIC_SITE). \ No newline at end of file +Le canonical dépend du build : PUBLIC_SITE doit être injecté (voir runbook ENV-PUBLIC_SITE). + +## 10) CI Deploy (Gitea Actions) — Gate SKIP / HOTPATCH / FULL (merge-proof) + preuves + +Cette section documente le comportement **canonique** du workflow : +- `.gitea/workflows/deploy-staging-live.yml` + +Objectif : **zéro surprise**. +On ne veut plus “penser à force=1”. +Le gate doit décider automatiquement, y compris sur des **merge commits**. + +### 10.1 — Principe (ce que fait réellement le gate) + +Le job `deploy` calcule les fichiers modifiés entre : +- `BEFORE` = commit précédent (avant le push sur main) +- `AFTER` = commit actuel (après le push / merge sur main) + +Puis il classe le déploiement dans un mode : + +- **MODE=full** + - rebuild image + restart `archicratie-web-blue` (8081) + `archicratie-web-green` (8082) + - warmup endpoints (para-index, annotations-index, pagefind.js) + - vérification canonical staging + live + +- **MODE=hotpatch** + - rebuild d’un `annotations-index.json` consolidé depuis `src/annotations/**` + - patch direct dans les conteneurs en cours d’exécution (blue+green) + - copie des médias modifiés `public/media/**` vers `/usr/share/nginx/html/media/**` + - smoke sur `/annotations-index.json` des deux ports + +- **MODE=skip** + - pas de déploiement (on évite le bruit) + +⚠️ Important : le mode “hotpatch” **ne rebuild pas** Astro. +Donc toute modification de contenu, routes, scripts, anchors, etc. doit déclencher **full**. + +### 10.2 — Matrice de décision (règles officielles) + +Le gate définit deux flags : +- `HAS_FULL=1` si changement “build-impacting” +- `HAS_HOTPATCH=1` si changement “annotations/media only” + +Règle de priorité : +1) Si `HAS_FULL=1` → **MODE=full** +2) Sinon si `HAS_HOTPATCH=1` → **MODE=hotpatch** +3) Sinon → **MODE=skip** + +#### 10.2.1 — Changements qui déclenchent FULL (build-impacting) + +Exemples typiques (non exhaustif, mais on couvre le cœur) : +- `src/content/**` (contenu MD/MDX) +- `src/pages/**` (routes Astro) +- `src/anchors/**` (aliases d’ancres) +- `scripts/**` (tooling postbuild : injection, index, tests) +- `src/layouts/**`, `src/components/**`, `src/styles/**` (rendu et scripts inline) +- `astro.config.mjs`, `package.json`, `package-lock.json` +- `Dockerfile`, `docker-compose.yml`, `nginx.conf` +- `.gitea/workflows/**` (changement infra CI/CD) + +=> On veut **full** pour garantir cohérence et éviter “site partiellement mis à jour”. + +#### 10.2.2 — Changements qui déclenchent HOTPATCH (sans rebuild) + +Uniquement : +- `src/annotations/**` (shards YAML) +- `public/media/**` (assets média) + +=> On veut hotpatch pour vitesse et éviter rebuild NAS. + +### 10.3 — “Merge-proof” : pourquoi on ne lit PAS seulement `git show $SHA` + +Sur un merge commit, `git show --name-only $SHA` peut être trompeur selon le contexte. +La méthode robuste est : +- utiliser `event.json` (Gitea Actions) pour récupérer `before` et `after` +- calculer `git diff --name-only BEFORE AFTER` + +C’est ce qui rend le gate **merge-proof**. + +### 10.4 — Tests de preuve A/B (reproductibles) + +Ces tests valident le gate sans ambiguïté. +But : vérifier que le mode choisi est EXACTEMENT celui attendu. + +#### Test A — toucher `src/content/...` (FULL auto) + +1) Créer une branche test +2) Modifier 1 fichier dans `src/content/` (ex : ajouter une ligne de commentaire non destructive) +3) PR → merge dans `main` +4) Vérifier dans `deploy-staging-live.yml` : + +Attendus : +- `Gate flags: HAS_FULL=1 HAS_HOTPATCH=0` +- `✅ build-impacting change -> MODE=full (rebuild+restart)` +- Les étapes FULL (blue puis green) s’exécutent réellement + +#### Test B — toucher `src/annotations/...` uniquement (HOTPATCH auto) + +1) Créer une branche test +2) Modifier 1 fichier sous `src/annotations/**` (ex: un champ comment, ts, etc.) +3) PR → merge dans `main` +4) Vérifier dans `deploy-staging-live.yml` : + +Attendus : +- `Gate flags: HAS_FULL=0 HAS_HOTPATCH=1` +- `✅ annotations/media change -> MODE=hotpatch` +- Les étapes FULL sont “skip” (durée 0s) +- L’étape HOTPATCH s’exécute réellement + +### 10.5 — Preuve opérationnelle côté NAS (2 URLs + 2 commandes) + +But : prouver que staging+live servent bien les endpoints essentiels (et que le déploiement n’a pas “fait semblant”). + +#### 10.5.1 — Deux URLs à vérifier (staging et live) + +- Staging (blue) : `http://127.0.0.1:8081/` +- Live (green) : `http://127.0.0.1:8082/` + +#### 10.5.2 — Deux commandes minimales (zéro débat) + +```bash +curl -fsSI http://127.0.0.1:8081/ | head -n 1 +curl -fsSI http://127.0.0.1:8082/ | head -n 1 \ No newline at end of file