test-env: podman-compose fresh-Debian harness + ssh/rsync deploy shims

Add a test environment that simulates a blank Debian trixie box (systemd
+ sshd container) and routes the project's real 
📦 Linting CSS + JS (biome)…
Checked 71 files in 140ms. No fixes applied.

📦 Building CSS bundles…
🎨 Building CSS bundles…

  ✓ base.min.css (22,317 bytes)
  ✓ admin.min.css (55,602 bytes)
  ✓ form.min.css (41,855 bytes)
  ✓ public.min.css (4,310 bytes)
  ✓ tfe.min.css (9,089 bytes)
  ✓ repertoire.min.css (13,166 bytes)
  ✓ content-page.min.css (3,683 bytes)
  ✓ not-found.min.css (672 bytes)
  ✓ system.min.css (7,408 bytes)
  ✓ file-access.min.css (3,733 bytes)
  ✓ form-base.min.css (19,110 bytes)
  ✓ partage-form.min.css (37,229 bytes)

✅ CSS bundles done — 218,174 bytes total


📦 Building JS bundles…
📦 Building JS bundles…

  ✓ admin.min.js (60,911 bytes)
  ✓ public.min.js (22,022 bytes)
  ✓ form.min.js (41,547 bytes)
  ✓ partage.min.js (42,441 bytes)

✅ JS bundles done


✅ Build complete


              0   0%    0.00kB/s    0:00:00 (xfr#0, to-chk=461/469)
              0   0%    0.00kB/s    0:00:00 (xfr#0, to-chk=458/469)
              0   0%    0.00kB/s    0:00:00 (xfr#0, to-chk=411/469)
              0   0%    0.00kB/s    0:00:00 (xfr#0, to-chk=411/469)
              0   0%    0.00kB/s    0:00:00 (xfr#0, to-chk=401/469)
              0   0%    0.00kB/s    0:00:00 (xfr#0, to-chk=360/469)
              0   0%    0.00kB/s    0:00:00 (xfr#0, to-chk=325/469)
              0   0%    0.00kB/s    0:00:00 (xfr#0, to-chk=319/469)
              0   0%    0.00kB/s    0:00:00 (xfr#0, to-chk=309/469)
              0   0%    0.00kB/s    0:00:00 (xfr#0, to-chk=302/469)
              0   0%    0.00kB/s    0:00:00 (xfr#0, to-chk=280/469)
            700   0%    0.00kB/s    0:00:00  
         55,602   1%   52.36MB/s    0:00:00 (xfr#1, to-chk=265/469)
         55,602   1%   52.36MB/s    0:00:00 (xfr#1, to-chk=250/469)
        116,513   2%  110.45MB/s    0:00:00 (xfr#2, to-chk=264/469)
        138,830   2%  131.73MB/s    0:00:00 (xfr#3, to-chk=263/469)
        142,513   2%  135.24MB/s    0:00:00 (xfr#4, to-chk=262/469)
        146,246   2%  138.80MB/s    0:00:00 (xfr#5, to-chk=261/469)
        165,356   2%  157.03MB/s    0:00:00 (xfr#6, to-chk=260/469)
        207,211   3%  196.94MB/s    0:00:00 (xfr#7, to-chk=259/469)
        248,758   4%  236.57MB/s    0:00:00 (xfr#8, to-chk=258/469)
        249,430   4%  237.21MB/s    0:00:00 (xfr#9, to-chk=257/469)
        286,659   5%  272.71MB/s    0:00:00 (xfr#10, to-chk=256/469)
        329,100   5%  313.19MB/s    0:00:00 (xfr#11, to-chk=255/469)
        333,410   6%  317.30MB/s    0:00:00 (xfr#12, to-chk=254/469)
        355,432   6%  338.30MB/s    0:00:00 (xfr#13, to-chk=253/469)
        368,598   6%  350.85MB/s    0:00:00 (xfr#14, to-chk=252/469)
        376,006   6%  357.92MB/s    0:00:00 (xfr#15, to-chk=251/469)
        385,095   6%  366.59MB/s    0:00:00 (xfr#16, to-chk=250/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=250/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=235/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=230/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=193/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=192/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=189/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=156/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=156/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=147/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=135/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=134/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=129/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=120/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=93/469) 
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=78/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=77/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=71/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=71/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=64/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=43/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=39/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=34/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=33/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=25/469)
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=8/469) 
        385,095   6%    2.96MB/s    0:00:00 (xfr#16, to-chk=0/469)
📋 Deploying nginx configuration…
xamxam.conf

sent 145 bytes  received 125 bytes  540.00 bytes/sec
total size is 10,012  speedup is 37.08
deploy-server.sh

sent 1,089 bytes  received 107 bytes  797.33 bytes/sec
total size is 8,519  speedup is 7.12 recipes to it
via safe ssh/rsync shims, so setup scripts can be validated without touching
production. Includes provision-server-packages.sh, setup.sh/teardown.sh,
server Dockerfile + helper, and the rendered ssh config.

Validated end-to-end against the box:
- provisioning (apt nginx, php8.4-fpm, composer),
- scripts/setup-server.sh (group/user/dir bootstrap),
- just deploy-code (transfer; surfaced the deploy-code --chown regression).
This commit is contained in:
Pontoporeia
2026-09-18 16:26:49 +02:00
parent 3f352b0d26
commit 64fd92b913
14 changed files with 686 additions and 31 deletions
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
# Install the XAMXAM server runtime stack on the fresh Debian box.
#
# This is the "apt-get install" bootstrap you'd run manually on a greenfield
# Debian server (Debian trixie — ships PHP 8.4, which the project requires)
# before running the project's setup/deploy scripts. It installs only the
# packages; the project's own scripts (setup-server.sh / deploy-server.sh /
# provision-server-env.sh / just deploy*) handle directories, permissions,
# nginx config, .env and migrations, so the recipes stay the thing under test.
#
# Run on the HOST through the test-env shim so `xamxam` resolves to the podman
# box (NOT production):
# PATH=test-env/bin:$PATH bash test-env/scripts/provision-server-packages.sh
# or
# just test-env-provision
#
# The install block is written to a temp file, rsynced to the box and run with
# `sudo bash` (mirroring how the project's own scripts are deployed and run).
set -euo pipefail
TARGET="${1:-xamxam}"
INSTALL_SH="/tmp/xamxam-provision-packages.sh"
cat > "$INSTALL_SH" <<'REMOTE'
#!/usr/bin/env bash
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
# Rootless podman can leave /var/lib/apt/lists/partial missing, which makes
# apt-get update silently fail. Recreate it before updating.
install -d -m 755 /var/lib/apt/lists/partial 2>/dev/null || mkdir -p /var/lib/apt/lists/partial
chown _apt:root /var/lib/apt/lists/partial 2>/dev/null || true
apt-get update
# ── Web + PHP 8.4 ──────────────────────────────────────────────────────────
# nginx ships /etc/nginx/snippets/fastcgi-php.conf (referenced by the project
# xamxam.conf). php8.4-fpm exposes unix:/run/php/php8.4-fpm.sock (the socket
# path the nginx config fastcgi_passes to).
apt-get install -y --no-install-recommends \
nginx \
php8.4-fpm \
php8.4-cli \
php8.4-curl \
php8.4-sqlite3 \
php8.4-mbstring \
php8.4-xml
# ── Deploy utilities (what the just recipes call on the server) ────────────
# composer installs deps during `just deploy-deps`; `just` runs recipes.
apt-get install -y --no-install-recommends \
composer \
sqlite3 \
gzip \
rsync \
git \
just \
openssh-client
REMOTE
echo "▶ Installing XAMXAM server packages on $TARGET (Debian trixie)…"
# No -e here: the test-env rsync shim injects the correct ssh transport
# (`real-rsync -e "/usr/sbin/ssh -F …/config" …`). Passing `-e ssh` ourselves
# would override the shim and route to the host's real ~/.ssh/config (prod!).
rsync -a "$INSTALL_SH" "$TARGET:$INSTALL_SH"
ssh "$TARGET" "sudo bash '$INSTALL_SH'"
ssh "$TARGET" "rm -f '$INSTALL_SH'"
rm -f "$INSTALL_SH"
echo "▶ Ensuring php8.4-fpm and nginx start automatically with systemd…"
ssh "$TARGET" "sudo systemctl enable --now nginx php8.4-fpm" || true
echo "✓ Packages installed. Next:"
echo " just test-env-run # just deploy (app + nginx + deps + migrate)"
echo " just test-env-run recipe=setup-server # role/user/dir bootstrap"
echo " just test-env-run recipe=provision-server # full server provisioning chain"
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env bash
# Host-side bootstrap for the XAMXAM podman test environment.
#
# 1. Generates a throwaway test SSH keypair in test-env/keys/.
# 2. Builds + starts the `server` (Debian trixie systemd ssh container).
# 3. Installs the public key into the server's `deploy` user and gives that
# user passwordless sudo (so the just deploy recipes' `sudo` calls run
# unattended — see README for the password option).
# 4. Renders test-env/ssh/config — an SSH config that routes the `xamxam` alias
# to the podman box, used only by the `just test-env-run` recipes.
#
# Run from the host (repo root):
# just test-env-up # == test-env/scripts/setup.sh
# test-env/scripts/setup.sh
# test-env/scripts/setup.sh --rebuild
#
set -euo pipefail
cd "$(cd "$(dirname "$0")/.." && pwd)" # -> test-env/
KEYS_DIR="$PWD/keys"
KEY="$KEYS_DIR/xamxam-test_ed25519"
PUBKEY="$KEY.pub"
SSH_CONF="$PWD/ssh/config"
DEPLOY_USER="deploy"
PORT="22022"
mkdir -p "$KEYS_DIR" "$PWD/ssh"
if [ ! -f "$KEY" ]; then
echo "▶ Generating throwaway test SSH keypair…"
ssh-keygen -t ed25519 -N '' -C "xamxam-test" -f "$KEY" -q
chmod 600 "$KEY"
echo "✓ $KEY"
fi
echo "▶ Building images…"
if [ "${1:-}" = "--rebuild" ]; then
podman compose build --no-cache
else
podman compose build
fi
echo "▶ Starting server (systemd container)…"
podman compose up -d server
# systemd containers don't always order sshd at boot (no real network manager);
# start it explicitly so the host ssh can connect.
podman compose exec -T server systemctl start ssh 2>/dev/null || true
echo "▶ Waiting for sshd inside server…"
for i in $(seq 1 30); do
if podman compose exec -T server bash -c 'pgrep -x sshd >/dev/null' 2>/dev/null; then
break
fi
sleep 1
done
echo "▶ Installing public key + deploy user with passwordless sudo…"
PUB="$(cat "$PUBKEY")"
podman compose exec -T server /usr/local/bin/helper add-key "$PUB" "xamxam"
echo "▶ Rendering $SSH_CONF…"
sed -e "s|{{PORT}}|$PORT|g" \
-e "s|{{DEPLOY_USER}}|$DEPLOY_USER|g" \
-e "s|{{KEYFILE}}|$KEY|g" \
ssh/config.template > "$SSH_CONF"
chmod 600 "$SSH_CONF"
echo
echo "✓ Test environment is up."
echo
echo " ssh to the box : ssh -p $PORT -i $KEY $DEPLOY_USER@127.0.0.1"
echo " test SSH config: $SSH_CONF (used only by the shim)"
echo
echo "Now drive the real just recipes on the HOST via the test-env-run shim:"
echo " just test-env-run # just deploy"
echo " just test-env-run recipe=provision-server-packages # apt installs"
echo " just test-env-run recipe=setup-server # role/dirs"
echo " just test-env-run recipe=deploy-nginx # nginx"
echo " just test-env-run recipe=deploy-db"
echo " just test-env-run recipe=provision-server # full chain"
echo
echo "See test-env/README.md for the full matrix of what each step validates."
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
# Teardown the XAMXAM podman test environment.
# Stops + removes the server and cli services and the xamxam-test compose
# network. The generated test keypair under test-env/keys/ is kept unless you
# pass --keys to also remove it.
set -euo pipefail
cd "$(cd "$(dirname "$0")/.." && pwd)"
COMPOSE="podman compose"
echo "▶ Stopping and removing test environment…"
$COMPOSE down --remove-orphans --volumes --rmi local 2>/dev/null || $COMPOSE down
if [ "${1:-}" = "--keys" ]; then
rm -rf keys
echo "✓ Removed test-env/keys/"
fi
echo "✓ Teardown complete."