Three things, all in service of the match reading as its own mode: BLOOD, COUNTED. The vignette says "worse" and never says "how many". One pip per hit left, top-right beside the board, in the panel language the game/sets bar already uses; the last two breathe. Pips rather than a bar on purpose — the pool is 4 or 8, and a smooth readout of a small integer is the exact ambiguity being fixed. OPPONENTS FELLED IS THE MEASURE (handoff slice 1). Blood-only scoring means a lost game costs nothing but the blood already spent, so games survived is not the achievement. The in-play bar prints OPPONENT n · m FELLED instead of the game/sets line, the death screen says BLED OUT / FELLED n OPPONENTS · m GAMES, and it keeps its own matchBest — drillBest's pattern. The kills-to-next-point pips are hidden in a match: nothing there is killed for points, and they were colliding with the new line anyway. STAGING WEARS A DIFFERENT ICON. Both builds live on the same phone and two identical bloody tennis balls is a coin toss. Driven by HOSTNAME, not by the branch — the block is identical on staging and main, so it never conflicts on a merge and never has to be remembered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
28 lines
1.3 KiB
Docker
28 lines
1.3 KiB
Docker
# STAGE 1 — the test gate. `node test.js` drives the real game modules headless; if the state
|
|
# machine breaks, this stage fails, the image never builds, and the deploy never ships. The final
|
|
# stage COPYs the marker so BuildKit cannot skip this stage as unused.
|
|
FROM node:22-alpine AS test
|
|
WORKDIR /app
|
|
COPY test.js /app/
|
|
COPY src /app/src
|
|
RUN node test.js && touch /tests-passed
|
|
|
|
FROM python:3.12-alpine
|
|
# Was nginx:alpine. The leaderboard needs one process that can WRITE, and nginx is not one.
|
|
# Everything the nginx conf was doing here — no-cache so a deploy is picked up on a plain reload,
|
|
# and a real MIME type for the manifest, without which Chrome refuses to install the homescreen
|
|
# app — is a few lines in server.py. Standard library only: no pip, no requirements file.
|
|
# ponytail: switch to hashed filenames if traffic ever matters.
|
|
WORKDIR /app
|
|
COPY --from=test /tests-passed /tmp/tests-passed
|
|
COPY index.html manifest.webmanifest manifest-stg.webmanifest server.py /app/
|
|
COPY src /app/src
|
|
COPY assets /app/assets
|
|
# THE SCORES LIVE HERE AND THIS MUST BE A PERSISTENT VOLUME. Without one the json sits in the
|
|
# container's writable layer and every deploy silently resets the leaderboard to empty — which
|
|
# looks exactly like the feature working until the first time you push.
|
|
ENV SPLAT_DATA=/data
|
|
VOLUME /data
|
|
EXPOSE 80
|
|
CMD ["python", "-u", "server.py"]
|