check / check (push) Canceled after 0s
Takes .golangci.yml, .dockerignore, .gitignore, .editorconfig, the workflow and REPO_POLICIES.md from sneak/prompts 0653579, with this repository's own entries after the shared content. Lint (golangci-lint v2.14.0) and test are Dockerfile phases that script/lint and script/test build alone with a new CACHEBUST value, keeping Go's and golangci-lint's caches; the image build depends on both. make fmt and make fmt-check also format the markdown with prettier, pinned in yarn.lock. The agent guidance moves to AGENTS.md. The Go code is fixed for what the new configuration finds, and the AS and prefix detail reads now return the rows' error after their loops. Model: opus-5-5
147 lines
5.8 KiB
Docker
147 lines
5.8 KiB
Docker
# In every stage ARG CACHEBUST comes right after the steps that install
|
|
# system packages and download Go's modules, or right after FROM in a
|
|
# stage with neither, and every step that lints, tests or compiles
|
|
# comes after it. script/ passes a new value on every build, which
|
|
# reruns every RUN step after it. Docker keeps the layers above it
|
|
# until the base image, their own lines or go.mod and go.sum change.
|
|
# The steps that compile mount Go's build cache, and golangci-lint run
|
|
# also mounts golangci-lint's; both survive from build to build.
|
|
|
|
# Lint phase. The image is Debian-based and has gcc, so it can check the
|
|
# cgo SQLite package without extra installs.
|
|
# golangci/golangci-lint:v2.14.0 (Go 1.27.0), 2026-10-19
|
|
FROM golangci/golangci-lint@sha256:ad862ba6b3798cbe0fd9fd7408d498fd74fbd2623a92406b2fd3898faf0bf98f AS lint
|
|
WORKDIR /src
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
ARG CACHEBUST
|
|
COPY . .
|
|
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
--mount=type=cache,target=/root/.cache/golangci-lint \
|
|
golangci-lint run --config .golangci.yml ./...
|
|
|
|
# Test phase. -race needs cgo and so a C compiler, which the Debian Go
|
|
# image ships and the alpine one does not. -count=1 because the mounted
|
|
# cache also holds test results, which go test would otherwise replay.
|
|
# -short skips the live RIS Live feed test, so the suite needs no network.
|
|
# golang:1.24-bookworm, 2026-09-21
|
|
FROM golang@sha256:1a6d4452c65dea36aac2e2d606b01b4a029ec90cc1ae53890540ce6173ea77ac AS test
|
|
WORKDIR /src
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
ARG CACHEBUST
|
|
COPY . .
|
|
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
go test -short -count=1 -timeout 90s -race -cover ./... || \
|
|
{ echo "--- Rerunning with -v for details ---"; \
|
|
go test -short -count=1 -timeout 90s -race -v ./...; exit 1; }
|
|
|
|
# Build stage. Nothing is wanted from either phase above; the copies
|
|
# are what make BuildKit build them first, so nothing after them can
|
|
# run unless lint and test passed.
|
|
# golang:1.24-bookworm, 2026-09-21
|
|
FROM golang@sha256:1a6d4452c65dea36aac2e2d606b01b4a029ec90cc1ae53890540ce6173ea77ac AS builder
|
|
|
|
# Install build dependencies (zstd for archive, gcc for CGO/sqlite3)
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
zstd \
|
|
gcc \
|
|
libc6-dev \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
WORKDIR /src
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
ARG CACHEBUST
|
|
COPY --from=lint /src/go.sum /dev/null
|
|
COPY --from=test /src/go.sum /dev/null
|
|
# A tar-stream context keeps the sender's file owners, which git refuses.
|
|
RUN git config --system --add safe.directory /src
|
|
COPY . .
|
|
|
|
# Vendor dependencies for the source archive (must be after copying source)
|
|
RUN go mod vendor
|
|
|
|
# Build the binary with CGO enabled (required for sqlite3). The version the
|
|
# page footer shows is the VERSION build arg when one is given, otherwise
|
|
# `git describe --tags --always` on the .git in the build context: the tag
|
|
# on a tagged commit, tag-N-gHASH after one, the short commit when no tag is
|
|
# reachable. With .git present, a version that is still empty, dev or
|
|
# unknown fails the build: git is missing or could not read the checkout.
|
|
# Without .git the footer says unknown. The footer links to the full commit.
|
|
ARG VERSION
|
|
RUN --mount=type=cache,target=/root/.cache/go-build \
|
|
VERSION="${VERSION:-$(git describe --tags --always)}"; \
|
|
if [ -e .git ]; then \
|
|
case "$VERSION" in ""|dev|unknown) \
|
|
echo "version is '$VERSION' although .git is present" >&2; \
|
|
exit 1 ;; \
|
|
esac; \
|
|
fi; \
|
|
REVISION="$(git rev-parse --verify HEAD || echo unknown)"; \
|
|
CGO_ENABLED=1 GOOS=linux go build -o /routewatch -ldflags "\
|
|
-X git.eeqj.de/sneak/routewatch/internal/version.GitRevision=$REVISION \
|
|
-X git.eeqj.de/sneak/routewatch/internal/version.GitRevisionShort=${VERSION:-unknown}" \
|
|
./cmd/routewatch
|
|
|
|
# Create source archive with vendored dependencies
|
|
RUN tar --zstd -cf /routewatch-source.tar.zst \
|
|
--exclude='.git' \
|
|
--exclude='*.tar.zst' \
|
|
.
|
|
|
|
# Runtime stage, and the last one
|
|
# debian:bookworm-slim, 2026-09-21
|
|
FROM debian@sha256:3783cc01769c7b2b1b83a5c5ad96c815348e28ed7da68e2e3687004faa906251
|
|
|
|
# Install runtime dependencies
|
|
# - ca-certificates: for HTTPS connections
|
|
# - curl: for health checks
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
ca-certificates \
|
|
curl \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
ARG CACHEBUST
|
|
|
|
# Create non-root user
|
|
RUN useradd -r -u 1000 -m routewatch
|
|
|
|
RUN mkdir -p /var/lib/berlin.sneak.app.routewatch && chown routewatch:routewatch /var/lib/berlin.sneak.app.routewatch
|
|
|
|
RUN mkdir /app
|
|
WORKDIR /app
|
|
|
|
# Copy binary and source archive from builder
|
|
COPY --from=builder /routewatch /app/routewatch
|
|
COPY --from=builder /routewatch-source.tar.zst /app/source/routewatch-source.tar.zst
|
|
|
|
# Set ownership
|
|
RUN chown -R routewatch:routewatch /app
|
|
|
|
ENV XDG_DATA_HOME=/var/lib
|
|
|
|
# Cap the Go heap at 1.5 GiB so the runtime collects harder before the
|
|
# container's memory limit is reached. setpriv in the entrypoint preserves this
|
|
# the way it does XDG_DATA_HOME above.
|
|
ENV GOMEMLIMIT=1536MiB
|
|
|
|
# Cap glibc's malloc arenas. The SQLite C library allocates and frees millions
|
|
# of small page-cache chunks from many threads; glibc otherwise creates up to
|
|
# eight arenas per core (hundreds on a large host) and keeps each arena's freed
|
|
# chunks resident, so process RSS climbs far above SQLite's live heap and never
|
|
# comes back down. Two arenas keep that retained memory bounded; database writes
|
|
# are already serialized, so the lost allocator concurrency costs nothing here.
|
|
ENV MALLOC_ARENA_MAX=2
|
|
|
|
# Expose HTTP port
|
|
EXPOSE 8080
|
|
|
|
COPY ./entrypoint.sh /entrypoint.sh
|
|
|
|
# Health check using the health endpoint, on the port PORT names
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD curl -sf "http://localhost:${PORT:-8080}/.well-known/healthcheck.json" || exit 1
|
|
|
|
ENTRYPOINT ["/bin/bash", "/entrypoint.sh" ]
|