mtg_python_deckbuilder/DOCKER.md
matt 88cf832bf2 Finalize MDFC follow-ups, docs, and diagnostics tooling
document deck summary DFC badges, exporter annotations, and per-face metadata across README/DOCKER/release notes

record completion of all MDFC roadmap follow-ups and add the authoring guide for multi-face CSV entries

wire in optional DFC_PER_FACE_SNAPSHOT env support, exporter regression tests, and diagnostics updates noted in the changelog
2025-10-02 15:31:05 -07:00

9.6 KiB
Raw Blame History

Docker Guide

Run the MTG Deckbuilder (CLI and Web UI) in Docker with persistent volumes and optional headless mode.

Quick start

docker compose build
docker compose run --rm mtg-deckbuilder

From Docker Hub (PowerShell)

docker run -it --rm `
    -v "${PWD}/deck_files:/app/deck_files" `
    -v "${PWD}/logs:/app/logs" `
    -v "${PWD}/csv_files:/app/csv_files" `
    -v "${PWD}/owned_cards:/app/owned_cards" `
    -v "${PWD}/config:/app/config" `
    mwisnowski/mtg-python-deckbuilder:latest

Web UI (new)

The web UI runs the same deckbuilding logic behind a browser-based interface.

docker compose up --build --no-deps -d web

Then open http://localhost:8080

Volumes are the same as the CLI service, so deck exports/logs/configs persist in your working folder. The app serves a favicon at /favicon.ico and exposes a health endpoint at /healthz. Compare view offers a Copy summary button to copy a plain-text diff of two runs. The sidebar has a subtle depth shadow for clearer separation.

Web UI feature highlights:

  • Locks: Click a card or the lock control in Step 5; locks persist across reruns.
  • Replace: Enable Replace in Step 5, click a card to open Alternatives (filters include Owned-only), then choose a swap.
  • Permalinks: Copy a permalink from Step 5 or a Finished deck; paste via “Open Permalink…” to restore.
  • Compare: Use the Compare page from Finished Decks; quick actions include Latest two and Swap A/B.

Virtualized lists and lazy images (optin)

  • Set WEB_VIRTUALIZE=1 to enable virtualization in Step 5 grids/lists and the Owned library for smoother scrolling on large sets.
  • Example (Compose):
    services:
        web:
            environment:
                - WEB_VIRTUALIZE=1
    
  • Example (Docker Hub):
    docker run --rm -p 8080:8080 `
        -e WEB_VIRTUALIZE=1 `
        -v "${PWD}/deck_files:/app/deck_files" `
        -v "${PWD}/logs:/app/logs" `
        -v "${PWD}/csv_files:/app/csv_files" `
        -v "${PWD}/owned_cards:/app/owned_cards" `
        -v "${PWD}/config:/app/config" `
    -e SHOW_DIAGNOSTICS=1 ` # optional: enables diagnostics tools and overlay
    mwisnowski/mtg-python-deckbuilder:latest `
        bash -lc "cd /app && uvicorn code.web.app:app --host 0.0.0.0 --port 8080"
    

Diagnostics and logs (optional)

Enable internal diagnostics and a read-only logs viewer with environment flags.

  • SHOW_DIAGNOSTICS=1 — adds a Diagnostics nav link and /diagnostics tools
  • SHOW_LOGS=1 — enables /logs and /status/logs?tail=200

Per-face MDFC snapshot (opt-in)

  • DFC_PER_FACE_SNAPSHOT=1 — write merged MDFC face metadata to logs/dfc_per_face_snapshot.json; disable parallel tagging (WEB_TAG_PARALLEL=0) if you need the snapshot during setup.
  • DFC_PER_FACE_SNAPSHOT_PATH=/app/logs/custom_snapshot.json — optional path override for the snapshot artifact.

When enabled:

  • /logs supports an auto-refresh toggle with interval, a level filter (All/Error/Warning/Info/Debug), and a Copy button to copy the visible tail.
  • /status/sys returns a simple system summary (version, uptime, UTC server time, and feature flags) and is shown on the Diagnostics page when SHOW_DIAGNOSTICS=1.
  • Virtualization overlay: press v on pages with virtualized grids to toggle per-grid overlays and a global summary bubble.

Compose example (web service):

environment:
    - SHOW_LOGS=1
    - SHOW_DIAGNOSTICS=1

Docker Hub (PowerShell) example:

docker run --rm `
    -p 8080:8080 `
    -e SHOW_LOGS=1 -e SHOW_DIAGNOSTICS=1 -e ENABLE_THEMES=1 -e THEME=system `
    -e SPLASH_ADAPTIVE=1 -e SPLASH_ADAPTIVE_SCALE="1:1.0,2:1.0,3:1.0,4:0.6,5:0.35" ` # optional experiment
    -e RANDOM_MODES=1 -e RANDOM_UI=1 -e RANDOM_MAX_ATTEMPTS=5 -e RANDOM_TIMEOUT_MS=5000 `
    -v "${PWD}/deck_files:/app/deck_files" `
    -v "${PWD}/logs:/app/logs" `
    -v "${PWD}/csv_files:/app/csv_files" `
    -v "${PWD}/owned_cards:/app/owned_cards" `
    -v "${PWD}/config:/app/config" `
    mwisnowski/mtg-python-deckbuilder:latest `
    bash -lc "cd /app && uvicorn code.web.app:app --host 0.0.0.0 --port 8080"

MDFC merge rollout (staging)

The web service now runs the MDFC merge by default. Set DFC_COMPAT_SNAPSHOT=1 on the web service when you need the legacy unmerged compatibility snapshot (csv_files/compat_faces/). Combine this with python -m code.scripts.refresh_commander_catalog --compat-snapshot inside the container to regenerate the commander files before smoke testing.

Follow the QA steps in docs/qa/mdfc_staging_checklist.md after toggling the flag.

Compose example:

services:
    web:
        environment:
            - DFC_COMPAT_SNAPSHOT=1

Verify the refresh inside the container:

docker compose run --rm web bash -lc "python -m code.scripts.refresh_commander_catalog"

Downstream consumers can diff csv_files/compat_faces/commander_cards_unmerged.csv against historical exports during the staging window.

Setup speed: parallel tagging (Web)

First-time setup or stale data triggers card tagging. The web service uses parallel workers by default.

Configure via environment variables on the web service:

  • WEB_TAG_PARALLEL=1|0 — enable/disable parallel tagging (default: 1)
  • WEB_TAG_WORKERS=<N> — number of worker processes (default: 4 in compose)

If parallel initialization fails, the service falls back to sequential tagging and continues.

From Docker Hub (PowerShell)

If you prefer not to build locally, pull mwisnowski/mtg-python-deckbuilder:latest and run uvicorn:

docker run --rm `
    -p 8080:8080 `
    -v "${PWD}/deck_files:/app/deck_files" `
    -v "${PWD}/logs:/app/logs" `
    -v "${PWD}/csv_files:/app/csv_files" `
    -v "${PWD}/owned_cards:/app/owned_cards" `
    -v "${PWD}/config:/app/config" `
    mwisnowski/mtg-python-deckbuilder:latest `
    bash -lc "cd /app && uvicorn code.web.app:app --host 0.0.0.0 --port 8080"

Health check:

GET http://localhost:8080/healthz  ->  { "status": "ok", "version": "dev", "uptime_seconds": 123 }

Theme preference reset (client-side): use the headers Reset Theme control to clear the saved browser preference; the server default (THEME) applies on next paint.

Random Modes (alpha) and test dataset override

Enable experimental Random Modes and UI controls in Web runs by setting:

services:
    web:
        environment:
            - RANDOM_MODES=1
            - RANDOM_UI=1
            - RANDOM_MAX_ATTEMPTS=5
            - RANDOM_TIMEOUT_MS=5000

For deterministic tests or development, you can point the app to a frozen dataset snapshot:

services:
    web:
        environment:
            - CSV_FILES_DIR=/app/csv_files/testdata

Taxonomy snapshot (maintainers)

Capture the current bracket taxonomy into an auditable JSON file inside the container:

docker compose run --rm web bash -lc "python -m code.scripts.snapshot_taxonomy"

Artifacts appear under ./logs/taxonomy_snapshots/ on your host via the mounted volume.

To force a new snapshot even when the content hash matches the latest, pass --force to the module.

Volumes

  • /app/deck_files./deck_files
  • /app/logs./logs
  • /app/csv_files./csv_files
  • /app/owned_cards./owned_cards (owned cards lists: .txt/.csv)
  • Optional: /app/config./config (JSON configs for headless)

Interactive vs headless

  • Interactive: attach a TTY (compose run or docker run -it)
  • Headless auto-run:
    docker compose run --rm -e DECK_MODE=headless mtg-deckbuilder
    
  • Headless with JSON config:
    docker compose run --rm `
        -e DECK_MODE=headless `
        -e DECK_CONFIG=/app/config/deck.json `
        mtg-deckbuilder
    

Common env vars

  • DECK_MODE=headless
  • DECK_CONFIG=/app/config/deck.json
  • DECK_COMMANDER, DECK_PRIMARY_CHOICE
  • DECK_ADD_LANDS, DECK_FETCH_COUNT
  • DECK_TAG_MODE=AND|OR (combine mode used by the builder)

Web UI tuning env vars

  • WEB_TAG_PARALLEL=1|0 (parallel tagging on/off)
  • WEB_TAG_WORKERS= (process count; set based on CPU/memory)
  • WEB_VIRTUALIZE=1 (enable virtualization)
  • SHOW_DIAGNOSTICS=1 (enables diagnostics pages and overlay hotkey v)
  • RANDOM_MODES=1 (enable random build endpoints)
  • RANDOM_UI=1 (show Surprise/Theme/Reroll/Share controls)
  • RANDOM_MAX_ATTEMPTS=5 (cap retry attempts)
  • (Upcoming) Multi-theme inputs: once UI ships, Random Mode will accept primary_theme, secondary_theme, tertiary_theme fields; current backend already supports the cascade + diagnostics.
  • RANDOM_TIMEOUT_MS=5000 (per-build timeout in ms)

Testing/determinism helper (dev):

  • CSV_FILES_DIR=csv_files/testdata — override CSV base dir to a frozen set for tests

Manual build/run

docker build -t mtg-deckbuilder .
docker run -it --rm `
    -v "${PWD}/deck_files:/app/deck_files" `
    -v "${PWD}/logs:/app/logs" `
    -v "${PWD}/csv_files:/app/csv_files" `
    -v "${PWD}/owned_cards:/app/owned_cards" `
    -v "${PWD}/config:/app/config" `
    mtg-deckbuilder

Troubleshooting

  • No prompts? Use docker compose run --rm (not up) or add -it to docker run
  • Files not saving? Verify volume mounts and that folders exist
  • Headless not picking config? Ensure ./config is mounted to /app/config and DECK_CONFIG points to a JSON file
  • Owned-cards prompt not seeing files? Ensure ./owned_cards is mounted to /app/owned_cards

Tips

  • Use docker compose run, not up, for interactive mode
  • Exported decks appear in deck_files/
  • JSON run-config is exported only in interactive runs; headless skips it