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.
2.8 KiB
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 jellyfinaftercp→ 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)