check / check (push) Waiting to run
script/bootstrap --cgo installs vips-jxl on Alpine, where libvips' JPEG XL loader and saver are a package of their own; the nix and brew libvips include them, as does apt's from Debian 12 and Ubuntu 24.04 on. The runtime stage of the Dockerfile installs vips-jxl too. imageprocessor.New now returns an error when libvips cannot load and save JPEG XL, and NewService passes it on, so pixad does not start without that support. JPEG XL becomes the default output later in the issue, which a pixad without it could not serve. A new test saves an image as JPEG XL with govips and loads it back. Model: opus-5-5
122 lines
4.7 KiB
Docker
122 lines
4.7 KiB
Docker
# Lint phase. script/lint builds it alone. The linter is run directly:
|
|
# `make lint` and script/lint are themselves a docker build.
|
|
# golangci/golangci-lint:v2.12.2, 2026-10-04
|
|
FROM golangci/golangci-lint@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240 AS lint
|
|
|
|
# The linter compiles every package, and govips needs the libvips
|
|
# headers for that. REPO_POLICIES.md has the lint phase install them
|
|
# itself; this image is Debian, so with apt-get rather than apk.
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends libvips-dev \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
WORKDIR /src
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
COPY . .
|
|
RUN golangci-lint run --config .golangci.yml ./...
|
|
|
|
# Test phase. script/test builds it alone.
|
|
# golang:1.25.4-alpine, 2026-02-25
|
|
FROM golang:1.25.4-alpine@sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb AS test
|
|
|
|
WORKDIR /src
|
|
|
|
# script/bootstrap --cgo installs the build dependencies (a C compiler,
|
|
# the libvips and libheif headers, and libvips' JPEG XL support, which
|
|
# the tests need) and downloads the Go modules.
|
|
COPY script/ ./script/
|
|
COPY go.mod go.sum ./
|
|
RUN script/bootstrap --cgo
|
|
|
|
COPY . .
|
|
|
|
# Without -v first; on a failure, again with -v for the details, and
|
|
# the step fails even if the second run passes.
|
|
RUN go test -count=1 -timeout 90s -race -cover ./... || \
|
|
{ echo "--- Rerunning with -v for details ---"; \
|
|
go test -count=1 -timeout 90s -race -v ./...; exit 1; }
|
|
|
|
# Build stage. Nothing is wanted from the two phases above: these copies
|
|
# make BuildKit build them first, so this stage runs only when lint and
|
|
# test passed.
|
|
# golang:1.25.4-alpine, 2026-02-25
|
|
FROM golang:1.25.4-alpine@sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb AS builder
|
|
|
|
COPY --from=lint /src/go.sum /dev/null
|
|
COPY --from=test /src/go.sum /dev/null
|
|
|
|
WORKDIR /src
|
|
|
|
# Build dependencies and Go modules, as in the test phase
|
|
COPY script/ ./script/
|
|
COPY go.mod go.sum ./
|
|
RUN script/bootstrap --cgo
|
|
|
|
# Copy source code
|
|
COPY . .
|
|
|
|
# VERSION is declared here, not earlier: a new value reruns only the
|
|
# build, not script/bootstrap. Given none, the version is
|
|
# `git describe --tags --always` of the .git in the build context (git
|
|
# comes from script/bootstrap): the tag on a tagged commit, tag-N-gHASH
|
|
# after one, the short commit when no tag is reachable. A context that
|
|
# carries .git and still yields no version fails the build; one without
|
|
# .git, as from a source tarball, stamps an empty version. CGO stays
|
|
# enabled for govips; -trimpath keeps build paths out of the binary, and
|
|
# -s -w leave out the symbol table and debug information.
|
|
ARG VERSION
|
|
RUN version="${VERSION:-$(git describe --tags --always)}"; \
|
|
if [ -e .git ] && { [ -z "$version" ] || [ "$version" = dev ] || \
|
|
[ "$version" = unknown ]; }; then \
|
|
echo "the build context carries .git but yields no version" >&2; \
|
|
exit 1; \
|
|
fi; \
|
|
CGO_ENABLED=1 GOTOOLCHAIN=auto go build -trimpath \
|
|
-ldflags "-s -w -X main.Version=${version}" \
|
|
-o /pixad ./cmd/pixad
|
|
|
|
# Runtime stage, and the last one: a plain `docker build .` builds this
|
|
# stage and what it depends on, and nothing else.
|
|
# alpine:3.21, 2026-02-25
|
|
FROM alpine:3.21@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709
|
|
|
|
# Install runtime dependencies only. vips-jxl is libvips' JPEG XL
|
|
# support, without which pixad does not start.
|
|
RUN apk add --no-cache \
|
|
vips \
|
|
vips-jxl \
|
|
libheif \
|
|
ca-certificates \
|
|
tzdata \
|
|
su-exec
|
|
|
|
# Copy binary from builder
|
|
COPY --from=builder /pixad /usr/local/bin/pixad
|
|
COPY deploy/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
|
|
|
# Create non-root user, config directory, and data directory. pixad
|
|
# gets uid and gid 65532, which host login and system accounts do not
|
|
# use: a bind-mounted /var/lib/pixa is given to pixad, and on the host
|
|
# it must not belong to a person's account.
|
|
RUN addgroup -g 65532 pixad && \
|
|
adduser -D -H -s /sbin/nologin -u 65532 -G pixad pixad && \
|
|
mkdir -p /var/lib/pixa /etc/pixa && \
|
|
chown pixad:pixad /var/lib/pixa
|
|
|
|
# No USER: the entrypoint must start as root to give a bind-mounted
|
|
# /var/lib/pixa to pixad; it then runs the server as pixad.
|
|
WORKDIR /var/lib/pixa
|
|
|
|
EXPOSE 8080
|
|
|
|
# Shell form so the probe follows PORT; a port set only in a mounted
|
|
# config file is not seen here.
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD wget --spider -q "http://localhost:${PORT:-8080}/.well-known/healthcheck.json" || exit 1
|
|
|
|
# Settings come from PORT and the PIXA_ environment variables; only
|
|
# PIXA_SIGNING_KEY is required. A config file mounted at
|
|
# /etc/pixa/config.yml is optional and is read when present.
|
|
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|