Files
Pontoporeia 64fd92b913 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).
2026-09-18 16:26:49 +02:00
..

XAMXAM test environment — a fresh Debian box you can deploy to

test-env/ spins up a throwaway Debian trixie systemd container that behaves like a brand-new server with nothing preinstalled, then drives the project's real setup scripts and just deploy recipes against it over SSH — without ever touching the production host.

This is how you verify the setup scripts actually work before trusting them on real hardware.


How it works

            HOST (you)                          podman network
 ┌─────────────────────────────┐        ┌──────────────────────────────┐
 │  just test-env-run …        │ ssh /  │  server  = Debian trixie     │
 │   uses test-env/bin/* shims │ rsync  │  systemd + sshd :22022       │
 │   (ssh/rsync route `xamxam` │───────▶│  "fresh box"                 │
 │   → 127.0.0.1:22022)        │        └──────────────────────────────┘
 └─────────────────────────────┘
  • The just recipes run unmodified on your host. All they know is an SSH host named xamxam. Normally that alias points at production; with the test env it points at the container.
  • Two tiny shims, test-env/bin/ssh and test-env/bin/rsync, are put on PATH only inside test-env-run. They force the test SSH config (test-env/ssh/config), so every ssh xamxam … / rsync … xamxam:/… hits the container. Your real ~/.ssh/config is never touched.
  • Safety: the shims always force the test transport. A caller-supplied rsync -e is replaced, and when none is given the shim injects its own so rsync can never fall back to the host's real config (which maps xamxam → PRODUCTION).
  • The systemd container runs nginx + php-fpm as real services, so systemctl, nginx -t, socket paths etc. all behave like a real box.

What it tests

Step File Does
Stack install test-env/scripts/provision-server-packages.sh apt-get nginx, php8.4-fpm, php-cli/curl/sqlite3/mbstring/xml, composer, sqlite3, rsync, git, just
Server setup scripts/setup-server.sh (real) xamxam group, deploy+www-data membership, /var/www/xamxam, 2775/664 perms, cache/log/backup dirs
Deploy just deploy (real) build, deploy-code, deploy-nginx (→ scripts/deploy-server.sh), deploy-deps, deploy-migrate, deploy-env, deploy-verify-permissions
Nginx config nginx/xamxam.conf (real) installed via deploy-nginx, validated with nginx -t, reloaded
First provision scripts/provision-server-env.sh (real) server-side .env / APP_KEY
Remote provisioning just provision-server (real) chains env + deploy + backup + logrotate

Usage

# 1. Boot the fresh box (builds image, starts container, renders ssh config)
just test-env-up

# 2. Install the stack on the box (apt-get)
just test-env-provision

# 3. Run the real one-shot server setup (group/user/dir bootstrap)
just test-env-setup-server

# 4. Run the real deploy chain, pointed at the box
just test-env-run            # == just deploy
# or individual recipes:
just test-env-run recipe=deploy-nginx
just test-env-run recipe=provision-server-env
just test-env-run recipe=deploy-db

# 5. Health check
just test-env-status

# 6. Interactive shell
podman compose -f test-env/compose.yaml exec server bash

# Tear down
just test-env-teardown         # + -- --keys to also delete the SSH keypair

SSH to the box directly

ssh -p 22022 -i test-env/keys/xamxam-test_ed25519 deploy@127.0.0.1

The deploy user has passwordless sudo so the recipes' sudo … calls run unattended (the realistic option, matching that provision-server-env uses a non-TTY sudo tee). To force sudo to prompt instead, remove /etc/sudoers.d/deploy inside the container:

podman compose -f test-env/compose.yaml exec server rm /etc/sudoers.d/deploy

…but note some recipes (e.g. provision-server-env) rely on non-TTY sudo, so passwordless is the supported default.

How the justfile is wired

test-env-run recipe='deploy':          # run any recipe against the box
    @PATH="$(cd test-env && pwd)/bin:$PATH" just {{recipe}}

Only the test-env-* recipes prepend the shims; a plain just deploy still targets production as before.

Caveat: recipes using ssh -t need a real terminal

Recipes that call ssh -t xamxam "sudo …" (e.g. deploy-nginx) require a pseudo-terminal. When you run just test-env-run from a normal interactive terminal you SSH through the shim fine. But if just is driven from a non-TTY context (a script, CI, or agent), ssh -t cannot allocate a pty and sudo refuses with “a terminal is required”. Always drive these recipes from an interactive shell.

Generated / ignored

  • test-env/keys/ — throwaway SSH keypair (gitignored).
  • test-env/ssh/config — rendered from config.template, never committed.

Finding: deploy-code lost --chown=www-data:xamxam

First run of just deploy against a fresh box reproduces a real regression in the working-copy justfile (pre-existing uncommitted refactor that switched deploy-code to rsync -az … --exclude-from=.rsync-exclude):

rsync: [receiver] mkstemp "/var/www/xamxam/storage/.xamxam.sqlite…" failed: Permission denied
rsync error: code 23

Every deployed file/dir ends up deploy:deploy / drwxr-xr-x instead of www-data:xamxam / group-writable, so php-fpm (www-data) cannot write storage/. The recipe dropped --chown="www-data:xamxam" that the previous version had. Restore it to fix fresh deploys.