diff --git a/.github/workflows/dockerhub-publish.yml b/.github/workflows/dockerhub-publish.yml index 3cc1cb9..cc5b5a4 100644 --- a/.github/workflows/dockerhub-publish.yml +++ b/.github/workflows/dockerhub-publish.yml @@ -32,6 +32,7 @@ jobs: # Escape newlines for label usage DESC=$(awk 'BEGIN{ORS="\\n"} {print}' RELEASE_NOTES.md) echo "desc=$DESC" >> $GITHUB_OUTPUT + echo "version=$VERSION_REF" >> $GITHUB_OUTPUT - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -39,6 +40,26 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Smoke test image boots Web UI by default (amd64) + shell: bash + run: | + # Build a local test image (amd64) and load into docker + docker buildx build --platform linux/amd64 --load -t mtg-deckbuilder:test --build-arg APP_VERSION=${{ steps.notes.outputs.version }} . + # Run container and wait for it to serve on 8080 + docker rm -f mtg-smoke 2>/dev/null || true + docker run -d --name mtg-smoke -p 8080:8080 mtg-deckbuilder:test + echo "Waiting for Web UI..." + for i in {1..30}; do + if curl -fsS http://localhost:8080/ >/dev/null; then echo "Up"; break; fi + sleep 2 + done + # Final assert; print logs on failure + if ! curl -fsS http://localhost:8080/ >/dev/null; then + echo "Web UI did not start in time. Container logs:" && docker logs mtg-smoke || true + exit 1 + fi + docker rm -f mtg-smoke >/dev/null 2>&1 || true + - name: Docker Hub login uses: docker/login-action@v3 with: @@ -58,6 +79,7 @@ jobs: org.opencontainers.image.title=MTG Python Deckbuilder org.opencontainers.image.version=${{ github.ref_name }} org.opencontainers.image.description=${{ steps.notes.outputs.desc }} + org.opencontainers.image.revision=${{ github.sha }} - name: Build and push uses: docker/build-push-action@v6 @@ -68,4 +90,6 @@ jobs: platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + build-args: | + APP_VERSION=${{ steps.notes.outputs.version }} diff --git a/Dockerfile b/Dockerfile index 5f9356d..4248f5a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,8 @@ WORKDIR /app # Set environment variables ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 +ARG APP_VERSION=dev +ENV APP_VERSION=${APP_VERSION} # Install system dependencies if needed RUN apt-get update && apt-get install -y \ @@ -46,12 +48,25 @@ RUN cd /app/code && ls -la deck_files logs csv_files config owned_cards # Set the working directory to code for proper imports WORKDIR /app/code -# Run the application -CMD ["python", "main.py"] +# Add a tiny entrypoint to select Web UI (default) or CLI +COPY entrypoint.sh /usr/local/bin/entrypoint.sh +RUN chmod +x /usr/local/bin/entrypoint.sh +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] # Expose web port for the optional Web UI EXPOSE 8080 +# Container health check: verify Web UI health endpoint (skip if APP_MODE=cli) +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD python -c "import os,sys,json,urllib.request;\ +m=os.getenv('APP_MODE','web');\ +\ +\ +\ +sys.exit(0) if m=='cli' else None;\ +d=urllib.request.urlopen('http://127.0.0.1:8080/healthz', timeout=3).read();\ +sys.exit(0 if json.loads(d.decode()).get('status')=='ok' else 1)" + # Note: For the Web UI, start uvicorn in your orchestrator (compose/run) like: -# uvicorn code.web.app:app --host 0.0.0.0 --port 8080 +# (now default) container starts Web UI automatically; to run CLI set APP_MODE=cli # Phase 9: enable web list virtualization with env WEB_VIRTUALIZE=1 diff --git a/README.md b/README.md index 59c8267..d11e3d0 100644 Binary files a/README.md and b/README.md differ diff --git a/docker-compose.yml b/docker-compose.yml index 156c6a1..2ade2f5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -53,5 +53,4 @@ services: - ${PWD}/config:/app/config - ${PWD}/owned_cards:/app/owned_cards working_dir: /app - command: ["bash", "-lc", "cd /app && uvicorn code.web.app:app --host 0.0.0.0 --port 8080"] restart: "no" diff --git a/dockerhub-docker-compose.yml b/dockerhub-docker-compose.yml new file mode 100644 index 0000000..201e6c1 --- /dev/null +++ b/dockerhub-docker-compose.yml @@ -0,0 +1,34 @@ +services: + web: + image: mwisnowski/mtg-python-deckbuilder:latest + # Tip: pin to a specific tag when available, e.g. :2.0.2 (defaults to Web UI) + container_name: mtg-deckbuilder-web + ports: + - "8080:8080" # Host:Container — open http://localhost:8080 + environment: + # UI features/flags (all optional) + - SHOW_LOGS=1 # 1=enable /logs page; 0=hide (default off if unset) + - SHOW_DIAGNOSTICS=1 # 1=enable /diagnostics & /diagnostics/perf; 0=hide (default off) + - ENABLE_PWA=0 # 1=serve manifest/service worker (experimental); 0=disabled + - WEB_VIRTUALIZE=1 # 1=enable list virtualization/lazy tweaks in Web UI; 0=off + - WEB_TAG_PARALLEL=1 # 1=parallelize heavy tagging steps in Web UI; 0=serial + - WEB_TAG_WORKERS=4 # Worker count for parallel tagging (only used if WEB_TAG_PARALLEL=1) + + # Theming (optional) + - THEME=system # Default theme for first-time visitors: system|light|dark + # 'light' maps to the consolidated Light (Blend) palette + - ENABLE_THEMES=1 # 1=show theme selector in header; 0=hide selector + # Note: THEME still applies as the default even if selector is hidden + + # Version label (optional; shown in footer/diagnostics) + # - APP_VERSION=v2.0.2 + + volumes: + # Persist app data locally; ensure these directories exist next to this compose file + - ${PWD}/deck_files:/app/deck_files + - ${PWD}/logs:/app/logs + - ${PWD}/csv_files:/app/csv_files + - ${PWD}/config:/app/config + - ${PWD}/owned_cards:/app/owned_cards + + restart: unless-stopped \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..814404c --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env sh +set -e + +# Always operate from the code directory for imports to work +cd /app/code || exit 1 + +# Select mode: default to Web UI +MODE="${APP_MODE:-web}" + +if [ "$MODE" = "cli" ]; then + # Run the CLI (interactive menu; use DECK_MODE=headless for non-interactive) + exec python main.py +fi + +# Web UI (FastAPI via uvicorn) +HOST="${HOST:-0.0.0.0}" +PORT="${PORT:-8080}" +WORKERS="${WORKERS:-1}" + +exec uvicorn web.app:app --host "$HOST" --port "$PORT" --workers "$WORKERS"