legacy-arrflix/testing/DEPLOY.md
s8n d9d6bdba64 testing/ folder: theme-edit guides + error catalog + headless recipes
7 docs in /testing/ — institutional memory after 6+ regressions in
24-48h on the v6 theme. Read before any edit.

  README.md           — index + quickstart
  THEMING.md          — safe-edit checklist + layer/specificity tables
  ERROR-PATTERNS.md   — 12 cataloged patterns (Symptom/Cause/Diag/Fix/Prev)
  HEADLESS-PROBE.md   — 11 playwright recipes (md5 chain, darkPct,
                        ancestor bg sample, dropdown listItem probe)
  ROLLBACK.md         — 8 emergency revert recipes (overlay, branding,
                        encoding, full-from-repo, dev-clone-prod,
                        git-revert, pw-reset, bind-mount inode-swap)
  SMOKE-TEST.md       — manual + headless verify checklist
  DEPLOY.md           — dev → prod promotion workflow with backup +
                        chown root + restart inode-swap

Empty subdirs: snipUSER-Es/, recipes/, incidents/ (post-mortems land here).

Goal: stop reinventing the same fixes. Catalog every error class,
codify the recovery, build a skills folder for future ARRFLIX work.
2026-05-10 00:47:20 +01:00

2.8 KiB

DEPLOY — dev → prod promotion workflow

Strict order. Skip a step → break prod.

Pre-flight

  • testing/SMOKE-TEST.md passed on dev
  • You can name the change in one sentence
  • You have a rollback target (the current prod md5 — capture before deploy)

Step 1 — capture current prod md5 (rollback anchor)

PROD_BEFORE=$(ssh user@nullstone 'md5sum /opt/docker/jellyfin/web-overrides/index.html' | awk '{print $1}')
echo "rollback anchor: $PROD_BEFORE"

If anything goes wrong: cp /opt/docker/jellyfin/web-overrides/index.html.bak.<latest> /opt/docker/jellyfin/web-overrides/index.html + restart.

Step 2 — backup prod overlay + branding

ssh user@nullstone 'set -e
TS=$(date +%s)
docker run --rm --userns=host -v /opt/docker/jellyfin/web-overrides:/d:rw alpine cp /d/index.html /d/index.html.bak.deploy.$TS
docker run --rm --userns=host -v /home/docker/jellyfin/config/config:/d:rw alpine cp /d/branding.xml /d/branding.xml.bak.deploy.$TS
'

Step 3 — copy dev overlay to prod via docker shim (root-owned dest)

ssh user@nullstone 'docker run --rm --userns=host -v /opt/docker/jellyfin-dev/web-overrides:/dev:ro -v /opt/docker/jellyfin/web-overrides:/prod:rw alpine sh -c "cp /dev/index-dev.html /prod/index.html && chown root:root /prod/index.html && md5sum /prod/index.html"'

Step 4 — bind-mount inode swap requires restart

ssh user@nullstone 'docker restart jellyfin && sleep 14'

Step 5 — verify

ssh user@nullstone 'docker exec jellyfin md5sum /jellyfin/jellyfin-web/index.html'   # should match dev's md5
ssh user@nullstone 'docker exec jellyfin curl -s http://127.0.0.1:8096/web/index.html | grep -c ARRFLIX-MIDDLE-THEME-BEGIN'   # = 1
ssh user@nullstone 'docker exec jellyfin curl -s http://127.0.0.1:8096/Branding/Css.css | wc -c'   # ~36000
ssh user@nullstone 'docker ps --format "{{.Names}} {{.Status}}" | grep "^jellyfin "'   # healthy

Step 6 — manual smoke on prod

Open arrflix.s8n.ru in incognito. Run testing/SMOKE-TEST.md manual checklist.

Step 7 — commit + push to repo

cd /tmp/arrflix-recon
cp web-overrides/index.html snapshots/2026-05-09-v6-stable/index.html  # update snapshot
git add bin/inject-middle-theme.py web-overrides/index.html snapshots/2026-05-09-v6-stable/index.html docs/  # whatever changed
git -c user.name=s8n -c user.email=admin@s8n.ru commit -m "<one-line summary>"
git push origin main

If verify fails

Run testing/ROLLBACK.md immediately. Don't try to fix forward on prod.

Common deploy gotchas

  • Forget docker restart jellyfin after cp → bind-mount inode swap → container serves stale
  • Forget chown root:root → user can't write but root needs to own per host config
  • Forget snapshot bump → next "what's deployed" question gets wrong answer
  • Forget commit → repo drifts from prod (per doc 26 INC1 root cause)