Seed from NAS prod snapshot 20260130-190531
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

This commit is contained in:
archicratia
2026-01-31 10:51:38 +00:00
commit 60d88939b0
142 changed files with 33443 additions and 0 deletions

385
docs/OPS-DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,385 @@
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 {} \;
### 6) basculer current (symlink)
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