From 33aced3c97390351aa8314e5100192a4d64c7c91 Mon Sep 17 00:00:00 2001 From: matt Date: Sat, 4 Apr 2026 20:55:43 -0700 Subject: [PATCH] fix: invalidate CK price cache after GitHub download so prices load without restart --- .env.example | 2 +- CHANGELOG.md | 4 ++++ code/web/routes/setup.py | 8 ++++++++ code/web/services/orchestrator.py | 7 +++++++ code/web/services/price_service.py | 12 ++++++++++++ docker-compose.yml | 2 +- dockerhub-docker-compose.yml | 2 +- docs/releases/v4.6.3.md | 4 ++++ pyproject.toml | 2 +- 9 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 docs/releases/v4.6.3.md diff --git a/.env.example b/.env.example index 79a3590..29bb731 100644 --- a/.env.example +++ b/.env.example @@ -13,7 +13,7 @@ # HOST=0.0.0.0 # Uvicorn bind host (only when APP_MODE=web). # PORT=8080 # Uvicorn port. # WORKERS=1 # Uvicorn worker count. -APP_VERSION=v4.6.2 # Matches dockerhub compose. +APP_VERSION=v4.6.3 # Matches dockerhub compose. ############################ # Theming diff --git a/CHANGELOG.md b/CHANGELOG.md index eb322bf..81c14e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,10 @@ _No unreleased changes yet_ ### Removed _No unreleased changes yet_ +## [4.6.3] - 2026-04-04 +### Fixed +- **CK prices not loading after GitHub cache download**: The `PriceService` singleton sets `_ck_loaded = True` as a graceful fallback when `ck_prices_cache.json` is missing at startup, preventing it from reloading the file if it is later written to disk. Added `invalidate_ck_cache()` to `PriceService`; the `Download from GitHub` route and the orchestrator auto-download now call it after successfully downloading `ck_prices_cache.json`, so CK prices appear immediately without a container restart. + ## [4.6.2] - 2026-04-04 ### Fixed - **CK prices missing after GitHub cache download**: `ck_prices_cache.json` was not included in the files committed to `similarity-cache-data` by the build workflow, nor fetched by the `Download from GitHub` button or the orchestrator auto-download flow. All three paths now include the file (graceful 404 handling preserves backward compatibility with existing cache branches). diff --git a/code/web/routes/setup.py b/code/web/routes/setup.py index 109a5a4..3807fc2 100644 --- a/code/web/routes/setup.py +++ b/code/web/routes/setup.py @@ -172,6 +172,14 @@ async def download_github(): msg = f"Downloaded {len(downloaded)} file(s) from GitHub" if failed: msg += f" ({len(failed)} unavailable)" + # Invalidate in-memory CK price cache if the file was downloaded + # so the singleton reloads it without a container restart. + if any("ck_prices_cache.json" in f for f in downloaded): + try: + from code.web.services.price_service import get_price_service + get_price_service().invalidate_ck_cache() + except Exception: + pass return JSONResponse({ "ok": True, "message": msg, diff --git a/code/web/services/orchestrator.py b/code/web/services/orchestrator.py index 9d10775..decb595 100644 --- a/code/web/services/orchestrator.py +++ b/code/web/services/orchestrator.py @@ -1408,6 +1408,13 @@ def _ensure_setup_ready(out, force: bool = False) -> None: "percent": 100, "finished_at": _dt.now().isoformat(timespec='seconds') }) + # Invalidate in-memory CK price cache so the singleton picks up + # the newly downloaded ck_prices_cache.json without a restart. + try: + from code.web.services.price_service import get_price_service + get_price_service().invalidate_ck_cache() + except Exception: + pass # Refresh theme catalog after successful download _refresh_theme_catalog(out, force=False, fast_path=True) return diff --git a/code/web/services/price_service.py b/code/web/services/price_service.py index 3bc3063..74d8091 100644 --- a/code/web/services/price_service.py +++ b/code/web/services/price_service.py @@ -546,6 +546,18 @@ class PriceService(BaseService): # rather than blocking every request. CK rebuild happens via Setup page. self._ck_loaded = True + def invalidate_ck_cache(self) -> None: + """Reset the CK loaded flag so the cache is re-read from disk on next access. + + Call this after externally writing a new ck_prices_cache.json (e.g. after + downloading a pre-built cache from GitHub) so the in-memory singleton + picks up the new file without a container restart. + """ + with self._lock: + self._ck_loaded = False + self._ck_cache = {} + logger.info("CK price cache invalidated — will reload on next access.") + def _rebuild_ck_cache(self) -> None: """Fetch the Card Kingdom price list and cache retail prices by card name. diff --git a/docker-compose.yml b/docker-compose.yml index 281bff8..88cdb88 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -144,7 +144,7 @@ services: # WEB_THEME_FILTER_PREWARM: "0" WEB_AUTO_ENFORCE: "0" # 1=auto-run compliance export after builds WEB_CUSTOM_EXPORT_BASE: "" # Optional: custom base dir for deck export artifacts - APP_VERSION: "v4.6.2" # Displayed version label (set per release/tag) + APP_VERSION: "v4.6.3" # Displayed version label (set per release/tag) # ------------------------------------------------------------------ # Misc / Land Selection (Step 7) Environment Tuning diff --git a/dockerhub-docker-compose.yml b/dockerhub-docker-compose.yml index 60decae..79ccb33 100644 --- a/dockerhub-docker-compose.yml +++ b/dockerhub-docker-compose.yml @@ -146,7 +146,7 @@ services: # WEB_THEME_FILTER_PREWARM: "0" WEB_AUTO_ENFORCE: "0" # 1=auto-run compliance export after builds WEB_CUSTOM_EXPORT_BASE: "" # Optional: custom base dir for deck export artifacts - APP_VERSION: "v4.6.2" # Displayed version label (set per release/tag) + APP_VERSION: "v4.6.3" # Displayed version label (set per release/tag) # ------------------------------------------------------------------ # Misc / Land Selection (Step 7) Environment Tuning diff --git a/docs/releases/v4.6.3.md b/docs/releases/v4.6.3.md new file mode 100644 index 0000000..8fcb0c3 --- /dev/null +++ b/docs/releases/v4.6.3.md @@ -0,0 +1,4 @@ +# MTG Python Deckbuilder v4.6.3 + +## Fixed +- **CK prices not loading after GitHub cache download**: The `PriceService` singleton marks `_ck_loaded = True` as a graceful fallback when `ck_prices_cache.json` is absent at startup. If the file was then downloaded during a running container (via "Download from GitHub" or the orchestrator auto-download), the in-memory flag prevented it from ever being read. `PriceService.invalidate_ck_cache()` is now called after a successful download, forcing a reload on the next price request without requiring a container restart. diff --git a/pyproject.toml b/pyproject.toml index 0b45b65..6d4442a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "mtg-deckbuilder" -version = "4.6.2" +version = "4.6.3" description = "A command-line tool for building and analyzing Magic: The Gathering decks" readme = "README.md" license = {file = "LICENSE"}