🚀 Docker Build and Push / build-and-push (15) (push) Successful in 5m29s
🚀 Docker Build and Push / build-and-push (16) (push) Successful in 6m10s
🚀 Docker Build and Push / build-and-push (17) (push) Successful in 5m20s
🚀 Docker Build and Push / build-and-push (18) (push) Successful in 5m21s
Image - Étape `base` commune : tous les paquets sont mis à niveau (PostgreSQL à la dernière mineure, pgvector, Debian). L'image de base de VectorChord restait figée à la version de sa publication. - En-têtes du serveur épinglés sur la version du serveur installé. PG18 avait été retiré parce que TimescaleDB, compilé avec des en-têtes 18.6, était chargé par un serveur 18.3 (`undefined symbol: palloc0_mul`). - Sonde de santé avec l'utilisateur et la base du conteneur : en `postgres`, elle écrivait une erreur FATAL au journal dès que POSTGRES_USER était défini. - Label `base.name` corrigé : il reprenait la version du paquet au lieu de la version majeure. CI - PG18 ajouté à la matrice. - Exécution nocturne : apt est interrogé dans l'image publiée ; elle n'est reconstruite et republiée que s'il lui reste des mises à jour, sinon elle est seulement testée. Lancement manuel possible. - `docker-build.yml` supprimé : l'ancien workflow à actions externes tournait encore à chaque push, en double de `docker.yml`. - `paths-ignore` : un push de documentation seule ne lance pas la CI. - Labels `version`, `url` et `fr.killinger.apt-refresh`. Tests - `tests.sh`, la même commande en local et en CI : utilisateur et base autres que `postgres`, version majeure du serveur, présence des cinq extensions, redémarrage, et reprise d'une base créée par l'image publiée. Documentation - README en vitrine, CLAUDE.md, docs/architecture.md, docs/exploitation/publication.md, six décisions, docs/a-faire.md.
92 lines
3.7 KiB
Bash
Executable File
92 lines
3.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Tests de l'image, en local comme en CI.
|
|
#
|
|
# ./tests.sh IMAGE démarrage, extensions, tests SQL, redémarrage
|
|
# ./tests.sh IMAGE PRECEDENTE en plus : reprise d'une base créée par l'image PRECEDENTE
|
|
#
|
|
# Tout tourne dans des conteneurs et un volume jetables, sans port publié, supprimés à la fin.
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")"
|
|
|
|
IMAGE="${1:?usage : tests.sh IMAGE [IMAGE_PRECEDENTE]}"
|
|
PRECEDENTE="${2:-}"
|
|
ID="ptvtest$$"
|
|
# Utilisateur et base autres que `postgres`, comme chez les consommateurs de l'image : les
|
|
# extensions doivent être créées dans POSTGRES_DB, par POSTGRES_USER.
|
|
OPTIONS=(-e POSTGRES_USER=test -e POSTGRES_PASSWORD=test -e POSTGRES_DB=test
|
|
--memory=768m --memory-swap=768m) # le runner de CI est limité en mémoire
|
|
|
|
nettoyer() {
|
|
docker rm -f -v "$ID" "$ID-reprise" >/dev/null 2>&1 || true
|
|
docker volume rm "$ID-donnees" >/dev/null 2>&1 || true
|
|
}
|
|
trap nettoyer EXIT
|
|
|
|
sql() { docker exec -i "$1" psql -X -q -v ON_ERROR_STOP=1 -U test -d test "${@:2}"; }
|
|
|
|
# Trois sondes, de la plus exigeante à la moins exigeante.
|
|
# Premier démarrage : le serveur temporaire de l'initialisation répond déjà aux requêtes, avant
|
|
# que les extensions soient créées ; il faut aussi la fin de l'initialisation dans le journal.
|
|
# Le journal est lu en entier avant la recherche : `grep -q` branché sur `docker logs` le coupe
|
|
# dès la première correspondance, ce que `pipefail` compte comme un échec.
|
|
initialise() {
|
|
local journal
|
|
journal=$(docker logs "$1" 2>&1)
|
|
grep -q "PostgreSQL init process complete" <<<"$journal" && repond "$1"
|
|
}
|
|
repond() { sql "$1" -Atc 'select 1' >/dev/null 2>&1; }
|
|
# Sans requête : après un changement de version de TimescaleDB, toute requête échoue tant que
|
|
# l'extension n'est pas mise à niveau (tests/reprise-2-controle.sql).
|
|
accepte() { docker exec "$1" pg_isready -q -U test -d test; }
|
|
|
|
attendre() { # attendre SONDE CONTENEUR
|
|
for _ in $(seq 1 90); do
|
|
"$1" "$2" && return 0
|
|
sleep 1
|
|
done
|
|
docker logs "$2" 2>&1 | tail -30 >&2
|
|
echo "ÉCHEC : $2 ne démarre pas" >&2
|
|
return 1
|
|
}
|
|
|
|
echo "== Démarrage de $IMAGE"
|
|
docker run -d --name "$ID" "${OPTIONS[@]}" "$IMAGE" >/dev/null
|
|
attendre initialise "$ID"
|
|
|
|
# L'étiquette pgN doit contenir un serveur N : PG_MAJOR vient de l'image de base.
|
|
attendue=$(docker exec "$ID" printenv PG_MAJOR)
|
|
servie=$(sql "$ID" -Atc "select current_setting('server_version_num')::int / 10000")
|
|
[[ "$servie" == "$attendue" ]] || { echo "ÉCHEC : serveur $servie, image pg$attendue" >&2; exit 1; }
|
|
|
|
for fichier in tests/test-*.sql; do
|
|
echo "== $fichier"
|
|
sql "$ID" < "$fichier"
|
|
done
|
|
|
|
echo "== Redémarrage sur des données existantes"
|
|
docker restart "$ID" >/dev/null
|
|
attendre repond "$ID"
|
|
sql "$ID" < tests/test-1-config.sql
|
|
|
|
if [[ -n "$PRECEDENTE" ]]; then
|
|
echo "== Reprise d'une base créée par $PRECEDENTE"
|
|
# Le volume se monte là où l'image déclare le sien : /var/lib/postgresql/data jusqu'à
|
|
# PostgreSQL 17, /var/lib/postgresql à partir de 18.
|
|
montage=$(docker image inspect -f '{{range $chemin, $_ := .Config.Volumes}}{{$chemin}}{{end}}' "$IMAGE")
|
|
docker volume create "$ID-donnees" >/dev/null
|
|
docker run -d --name "$ID-reprise" "${OPTIONS[@]}" -v "$ID-donnees:$montage" "$PRECEDENTE" >/dev/null
|
|
attendre initialise "$ID-reprise"
|
|
sql "$ID-reprise" < tests/reprise-1-semis.sql
|
|
docker stop "$ID-reprise" >/dev/null
|
|
docker rm "$ID-reprise" >/dev/null
|
|
|
|
docker run -d --name "$ID-reprise" "${OPTIONS[@]}" -v "$ID-donnees:$montage" "$IMAGE" >/dev/null
|
|
attendre accepte "$ID-reprise"
|
|
sql "$ID-reprise" < tests/reprise-2-controle.sql
|
|
fi
|
|
|
|
echo "== Versions"
|
|
sql "$ID" -At -c "select version()" \
|
|
-c "select string_agg(extname || ' ' || extversion, ', ' order by extname) from pg_extension"
|
|
echo "Tests réussis : $IMAGE"
|