48614e622d
The dictionary lives on a persistent volume seeded from the image once and never re-seeded, so a redeploy that bumped BACKEND_DICT_VERSION left the new DAWGs unreachable (the volume mount shadows the image copy) and the active version stuck at whatever the admin console last installed — a native offline-first client then fetched the server's older pinned version over the network instead of using its bundled dict. On boot the backend now DELIVERS the build version onto the volume add-only, from a second unshadowed image copy at BACKEND_DICT_SEED_DIR, into DICT_DIR/<version>/ when absent (the flat seed and prior uploads untouched, so in-flight games keep theirs), and InitActiveVersion makes the build version active — except a console-installed version NEWER than the build (compared numerically) is not downgraded by a restart. The .seed_version guard still protects the flat seed's label (a bump is a new subdirectory, never a relabel). The console stays for out-of-band updates. Docs: ARCHITECTURE.md §5, deploy/README.md, seedmarker.go. Tests: engine.DeliverVersion (add-only, idempotent, flat-seed skip), compareDictVersions, and the dictionary-update integration test's deploy-delivers path.
63 lines
3.4 KiB
Docker
63 lines
3.4 KiB
Docker
# Multi-stage build for the backend service. Mirrors platform/telegram/Dockerfile:
|
|
# a golang-alpine builder yields a static binary shipped on distroless nonroot.
|
|
#
|
|
# The dictionary DAWGs are baked in from the scrabble-dictionary release artifact
|
|
# — the same set the Go CI downloads — and BACKEND_DICT_DIR points the
|
|
# binary at them. The published solver module is fetched directly from Gitea
|
|
# (GOPRIVATE), so the build stage needs git and network.
|
|
#
|
|
# Build from the repository root so go.work, go.work.sum, pkg/ and backend/ are all
|
|
# in the Docker context. DICT_VERSION has no default — the caller supplies the
|
|
# scrabble-dictionary release tag (compose/CI pass it; see deploy/README.md
|
|
# "Bumping the dictionary version"):
|
|
# docker build --build-arg DICT_VERSION=v1.3.0 -f backend/Dockerfile -t scrabble-backend .
|
|
|
|
# --- dictionary artifact -----------------------------------------------------
|
|
FROM alpine:3.20 AS dawg
|
|
ARG DICT_VERSION
|
|
RUN apk add --no-cache curl tar
|
|
RUN mkdir -p /dawg \
|
|
&& curl -fsSL -o /tmp/dawg.tar.gz \
|
|
"https://gitea.iliadenisov.ru/developer/scrabble-dictionary/releases/download/${DICT_VERSION}/scrabble-dawg-${DICT_VERSION}.tar.gz" \
|
|
&& tar xzf /tmp/dawg.tar.gz -C /dawg
|
|
|
|
# --- build -------------------------------------------------------------------
|
|
FROM golang:1.26.3-alpine AS build
|
|
WORKDIR /src
|
|
# git: the published solver module is fetched from Gitea directly (GOPRIVATE).
|
|
RUN apk add --no-cache git
|
|
ENV GOPRIVATE=gitea.iliadenisov.ru/*
|
|
|
|
COPY go.work go.work.sum ./
|
|
COPY pkg ./pkg
|
|
COPY backend ./backend
|
|
|
|
# Reduce the workspace to what the backend needs: backend + pkg. loadtest and the
|
|
# gateway replace it requires are not in this context, so drop both.
|
|
RUN go work edit -dropuse=./gateway -dropuse=./platform/telegram -dropuse=./loadtest -dropreplace=scrabble/gateway@v0.0.0
|
|
# VERSION (the deploy passes the git tag) is stamped into the binary via the linker.
|
|
ARG VERSION=dev
|
|
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags "-X scrabble/pkg/version.Version=${VERSION}" -o /out/backend ./backend/cmd/backend
|
|
|
|
# --- runtime -----------------------------------------------------------------
|
|
FROM gcr.io/distroless/static-debian12:nonroot
|
|
# Re-declare the build arg in this stage so it labels the seed dictionary. One
|
|
# DICT_VERSION drives both the artifact the dawg stage downloads and the version
|
|
# label the binary pins, so the resident version equals the release tag.
|
|
ARG DICT_VERSION
|
|
COPY --from=build /out/backend /usr/local/bin/backend
|
|
# Own the seed dictionary as the nonroot runtime user (UID 65532): a named volume
|
|
# mounted at /opt/dawg inherits this ownership on first use, so the admin console
|
|
# can write new version subdirectories at runtime. The volume preserves uploaded
|
|
# versions across deploys and, once seeded, is not re-seeded (ARCHITECTURE.md §5).
|
|
COPY --from=dawg --chown=65532:65532 /dawg /opt/dawg
|
|
# A second, UNSHADOWED copy of the build's DAWGs: the /opt/dawg volume mount hides the
|
|
# image's copy there, so this is where engine.DeliverVersion reads the build version from
|
|
# to add it (add-only) to the volume on boot — the deploy-delivers path that lets a bumped
|
|
# BACKEND_DICT_VERSION go live for new games without an admin upload (ARCHITECTURE.md §5).
|
|
COPY --from=dawg --chown=65532:65532 /dawg /opt/dawg-seed
|
|
ENV BACKEND_DICT_DIR=/opt/dawg
|
|
ENV BACKEND_DICT_SEED_DIR=/opt/dawg-seed
|
|
ENV BACKEND_DICT_VERSION=${DICT_VERSION}
|
|
ENTRYPOINT ["/usr/local/bin/backend"]
|