Files
archicratie-edition/docs/OPS-DEPLOYMENT.md
archicratia 60d88939b0
All checks were successful
CI / build-and-anchors (push) Successful in 1m25s
SMOKE / smoke (push) Successful in 11s
CI / build-and-anchors (pull_request) Successful in 1m20s
Seed from NAS prod snapshot 20260130-190531
2026-01-31 10:51:38 +00:00

15 KiB
Raw Blame History

OPS — Déploiement Archicratie Web Edition (Mac Studio → DS220+) Objectif : déployer une nouvelle version du site sur le NAS (DS220+) sans jamais casser la prod, en utilisant un schéma blue/green piloté par DSM Reverse Proxy, avec une procédure robuste même quand docker compose build est instable sur le NAS.

0) Repères essentiels

Noms & domaines • Site public (prod) : https://archicratie.trans-hands.synology.me • Gitea public : https://gitea.archicratie.trans-hands.synology.me Attention : tu as eu un “piège” de typo (trans-hands-synology.me ≠ trans-hands.synology.me). Toujours vérifier lorthographe exacte. Ports locaux NAS • web_blue : 127.0.0.1:8081 → container:80 • web_green : 127.0.0.1:8082 → container:80 • DSM Reverse Proxy pointe soit vers 8081 soit vers 8082. Règle dor “safe prod” • On ne touche jamais au slot live (celui pointé par DSM). • On construit/teste sur lautre slot. • On bascule DSM (10 secondes). • On rollback DSM (10 secondes) si besoin.

1) Blue/Green : qui fait quoi ?

Convention simple et pro • blue = slot A (souvent “actif” par défaut) → 8081 • green = slot B (staging/next) → 8082 Mais : la vérité = DSM. Ce nest pas “blue” ou “green” qui décide : cest le Reverse Proxy DSM qui pointe vers 8081 ou 8082. ➡️ Donc : • blue et green sont deux environnements identiques • lun est “live”, lautre est “next” • après bascule, les rôles sinversent

2) Arborescence NAS (standard à stabiliser)

Chemin racine : • /volume2/docker/archicratie-web/ Sous-dossiers recommandés : • incoming/ : dépôt darchives venant du Mac (upload File Station / scp) • releases/ : releases dépliées, horodatées • current : symlink vers la release “courante” (le code utilisé pour builder) • ops/ : scripts dexploitation (smoke, which-live, helpers) • current__backup_before_cleanup/ : backup historique (optionnel) Exemple sain : /volume2/docker/archicratie-web/ incoming/ archicratie-web-20260130-104937.tar.gz archicratie-web-20260130-104937.tar.gz.sha256 releases/ 20260130-104937/ app/ (code déplié prêt) current -> releases/20260130-104937/app ops/ smoke.sh which-live.sh

3) Permissions NAS (ce qui est “propre”)

Tu as eu des problèmes de release dépliée “root:root” → impossible à lire en user. Politique simple • propriétaire : archicratia:users • dossiers : 750 • fichiers : 640 • incoming peut être 770 si tu y upload souvent via File Station. Commandes (à lancer à la racine) : BASE="/volume2/docker/archicratie-web"

sudo chown -R archicratia:users "$BASE" sudo chmod 750 "$BASE" "$BASE/ops" "$BASE/releases" "$BASE/current__backup_before_cleanup" 2>/dev/null || true sudo chmod 770 "$BASE/incoming" 2>/dev/null || true

Important : options find AVANT les tests

find "$BASE/releases" -maxdepth 3 -type d -exec chmod 750 {} ; find "$BASE/releases" -maxdepth 3 -type f -exec chmod 640 {} ;

⚠️ Éviter chmod 700 * “au hasard” : ça peut te bloquer toi-même + bloquer des outils DSM. Le trio 750/640 est ton bon compromis.

4) Variables Gitea (les 3 variables qui conditionnent “Proposer”)

Dans .env sur le NAS (dans current/ ou dans la release) : PUBLIC_GITEA_BASE=https://gitea.archicratie.trans-hands.synology.me PUBLIC_GITEA_OWNER=Archicratia PUBLIC_GITEA_REPO=archicratie-edition

Point critique : la casse du OWNER Tu as déjà vu le symptôme : • archicratia/archicratie-edition → peut renvoyer 404 • Archicratia/archicratie-edition → OK Donc : OWNER doit être exactement la casse que Gitea attend. Test rapide (NAS) : BASE="https://gitea.archicratie.trans-hands.synology.me" curl -kI "$BASE/Archicratia/archicratie-edition/" | head -n 8 attendu : HTTP/2 200

5) Côté Mac Studio : préparer une release “propre” (sans scories macOS)

Pourquoi Tu as eu : • fichiers ._* • xattrs LIBARCHIVE.xattr.com.apple.* • warnings à lextraction ➡️ Ce nest pas bloquant, mais cest sale et ça te pollue les releases. Script Mac : release-pack.sh (sans Git, ultra simple) À placer à la racine du repo site/ sur Mac. #!/bin/zsh set -euo pipefail

TS="${1:-$(date +%Y%m%d-%H%M%S)}" NAME="archicratie-web-$TS" OUT_DIR="_release_out" ARCHIVE="$OUT_DIR/$NAME.tar.gz" SHA="$ARCHIVE.sha256"

mkdir -p "$OUT_DIR"

Évite les AppleDouble (._*) + certaines métadonnées

export COPYFILE_DISABLE=1 export COPY_EXTENDED_ATTRIBUTES_DISABLE=1

Nettoyages “safe”

find . -name ".DS_Store" -delete 2>/dev/null || true

Crée un dossier staging

STAGE="$(mktemp -d)" mkdir -p "$STAGE/$NAME"

Copie le repo SANS déchets

rsync -a --delete
--exclude ".git/"
--exclude "node_modules/"
--exclude "dist/"
--exclude ".astro/"
--exclude "release_out/"
--exclude ".DS_Store"
--exclude "__MACOSX/"
--exclude ".
*"
./ "$STAGE/$NAME/"

Tar propre

tar -czf "$ARCHIVE" -C "$STAGE" "$NAME"

Checksum

shasum -a 256 "$ARCHIVE" > "$SHA"

echo "OK: $ARCHIVE" echo "OK: $SHA"

Usage : chmod +x release-pack.sh ./release-pack.sh 20260130-104937

Résultat : • _release_out/archicratie-web-20260130-104937.tar.gz • _release_out/archicratie-web-20260130-104937.tar.gz.sha256

6) Transfert vers le NAS

Option A — DSM File Station (simple) • Upload les 2 fichiers dans : ◦ /volume2/docker/archicratie-web/incoming/ Option B — scp (si SSH) Depuis le Mac : scp _release_out/archicratie-web-20260130-104937.tar.gz*
archicratia@192.168.1.20:/volume2/docker/archicratie-web/incoming/

7) NAS : vérifier + déplier une release (procédure canonique)

BASE="/volume2/docker/archicratie-web" TS="20260130-104937"

cd "$BASE"

1) checksum (IMPORTANT)

sha256sum -c "incoming/archicratie-web-$TS.tar.gz.sha256"

2) créer dossier release

rm -rf "releases/$TS" mkdir -p "releases/$TS"

3) extraire

tar -xzf "incoming/archicratie-web-$TS.tar.gz" -C "releases/$TS"

4) pointer APP vers le dossier extrait

APP="$BASE/releases/$TS/archicratie-web-$TS"

option: normaliser en "app"

mv "$APP" "$BASE/releases/$TS/app" APP="$BASE/releases/$TS/app"

5) ownership + perms

sudo chown -R archicratia:users "$BASE/releases/$TS" find "$BASE/releases/$TS" -type d -exec chmod 750 {} ; find "$BASE/releases/$TS" -type f -exec chmod 640 {} ;

ln -sfn "$APP" "$BASE/current"

7) sanity check : compose présent ?

ls -la "$BASE/current/docker-compose.yml" "$BASE/current/Dockerfile" "$BASE/current/nginx.conf" "$BASE/current/.env"

Cas réel déjà rencontré : la release “na pas les fichiers ops” Tu as eu un tar “inner” (archicratie-web.tar.gz) qui nembarque pas docker-compose.yml/Dockerfile/nginx.conf/.env. Règle pro : ces fichiers doivent être dans la release. Fix immédiat si cest absent (tu las déjà fait) : OPS_SRC="$BASE/releases/20260129-174516_clean" cp -a "$OPS_SRC/docker-compose.yml" "$APP/" cp -a "$OPS_SRC/Dockerfile" "$APP/" cp -a "$OPS_SRC/nginx.conf" "$APP/" cp -a "$OPS_SRC/.env" "$APP/.env"

8) Build GREEN sur NAS (robuste) — IMPORTANT

Pourquoi docker compose build nest PAS fiable sur DS220+ Tu as vu : • Temporary failure resolving 'deb.debian.org' • apt-get update qui part en Ign: puis Err: ➡️ En pratique sur ton NAS : le builder na pas toujours le bon réseau, même si build: network: host est déclaré. Procédure “qui marche vraiment” (golden path) ➡️ On build en host network avec docker build, puis on déploie avec compose sans rebuild. cd /volume2/docker/archicratie-web/current

charge les env vars du .env

set -a . ./.env set +a

BUILD image green (host network)

sudo docker build --no-cache --network host
--build-arg PUBLIC_GITEA_BASE="$PUBLIC_GITEA_BASE"
--build-arg PUBLIC_GITEA_OWNER="$PUBLIC_GITEA_OWNER"
--build-arg PUBLIC_GITEA_REPO="$PUBLIC_GITEA_REPO"
-t archicratie-web:green
-f Dockerfile .

Voir Ign: pendant apt-get nest pas forcément une erreur. Ce qui compte : est-ce quil finit par télécharger / réussir. On salarme uniquement si on voit Err: ... Temporary failure resolving + exit code non-zéro.

9) Démarrer GREEN (sans impacter BLUE)

cd /volume2/docker/archicratie-web/current

ne jamais lancer "up" sans préciser le service

sudo docker rm -f archicratie-web-green 2>/dev/null || true sudo docker compose up -d --force-recreate --no-build web_green Smoke test (port 8082) : /volume2/docker/archicratie-web/ops/smoke.sh 8082

10) DSM : basculer le Reverse Proxy (10 secondes)

DSM → Panneau de configuration → Portail de connexion (ou “Portail des applications”) → Proxy inversé Trouve la règle : • Source : https://archicratie.trans-hands.synology.me (443) Destination : • pour BLUE : http://127.0.0.1:8081 • pour GREEN : http://127.0.0.1:8082 Tu changes seulement le port, tu “Appliques”. Test immédiat : curl -kI https://archicratie.trans-hands.synology.me/ | head -n 8

11) Rollback (10 secondes)

Si ça ne va pas : • DSM Reverse Proxy → remettre lancien port (8081 ou 8082) • refresh navigateur Option pro : ne stoppe pas lautre container tout de suite (tu peux comparer / investiguer calmement).

12) Ops scripts (à garder dans /volume2/docker/archicratie-web/ops)

smoke.sh (version “complète”) #!/bin/sh set -eu

PORT="${1:-8081}" BASE="http://127.0.0.1:${PORT}"

echo "Smoke test ${BASE}"

curl -fsSI "${BASE}/" | head -n 5 curl -fsSI "${BASE}/pagefind/pagefind.js" | head -n 5 curl -fsSI "${BASE}/pagefind/pagefind-ui.js" | head -n 5 || true curl -fsSI "${BASE}/pagefind/pagefind-ui.css" | head -n 5 || true curl -fsSI "${BASE}/pagefind/pagefind-entry.json" | head -n 5 || true

echo "OK"

which-live.sh (déduire quel port est live) Idée : on compare les Last-Modified ou ETag entre le domaine public et les ports locaux. #!/bin/sh set -eu

DOMAIN="https://archicratie.trans-hands.synology.me" A="http://127.0.0.1:8081" B="http://127.0.0.1:8082"

etag() { curl -ksI "$1/" | awk -F': ' 'tolower($1)=="etag"{print $2}' | tr -d '\r'; } lm() { curl -ksI "$1/" | awk -F': ' 'tolower($1)=="last-modified"{print $2}' | tr -d '\r'; }

E_D="$(etag "$DOMAIN")" E_A="$(etag "$A")" E_B="$(etag "$B")"

echo "DOMAIN ETag: $E_D" echo "8081 ETag: $E_A" echo "8082 ETag: $E_B"

if [ -n "$E_D" ] && [ "$E_D" = "$E_A" ]; then echo "LIVE=8081 (blue slot probable)" elif [ -n "$E_D" ] && [ "$E_D" = "$E_B" ]; then echo "LIVE=8082 (green slot probable)" else echo "LIVE=INCONNU (ETag mismatch) — check Last-Modified:" echo "DOMAIN: $(lm "$DOMAIN")" echo "8081 : $(lm "$A")" echo "8082 : $(lm "$B")" fi

13) Pannes & diagnostics (les vrais cas rencontrés)

A) 504 via DSM Reverse Proxy

Symptôme : HTTP/2 504 côté domaine. Check : 1. le container répond en local : curl -I http://127.0.0.1:8081/ curl -I http://127.0.0.1:8082/ 2. DSM Reverse Proxy destination = http://127.0.0.1:808X (pas https) 3. ports bindés en loopback (recommandé) : 127.0.0.1:808X:80 4. logs container : sudo docker logs --tail=80 archicratie-web-blue sudo docker logs --tail=80 archicratie-web-green

B) “Proposer” ouvre Gitea mais tombe sur 404

Cest quasiment toujours lun de ces trois points : 1. OWNER/REPO incorrects (casse) Test : BASE="https://gitea.archicratie.trans-hands.synology.me" curl -kI "$BASE/Archicratia/archicratie-edition/" | head • Si 404 → cest ton .env / build args. • La build a embarqué une ancienne config Vérifie dans le HTML servi (dans le container) : sudo docker exec -it archicratie-web-green sh -lc
'grep -Rin "const GITEA_BASE" /usr/share/nginx/html | head -n 20' 1. Tu dois voir https://gitea.archicratie.trans-hands.synology.me + bon OWNER/REPO. 2. Problème dauth / droits Gitea Si tu es loggé mais “pas autorisé”, vérifier que tu as accès au repo et que lURL pointe au bon repo.

C) “Proposer” échoue avant Gitea (pas de pop-up, pas de 2 choix)

Check : • console navigateur (JS) • vérifier que le script “Proposer” est bien injecté dans la page (view source) • tester une page simple (ex: /archicratie/)

D) Pagefind nindexe plus

Ce que tu as déjà observé : • Pagefind ignore les pages sans data-pagefind-body • Tu avais bien ~12 pages indexées (log Pagefind) Check dans le container : sudo docker exec -it archicratie-web-green sh -lc
'ls -la /usr/share/nginx/html/pagefind | head' Note : • GET /pagefind/ peut renvoyer 403 (listing interdit) : ce nest pas un bug • tu testes plutôt : ◦ /pagefind/pagefind.js ◦ /pagefind/pagefind-entry.json

E) docker compose build casse sur apt-get update (DNS)

Symptôme : • Temporary failure resolving 'deb.debian.org' Diagnostic rapide : sudo docker run --rm --network host node:22-bookworm-slim sh -lc
'apt-get -o Acquire::ForceIPv4=true update -qq && echo OK_APT' si OK_APT : ton NAS sait sortir, cest le builder compose qui est instable → utiliser la golden path : docker build --network host.

14) Dockerfile : version robuste “NAS host-network”

Dans ton Dockerfile, garde : RUN --network=host apt-get update
&& apt-get install -y --no-install-recommends ca-certificates git
&& rm -rf /var/lib/apt/lists/*

Option “anti-fragile” (retries + IPv4) : RUN --network=host apt-get -o Acquire::Retries=5 -o Acquire::ForceIPv4=true update
&& apt-get install -y --no-install-recommends ca-certificates git
&& rm -rf /var/lib/apt/lists/*

15) Important : ne pas se faire piéger par le docker.sock

Sur ton DS220+, /var/run/docker.sock est root:root avec 660 → ton user ne peut pas accéder Docker sans sudo. ➡️ Règle : • commandes Docker = sudo docker ... • compose = sudo docker compose ... (Optionnel : alias) alias d='sudo docker' alias dc='sudo docker compose'

16) Workflow résumé “cockpit” (le plus important)

Depuis le Mac : 1. ./release-pack.sh 20260130-104937 2. upload _release_out/.tar.gz → NAS incoming/ Sur le NAS : 1. vérifier checksum 2. déplier releases/$TS + chown/chmod 3. ln -sfn releases/$TS/app current 4. build green : ◦ sudo docker build --network host ... -t archicratie-web:green . 5. run green : ◦ sudo docker compose up -d --force-recreate --no-build web_green 6. smoke : ◦ /volume2/docker/archicratie-web/ops/smoke.sh 8082 7. switch DSM Reverse Proxy 8081 ↔ 8082 8. si souci → rollback DSM

Post-scriptum : ton problème local Mac (Astro “Cannot find module astro/config”) Ce symptôme arrive typiquement quand : • node_modules est incomplet/corrompu • tu as basculé de dossier (ex: un _release_out/... a été pris pour le vrai repo) • ou le process dev a reload sur un tsconfig “nouveau” mais pas les deps Fix standard (dans le vrai dossier site/) : rm -rf node_modules .astro dist npm ci npm run dev