diff --git a/docs/widgets.md b/docs/widgets.md index 5c59235..15939e1 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -283,10 +283,16 @@ widgets on the same frame without a Floyd-Steinberg seam at the boundary (see that module's docstring for why ordered dithering doesn't have this problem and Floyd-Steinberg does) -- no `Frame`-level dithering setting was needed. Playwright/Chromium is a real, heavyweight runtime dependency -imported lazily only when a weather widget actually uses this style, and -its Docker packaging has a known likely-blocking image-size problem not -yet resolved (see `server/Dockerfile`'s own comment) -- treat this style -as unshipped/local-only until that's sorted out. +imported lazily only when a weather widget actually uses this style. +Its browser binary is fetched by `start.sh` at container startup rather +than baked into the image (see `server/Dockerfile`'s own comment) -- +a single ~181MB `chrome-headless-shell` binary can't be split across +Docker layers the way this project's pip/npm installs were, and +confirmed-failed to push to the registry as a build-time layer; cached +on the `/data` volume (`PLAYWRIGHT_BROWSERS_PATH`) so only the very +first boot on a fresh volume actually downloads it. Still real-panel- +unverified (see this widget's own render_style rollout notes/PR) -- +treat "modern" style as experimental regardless of deploy status. `current`/`hourly`/`daily` share one configured location (`city_label`/`city_latitude`/`city_longitude`, set via `POST .../ diff --git a/server/Dockerfile b/server/Dockerfile index 43307b1..5b37813 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -36,8 +36,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ tzdata fontconfig fonts-dejavu-core nodejs npm \ && rm -rf /var/lib/apt/lists/* -# EXPERIMENTAL, likely unmergeable as-is -- see below. System libs a -# headless Chromium needs (app/html_render.py, the weather widget's +# EXPERIMENTAL. System libs a headless Chromium needs (app/html_render.py, the weather widget's # opt-in "modern" render style), trimmed from Playwright's own full # `install-deps chromium` list to just what a headless (no Xvfb), # Latin-text-plus-emoji use case needs: dropped xvfb (only needed for a @@ -74,23 +73,23 @@ RUN pip install --no-cache-dir pypdfium2==5.12.1 RUN pip install --no-cache-dir playwright==1.61.0 RUN pip install --no-cache-dir -r requirements.txt -# KNOWN LIKELY BLOCKER, not resolved by pulling this into its own layer: -# `playwright install chromium-headless-shell` unpacks to ~262MB, and its -# single `chrome-headless-shell` binary alone (measured: 181MB) is one -# file -- unlike the pip/npm splits above (independently-installable -# smaller packages moved into their own layers), a single 181MB file -# can't be divided across multiple <100MB Docker layers by any ordinary -# COPY/RUN restructuring; the whole file lands in whichever layer's diff -# contains it. This almost certainly exceeds the same Cloudflare single- -# blob/layer limit that forced the pip/npm splits elsewhere in this file -# (see their comments) -- an actual push to this project's registry -# hasn't been attempted (would require pushing to `main`, which triggers -# deploy) to confirm, but there is no reason to expect a single 181MB -# blob to fit where combined ~113MB of many small wheels didn't. Needs a -# real resolution (a registry without this limit, hosting the browser -# binary outside the image, etc.) before this branch can actually ship -- -# tracked as open, not silently assumed away. -RUN playwright install chromium-headless-shell +# The headless Chromium binary itself is deliberately NOT installed +# here at build time. `playwright install chromium-headless-shell` +# unpacks to ~262MB, and its single `chrome-headless-shell` binary alone +# (measured: 181MB) is one file -- unlike the pip/npm splits above +# (independently-installable smaller packages moved into their own +# layers), a single 181MB file can't be divided across multiple <100MB +# Docker layers by any ordinary COPY/RUN restructuring; the whole file +# lands in whichever layer's diff contains it, which confirmed-failed +# to push to this project's registry (the same Cloudflare single-blob/ +# layer limit that forced the pip/npm splits elsewhere in this file -- +# see their comments). Fix: start.sh downloads it at container startup +# instead, cached on the /data volume (PLAYWRIGHT_BROWSERS_PATH below) +# so it survives restarts/redeploys and only ever downloads once per +# volume, not once per image layer. Trade-off: first boot on a fresh +# volume needs network access to Playwright's CDN -- true of Immich/ +# weather API access too, so not a new requirement for this server. +ENV PLAYWRIGHT_BROWSERS_PATH=/data/.playwright-browsers # render-service/'s dependencies installed as several separate layers # rather than one `npm install` covering all of them -- a from-scratch diff --git a/server/start.sh b/server/start.sh index b243e09..3adb5bd 100755 --- a/server/start.sh +++ b/server/start.sh @@ -5,6 +5,18 @@ # Then execs uvicorn as the foreground/PID 1 process so it receives # Docker's stop signal directly. # +# Before either: fetch headless Chromium (app/html_render.py, the +# weather widget's opt-in "modern" render style) into +# PLAYWRIGHT_BROWSERS_PATH (set in the Dockerfile to a path on the /data +# volume) if it isn't already cached there -- see the Dockerfile's own +# comment for why this happens at startup instead of build time. Only +# the very first boot on a fresh volume actually downloads anything; +# every boot after that is a no-op ls check. +if [ -z "$(ls -A "$PLAYWRIGHT_BROWSERS_PATH" 2>/dev/null)" ]; then + echo "Fetching headless Chromium into $PLAYWRIGHT_BROWSERS_PATH (first boot on this volume)..." >&2 + playwright install chromium-headless-shell +fi +# # Wrapped in a restart loop, not a bare `node ... &`: a bare background # process that crashes stays dead for good, with nothing to bring it # back -- turning any single render crash (a not-yet-found jsdom/