build zampler binary in CI instead of the Dockerfile
Build / build-and-push (push) Successful in 5m9s

The Go compile is dominated by modernc.org/libc (~400k lines of
generated Go from modernc.org/sqlite). Inside `docker build` on an
ephemeral runner it recompiled cold every push; on a 1 CPU / 1 GB
runner that took ~10 minutes.

Move the frontend and Go builds into the workflow, where actions/cache
persists ~/.cache/go-build across runs so libc is compiled once. The
Dockerfile is now just an alpine wrapper around the prebuilt
CGO_ENABLED=0 static binary.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-06 15:26:36 +01:00
co-authored by Claude Sonnet 5
parent 974edb28c2
commit dd6bb1a547
3 changed files with 42 additions and 42 deletions
+3 -11
View File
@@ -1,11 +1,3 @@
.git
.gitea
.vscode
docs
**/node_modules
config.json
zampler
zampler.files
zampler.sqlite3
*.env
internal/server/static
# Build context only needs the prebuilt ./zampler binary and the Dockerfile.
*
!zampler
+33 -3
View File
@@ -9,12 +9,42 @@ jobs:
build-and-push:
runs-on: ubuntu-latest
container:
image: docker:cli
image: golang:1.26-alpine
env:
CGO_ENABLED: "0"
steps:
- name: Install Node
run: apk add --no-cache nodejs
- name: Install toolchain deps
run: apk add --no-cache nodejs npm docker-cli git tar
- uses: actions/checkout@v4
- name: Cache npm downloads
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ hashFiles('frontend/package-lock.json') }}
restore-keys: npm-
- name: Cache Go modules and build cache
uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: go-${{ hashFiles('go.sum') }}-${{ hashFiles('**/*.go') }}
restore-keys: |
go-${{ hashFiles('go.sum') }}-
go-
- name: Build frontend
working-directory: frontend
run: |
npm ci
npx vite build --outDir ../internal/server/static --emptyOutDir
- name: Build binary
run: go build -ldflags="-s -w" -o zampler .
- name: Log in to the container registry
env:
BUILD_API_TOKEN: ${{ secrets.BUILD_API_TOKEN }}
+6 -28
View File
@@ -1,34 +1,12 @@
# syntax=docker/dockerfile:1
FROM node:24-alpine AS node-build
WORKDIR /build
# Install deps first so this layer is only rebuilt when the lockfile changes.
COPY frontend/package.json frontend/package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY frontend/ ./
RUN npx vite build --outDir dist
FROM golang:1.26-alpine AS go-build
WORKDIR /build
# Download modules first so this layer survives source-only changes.
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
COPY --from=node-build /build/dist ./internal/server/static
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=0 go build -ldflags="-s -w" -o zampler
# The zampler binary is built in CI (.gitea/workflows/build.yml) so the compile
# can use a persistent Go module + build cache. This image is just the runtime
# wrapper around the prebuilt static binary.
#
# For a local build: `go build -o zampler .` first, then `docker build .`.
FROM alpine:3.24
COPY --from=go-build /build/zampler /usr/local/bin
COPY zampler /usr/local/bin/zampler
ENTRYPOINT ["zampler"]
CMD ["-s", "/db/zampler.sqlite3", "/data"]