PG18 de retour, mises à jour nocturnes, tests de reprise et documentation
🚀 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.
This commit is contained in:
maxime.killinger committed 2026-10-02 17:55:42 +02:00
1 parent 2a0f84e81a
commit 7de55c5655
18 files changed
+956 -242

No files matched your search

-106
View File
@@ -1,106 +0,0 @@
name: 🚀 Docker Build and Push
on:
push:
branches:
- main
jobs:
build-and-push:
runs-on: ubuntu-latest
strategy:
matrix:
pg_version: [15, 16, 17]
steps:
- name: 📥 Checkout code
uses: https://github.com/actions/checkout@v4
- name: 🛠️ Set up Docker Buildx
uses: https://github.com/docker/setup-buildx-action@v3
- name: 🔐 Login to Gitea Registry
uses: https://github.com/docker/login-action@v3
with:
registry: gitea.killinger.fr
username: maxime.killinger
password: ${{ secrets.DOCKER_TOKEN }}
- name: 📦 Build Docker image
uses: https://github.com/docker/build-push-action@v5
with:
context: .
load: true
build-args: |
PG_VERSION=${{ matrix.pg_version }}
tags: |
postgres-ts-vectors:pg${{ matrix.pg_version }}-test
- name: 🧪 Test image - Start container
run: |
docker run -d --name test-pg${{ matrix.pg_version }} \
-e POSTGRES_PASSWORD=testpass \
postgres-ts-vectors:pg${{ matrix.pg_version }}-test
# Wait for PostgreSQL to be ready
echo "Waiting for PostgreSQL to start..."
for i in {1..30}; do
if docker exec test-pg${{ matrix.pg_version }} pg_isready -U postgres > /dev/null 2>&1; then
echo "PostgreSQL is ready!"
break
fi
sleep 2
done
- name: ⚙️ Setup Tests
run: |
# Wait for valid startup
sleep 10
docker cp tests/ test-pg${{ matrix.pg_version }}:/tmp/tests/
- name: "🧪 Test 1: Configuration"
run: docker exec test-pg${{ matrix.pg_version }} psql -U postgres -f /tmp/tests/test-1-config.sql
- name: "🧪 Test 2: TimescaleDB"
run: docker exec test-pg${{ matrix.pg_version }} psql -U postgres -f /tmp/tests/test-2-timescale.sql
- name: "🧪 Test 3: VectorChord"
run: docker exec test-pg${{ matrix.pg_version }} psql -U postgres -f /tmp/tests/test-3-vector.sql
- name: "🧪 Test 4: Hybrid Scenarios"
run: docker exec test-pg${{ matrix.pg_version }} psql -U postgres -f /tmp/tests/test-4-hybrid.sql
- name: "🧪 Test 5: PostGIS"
run: docker exec test-pg${{ matrix.pg_version }} psql -U postgres -f /tmp/tests/test-5-postgis.sql
- name: 🧹 Cleanup test container
if: always()
run: docker rm -f test-pg${{ matrix.pg_version }} || true
- name: 🚀 Push Docker image
uses: https://github.com/docker/build-push-action@v5
with:
context: .
push: true
build-args: |
PG_VERSION=${{ matrix.pg_version }}
tags: |
gitea.killinger.fr/maxime.killinger/postgres-ts-vectors:pg${{ matrix.pg_version }}
- name: 🏷️ Tag latest (PG17 only)
if: matrix.pg_version == 17
uses: https://github.com/docker/build-push-action@v5
with:
context: .
push: true
build-args: |
PG_VERSION=17
tags: |
gitea.killinger.fr/maxime.killinger/postgres-ts-vectors:latest
- name: 🧹 Cleanup Docker images
if: always()
run: |
docker rmi postgres-ts-vectors:pg${{ matrix.pg_version }}-test || true
docker image prune -f
+61 -52
View File
@@ -4,26 +4,32 @@ on:
push:
branches:
- main
# Un push qui ne touche que la doc ne reconstruit rien : aucun Markdown n'entre dans
# l'image. Un push mêlant code et doc lance toujours la CI.
paths-ignore:
- '**.md'
- 'docs/**'
# Chaque nuit (heure UTC) : les images publiées sont testées et, si apt leur propose des mises
# à jour, reconstruites et republiées.
# Voir docs/decisions/0005-mises-a-jour-nocturnes-pilotees-par-apt.md.
schedule:
- cron: '30 3 * * *'
workflow_dispatch:
jobs:
build-and-push:
runs-on: ubuntu-latest
strategy:
matrix:
pg_version: [15, 16, 17]
# Accélération (2026-09-27) :
# - aucune action externe : checkout, login et build-push étaient téléchargés depuis GitHub à
# chaque job, ~20 s par action ;
# - construction par le démon Docker du runner, sans Buildx : « Set up Docker Buildx » créait
# un constructeur neuf, sans cache, et TimescaleDB était recompilé à chaque job (7 à 9 min).
# Le cache du démon persiste d'une CI à l'autre : la compilation n'est rejouée que si la
# version de TimescaleDB, de PostgreSQL ou l'image de base change ;
# - une seule construction : l'image testée est celle qui est poussée (étiquetage), au lieu de
# la reconstruire pour le push puis pour `latest`.
pg_version: [15, 16, 17, 18]
# Aucune action externe, construction par le démon Docker du runner (sans Buildx) dont le
# cache persiste d'une CI à l'autre, et une seule construction par version : l'image testée
# est celle qui est poussée. Voir docs/decisions/0004-ci-sans-action-externe.md.
env:
IMAGE: gitea.killinger.fr/${{ github.repository }}
TEST: pg${{ matrix.pg_version }}-test-${{ github.run_id }}
PREVIOUS: postgres-ts-vectors-previous:pg${{ matrix.pg_version }}
steps:
# Le seul commit poussé, sans historique : c'est tout ce que la construction lit.
- name: 📥 Checkout code
env:
TOKEN: ${{ github.token }}
@@ -37,67 +43,70 @@ jobs:
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
run: echo "$DOCKER_TOKEN" | docker login gitea.killinger.fr -u maxime.killinger --password-stdin
# On demande à apt, dans l'image publiée, s'il lui reste des mises à jour :
# - oui, ou image jamais publiée : nouvelle clé APT_REFRESH, les couches apt sont rejouées ;
# - non : la clé de l'image publiée (label apt-refresh) est reprise et le cache sert.
# L'exécution planifiée ne reconstruit ni ne pousse alors rien : elle teste l'image publiée.
# L'image publiée est gardée sous $PREVIOUS : les tests y créent une base, que l'image
# reconstruite doit savoir reprendre.
- name: 🔎 Check for updates
id: updates
run: |
refresh=""; previous=""
if docker pull -q "$IMAGE:pg${{ matrix.pg_version }}" >/dev/null 2>&1; then
docker tag "$IMAGE:pg${{ matrix.pg_version }}" "$PREVIOUS"; previous="$PREVIOUS"
upgrade=$(docker run --rm --entrypoint sh "$PREVIOUS" -c \
'apt-get update -qq -o APT::Update::Error-Mode=any && apt-get -s upgrade --with-new-pkgs')
if pending=$(echo "$upgrade" | grep '^Inst'); then
echo "Mises à jour disponibles :"; echo "$pending"
else
refresh=$(docker image inspect -f '{{ index .Config.Labels "fr.killinger.apt-refresh" }}' "$PREVIOUS")
fi
fi
build=true
if [ -n "$refresh" ] && [ "${{ github.event_name }}" = schedule ]; then
echo "Image publiée à jour : rien à reconstruire"; build=false; previous=""
fi
echo "refresh=${refresh:-$(date -u +%Y%m%dT%H%M%SZ)}" >> "$GITHUB_OUTPUT"
echo "build=$build" >> "$GITHUB_OUTPUT"
echo "previous=$previous" >> "$GITHUB_OUTPUT"
# `--pull` : l'image de base (tensorchord/vchord-postgres) est vérifiée à chaque construction,
# sinon ses correctifs de sécurité ne seraient jamais repris avec le cache persistant.
# pour recevoir une republication sous la même étiquette malgré le cache persistant.
# Labels OCI propres à la construction ; les labels fixes sont dans le Dockerfile.
- name: 📦 Build Docker image
if: steps.updates.outputs.build == 'true'
run: |
docker build --pull \
--build-arg PG_VERSION=${{ matrix.pg_version }} \
--build-arg APT_REFRESH=${{ steps.updates.outputs.refresh }} \
--label fr.killinger.apt-refresh=${{ steps.updates.outputs.refresh }} \
--label org.opencontainers.image.url=https://gitea.killinger.fr/${{ github.repository }} \
--label org.opencontainers.image.revision=${{ github.sha }} \
--label org.opencontainers.image.version=pg${{ matrix.pg_version }} \
--label org.opencontainers.image.created="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-t "$IMAGE:pg${{ matrix.pg_version }}" .
# Prête quand l'initialisation est terminée (extensions créées, serveur temporaire arrêté) ET
# que le serveur définitif accepte les connexions — au lieu d'un `sleep 10` fixe.
- name: 🧪 Test image - Start container
run: |
docker run -d --name "$TEST" -e POSTGRES_PASSWORD=testpass "$IMAGE:pg${{ matrix.pg_version }}" >/dev/null
for i in $(seq 1 90); do
if docker logs "$TEST" 2>&1 | grep -q "PostgreSQL init process complete" \
&& docker exec "$TEST" pg_isready -U postgres -q; then
echo "PostgreSQL prêt en ${i} s"; exit 0
fi
sleep 1
done
docker logs "$TEST" 2>&1 | tail -30
exit 1
# La même commande qu'en local. Sans reconstruction, c'est l'image publiée qui est testée.
- name: 🧪 Tests
run: bash tests.sh "$IMAGE:pg${{ matrix.pg_version }}" ${{ steps.updates.outputs.previous }}
- name: ⚙️ Setup Tests
run: docker cp tests/ "$TEST":/tmp/tests/
- name: "🧪 Test 1: Configuration"
run: docker exec "$TEST" psql -U postgres -f /tmp/tests/test-1-config.sql
- name: "🧪 Test 2: TimescaleDB"
run: docker exec "$TEST" psql -U postgres -f /tmp/tests/test-2-timescale.sql
- name: "🧪 Test 3: VectorChord"
run: docker exec "$TEST" psql -U postgres -f /tmp/tests/test-3-vector.sql
- name: "🧪 Test 4: Hybrid Scenarios"
run: docker exec "$TEST" psql -U postgres -f /tmp/tests/test-4-hybrid.sql
- name: "🧪 Test 5: PostGIS"
run: docker exec "$TEST" psql -U postgres -f /tmp/tests/test-5-postgis.sql
- name: 🧹 Cleanup test container
if: always()
run: docker rm -f "$TEST" >/dev/null 2>&1 || true
# Poussée seulement si tous les tests sont passés (étapes précédentes sans `if: always()`).
# Poussée seulement si les tests sont passés.
- name: 🚀 Push Docker image
if: steps.updates.outputs.build == 'true'
run: docker push -q "$IMAGE:pg${{ matrix.pg_version }}"
# Voir docs/decisions/0006-latest-reste-sur-postgresql-17.md.
- name: 🏷️ Tag latest (PG17 only)
if: matrix.pg_version == 17
if: steps.updates.outputs.build == 'true' && matrix.pg_version == 17
run: |
docker tag "$IMAGE:pg17" "$IMAGE:latest"
docker push -q "$IMAGE:latest"
# Cache borné : il ne doit pas remplir le disque de Tower (la compilation de TimescaleDB
# pour trois versions de PostgreSQL y tient largement).
# Cache borné : il ne doit pas remplir le disque de Tower.
- name: 🧹 Cleanup Docker images
if: always()
run: |
docker rmi "$PREVIOUS" >/dev/null 2>&1 || true
docker builder prune -f --keep-storage 8GB >/dev/null
docker image prune -f >/dev/null