130 lines
6.1 KiB
Bash
Executable File
130 lines
6.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Vérifie l'image de production (deploy/) avec le site assemblé (make package) :
|
|
# construit l'image en local, la lance, et contrôle ce que reçoit un
|
|
# navigateur (application, aide, manifeste, service worker, types MIME,
|
|
# compression, politique de sécurité, version), et l'absence des anciennes
|
|
# pages de test du moteur.
|
|
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")/.."
|
|
|
|
unset MAKEFLAGS MFLAGS MAKELEVEL
|
|
make -s package >/dev/null
|
|
|
|
image="ustagammon/site-test:local"
|
|
docker build -q -t "$image" build/deploy >/dev/null
|
|
container="$(docker run -d --rm -p 127.0.0.1::80 "$image")"
|
|
trap 'docker stop "$container" >/dev/null' EXIT
|
|
port="$(docker port "$container" 80/tcp | head -n1 | sed 's/.*://')"
|
|
base="http://127.0.0.1:$port"
|
|
|
|
# Caddy démarre en une fraction de seconde : on attend qu'il réponde.
|
|
for _ in $(seq 1 50); do
|
|
curl -sf -o /dev/null "$base/" && break
|
|
sleep 0.1
|
|
done
|
|
|
|
errors=0
|
|
report() {
|
|
echo "$1"
|
|
errors=$((errors + 1))
|
|
}
|
|
|
|
# check <chemin> <début du type MIME attendu>
|
|
check() {
|
|
local headers
|
|
headers="$(curl -s -D - -o /dev/null "$base/$1")"
|
|
grep -qE "^HTTP/1.1 200" <<<"$headers" || report "/$1 : pas de réponse 200"
|
|
grep -qiE "^Content-Type: $2" <<<"$headers" || report "/$1 : type MIME attendu $2"
|
|
}
|
|
|
|
check "" "text/html"
|
|
check "app.css" "text/css"
|
|
check "app/main.js" "text/javascript"
|
|
check "app/ui/app.js" "text/javascript"
|
|
check "app/domain/game/game.js" "text/javascript"
|
|
check "manifest.webmanifest" "application/manifest\+json"
|
|
# Charte graphique (ADR 0010) : icônes et logo copiés de docs/identite/logo/,
|
|
# police Alegreya Sans et sa licence.
|
|
check "icons/favicon.svg" "image/svg\+xml"
|
|
check "icons/favicon-32.png" "image/png"
|
|
check "icons/apple-touch-icon-180.png" "image/png"
|
|
check "icons/logo-horizontal-couleur.svg" "image/svg\+xml"
|
|
check "icons/logo-horizontal-sombre.svg" "image/svg\+xml"
|
|
check "icons/symbole-couleur.svg" "image/svg\+xml"
|
|
check "icons/symbole-sombre.svg" "image/svg\+xml"
|
|
cmp -s <(curl -s "$base/icons/icone-512.png") docs/identite/logo/icone-512.png \
|
|
|| report "/icons/icone-512.png diffère de docs/identite/logo/"
|
|
for weight in Regular Italic Medium Bold ExtraBold; do
|
|
check "fonts/AlegreyaSans-$weight.woff2" "font/woff2"
|
|
done
|
|
check "fonts/OFL.txt" "text/plain"
|
|
# Chaque fichier désigné par la page (icônes, police, style) est servi.
|
|
page="$(curl -s "$base/")"
|
|
while read -r ref; do
|
|
curl -sf -o /dev/null "$base/$ref" || report "index.html : $ref introuvable"
|
|
done < <(grep -oE '(href|src|srcset)="[^"]*"' <<<"$page" | sed -E 's/^[a-z]+="(.*)"$/\1/')
|
|
grep -qF "url(\"fonts/AlegreyaSans-Regular.woff2\")" <(curl -s "$base/app.css") \
|
|
|| report "app.css : police Alegreya Sans non déclarée"
|
|
check "sw.js" "text/javascript"
|
|
check "engine-worker.mjs" "text/javascript"
|
|
check "wasm/engine.mjs" "text/javascript"
|
|
check "wasm/engine.wasm" "application/wasm"
|
|
check "wasm/engine.data" "application/octet-stream"
|
|
check "version.json" "application/json"
|
|
# Les pages de test du moteur (étape 0b) sont retirées (ADR 0009,
|
|
# amendement du 2026-11-03) : plus rien sous /moteur/.
|
|
[ ! -e build/site/moteur ] || report "build/site/moteur/ existe encore"
|
|
for gone in moteur/ moteur/bench.html; do
|
|
curl -s -o /dev/null -w "%{http_code}" "$base/$gone" | grep -qx 404 || report "/$gone : 404 attendu"
|
|
done
|
|
# Aide : copies du glossaire et des scénarios (ADR 0008).
|
|
check "docs/scenarios/jouer-une-partie.feature" "text/plain"
|
|
check "docs/glossaire/README.md" "text/markdown"
|
|
check "docs/glossaire/videau.md" "text/markdown"
|
|
cmp -s <(curl -s "$base/docs/glossaire/videau.md") docs/glossaire/videau.md \
|
|
|| report "/docs/glossaire/videau.md diffère de la source"
|
|
|
|
# Le manifeste désigne des icônes servies, et l'application à la racine.
|
|
manifest="$(curl -s "$base/manifest.webmanifest")"
|
|
grep -q '"start_url": "./"' <<<"$manifest" || report "manifeste : start_url absent"
|
|
grep -q '"theme_color": "#4A2E1F"' <<<"$manifest" || report "manifeste : theme_color autre que le noyer"
|
|
grep -q '"purpose": "maskable"' <<<"$manifest" || report "manifeste : pas d'icône maskable"
|
|
while read -r icon; do
|
|
curl -sf -o /dev/null "$base/$icon" || report "manifeste : icône $icon introuvable"
|
|
done < <(grep -o '"src": "[^"]*"' <<<"$manifest" | sed 's/"src": "\(.*\)"/\1/')
|
|
|
|
# Le service worker met en cache chaque fichier du site, avec une version.
|
|
sw="$(curl -s "$base/sw.js")"
|
|
grep -qE '^const VERSION = "[0-9a-f]{16}";' <<<"$sw" || report "sw.js : version absente"
|
|
for file in ./ index.html app/main.js wasm/engine.data wasm/engine.wasm \
|
|
docs/glossaire/videau.md version.json fonts/AlegreyaSans-Bold.woff2 icons/favicon.svg; do
|
|
grep -qF " \"$file\"," <<<"$sw" || report "sw.js : $file absent de la liste à mettre en cache"
|
|
done
|
|
listed="$(grep -c '^ "' <<<"$sw")"
|
|
present="$(find build/site -type f ! -name sw.js | wc -l)"
|
|
# Chaque fichier, plus « ./ » pour index.html.
|
|
[ "$listed" -eq "$((present + 1))" ] \
|
|
|| report "sw.js liste $listed adresses pour $present fichiers (plus « ./ »)"
|
|
grep -q '"moteur/' <<<"$sw" && report "sw.js met encore en cache des pages de test du moteur"
|
|
grep -o '^ "[^"]*"' <<<"$sw" | tr -d ' "' | while read -r file; do
|
|
curl -sf -o /dev/null "$base/$file" || echo "sw.js : $file introuvable sur le site"
|
|
done | grep . && report "sw.js liste des fichiers introuvables"
|
|
|
|
# Politique de sécurité du contenu : WebAssembly permis, rien en ligne.
|
|
csp="$(curl -s -D - -o /dev/null "$base/" | grep -i '^Content-Security-Policy:')"
|
|
grep -q "script-src 'self' 'wasm-unsafe-eval';" <<<"$csp" \
|
|
|| report "CSP absente ou sans 'wasm-unsafe-eval'"
|
|
grep -q "unsafe-inline" <<<"$csp" && report "CSP : unsafe-inline ne doit pas être permis"
|
|
curl -s -D - -o /dev/null "$base/engine-worker.mjs" | grep -qi '^Content-Security-Policy:' \
|
|
|| report "CSP absente du Web Worker du moteur"
|
|
|
|
curl -s -H "Accept-Encoding: gzip" -D - -o /dev/null "$base/wasm/engine.data" \
|
|
| grep -qi "^Content-Encoding: gzip" || report "/wasm/engine.data : pas de compression gzip"
|
|
curl -s -D - -o /dev/null "$base/" | grep -qi "^Cache-Control: no-cache" \
|
|
|| report "/ : en-tête Cache-Control absent"
|
|
[ "$(curl -s "$base/version.json" | sed -n 's/.*"commit": "\([0-9a-f]*\)".*/\1/p')" = "$(git rev-parse HEAD)" ] \
|
|
|| report "version.json ne donne pas le commit courant"
|
|
|
|
[ "$errors" -eq 0 ]
|