Files
Anthias/docker/Dockerfile.server.j2
Viktor Petersson 7fc57fecf0 fix(docker): pull the BuildKit frontend via mirror.gcr.io (#3008)
* fix(docker): pull the BuildKit frontend via mirror.gcr.io

The `# syntax=docker/dockerfile:1.4` directive made every image build
fetch the frontend from registry-1.docker.io — the last remaining
Docker Hub dependency (base images already come from mirror.gcr.io,
bun/uv from ghcr.io). Docker Hub pulls from shared GitHub runner IPs
intermittently time out, failing CI before the build even starts.

Re-point the directive at Google's pull-through cache, which serves
the same multi-arch manifest list. The version pin stays for frontend
reproducibility.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(docker): bump the BuildKit frontend pin from 1.4 to 1.24

1.4 dates to May 2022; 1.24 is the current release. Nothing in the
templates needs newer syntax (--mount=type=cache predates 1.4), so
this is purely picking up four years of frontend bugfixes. Keeps the
minor-pin convention — the tag floats only over patch releases.

Validated by building the rendered redis image against the mirrored
1.24 frontend.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(docker): use ENV key=value form flagged by 1.24 build checks

`docker build --check` with the 1.24 frontend flags the legacy
`ENV DEBIAN_FRONTEND noninteractive` form (LegacyKeyValueFormat) in
the test template — the only hit across all four templates. All
rendered Dockerfiles now lint clean against the new frontend.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 07:41:21 +02:00

98 lines
4.0 KiB
Django/Jinja

# syntax=mirror.gcr.io/docker/dockerfile:1.24
# vim: ft=dockerfile
{% if environment == 'production' %}
{# bun ships no 32-bit binaries at all — its release artifacts cover
only {linux,darwin,windows}-{x64,aarch64}, so a target-platform
build (linux/arm/v6/v7/v8 for pi1/2/3 and pi4-32) can't pull this
image when bun-builder inherits the target platform. Pin the stage
to $BUILDPLATFORM so it always runs on the build host instead: bun
is only used here to compile JS/CSS into the platform-independent
/app/static/dist/ that the next stage COPYs in, so the host
architecture doesn't matter. This also avoids a slow QEMU-emulated
`bun run build` on arm64 hosts.
Implication: building production images for 32-bit ARM requires an
amd64 or arm64 builder, which is what CI uses. The constraint is
bun itself, not Docker — there's no 32-bit bun binary to fall back
to even outside of Docker, so building this image on a pi3 was
never going to work. If device-local builds ever become a hard
requirement we'd need to either drop bun (e.g. back to Node, which
still ships armv7 binaries) or check `static/dist/` into the
image / a cache and skip this stage entirely. #}
FROM --platform=$BUILDPLATFORM ghcr.io/screenly/bun:1.3.13-slim AS bun-builder
RUN mkdir -p /app
WORKDIR /app
COPY package.json \
bun.lock \
tsconfig.json \
/app/
RUN bun install --frozen-lockfile
COPY ./src/anthias_server/app/static/sass/*.scss \
/app/src/anthias_server/app/static/sass/
COPY ./src/anthias_server/app/static/src/ \
/app/src/anthias_server/app/static/src/
# Tailwind v4 reads `@source` directives from tailwind.css, which
# point at ../../templates/**/*.html. Without the template tree in
# the builder context the JIT scan finds zero class strings and emits
# an almost-empty utility CSS — dev/test work because they share the
# host bind-mount, but the production image would ship unstyled.
# Copy the templates in before running the build.
COPY ./src/anthias_server/app/templates/ \
/app/src/anthias_server/app/templates/
RUN bun run build
{% endif %}
{% include 'uv-builder.j2' %}
{% include 'Dockerfile.base.j2' %}
COPY --from=uv-builder /venv /venv
ENV PATH="/venv/bin:$PATH"
ENV VIRTUAL_ENV="/venv"
RUN mkdir -p /usr/src/app
COPY . /usr/src/app/
WORKDIR /usr/src/app
{% if environment == 'production' %}
COPY --from=bun-builder \
/app/src/anthias_server/app/static/dist/ \
/usr/src/app/src/anthias_server/app/static/dist/
{% else %}
COPY --from=ghcr.io/screenly/bun:1.3.13-slim /usr/local/bin/bun /usr/local/bin/bun
RUN ln -sf bun /usr/local/bin/bunx
{% endif %}
ENV GIT_HASH={{ git_hash }}
ENV GIT_SHORT_HASH={{ git_short_hash }}
ENV GIT_BRANCH={{ git_branch }}
ENV DEVICE_TYPE={{ device_type }}
ENV DJANGO_SETTINGS_MODULE="anthias_server.django_project.settings"
ENV PYTHONPATH="/usr/src/app/src"
{% if environment == 'production' %}
# collectstatic at image build time. Source files (admin static + the
# bun-built dist/ copied in above) are immutable from this point on, so
# there's no reason to repeat the work on every container start. The
# previous setup ran `collectstatic --clear` from start_server.sh into
# a host bind-mount on /data/anthias/staticfiles — same data, every
# restart, into a writable host scratch dir. Baking the result into the
# image lets us drop the bind-mount, the runtime invocation, and one
# of the reasons /home/<user>/anthias has to persist after install.
#
# `HOME=/tmp/anthias-build` is throwaway: importing the Django settings
# module instantiates AnthiasSettings(), which writes a default
# anthias.conf into $HOME/.anthias/ if one isn't there yet. At runtime
# start_server.sh seeds /data/.anthias from ansible/roles/anthias/files/
# before this same import happens; at build time we just give it a
# scratch dir and remove it after collectstatic finishes.
RUN mkdir -p /tmp/anthias-build/.anthias && \
HOME=/tmp/anthias-build python -m anthias_server.manage collectstatic --noinput --clear && \
rm -rf /tmp/anthias-build
{% endif %}
CMD ["bash", "bin/start_server.sh"]