mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-12 05:49:00 +00:00
7bb9076ac5
The previous build line had:
-X main.commit=$(git rev-parse --short HEAD 2>/dev/null) \
-X main.buildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)
The `git rev-parse` ran INSIDE the build container, but .dockerignore
intentionally excludes `.git/` so the working directory has no git
metadata; the `2>/dev/null` swallowed the resulting error and the
substitution silently produced an empty string. Net result: every
Docker build of pad shipped a binary with `commit=""`, which
fullVersion() collapses to just `version` ("dev") with no commit
metadata at all. Caught during Claude Desktop dogfooding when
`pad_meta` returned `pad_version: "dev"` and bug reports had no way
to identify which build they were hitting.
Two ways to fix: add .git/ to the build context (rejected — operator
explicitly does not want .git in the image build), or pre-compute on
the host and pass via --build-arg (this PR). The wrapper that does
the host-side computation lands in pad-cloud separately.
The `date` substitution worked because alpine has `date` in the
builder image, but it had a worse problem: a fresh `date` value on
every build invalidates layer caching for this RUN. Moving to ARG
lets the caller decide cache semantics — production wrappers will
pass a real timestamp; dev rebuilds can omit BUILD_TIME entirely
to keep the cache warm.
Defaults are deliberately ugly-but-honest so a `docker build .`
without args produces "dev (unknown)" rather than hiding the
misconfiguration. Production builds with all three args produce
e.g. "0.1.0-rc.5 (40f636e 2026-05-03T00:15:00Z)".
Sanity-checked all four input combinations (defaults, all-set,
version+commit only, commit-empty) against the existing
fullVersion() logic — output shapes are clean for each.
64 lines
2.1 KiB
Docker
64 lines
2.1 KiB
Docker
# Stage 1: Build web UI
|
|
FROM node:22-alpine AS web-builder
|
|
WORKDIR /app/web
|
|
COPY web/package.json web/package-lock.json ./
|
|
RUN npm ci
|
|
COPY web/ ./
|
|
RUN npm run build
|
|
|
|
# Stage 2: Build Go binary
|
|
FROM golang:1.26-alpine AS go-builder
|
|
WORKDIR /app
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
COPY . .
|
|
COPY --from=web-builder /app/web/build ./web/build
|
|
|
|
# Build metadata. All three are caller-passed via --build-arg (see
|
|
# pad-cloud/scripts/build-pad.sh for the production wrapper that
|
|
# resolves them from the host's pad checkout).
|
|
#
|
|
# Why all three are passed in vs. computed inside the container:
|
|
#
|
|
# - .dockerignore intentionally excludes .git/, so an in-container
|
|
# `git rev-parse` substitution returns empty (with `2>/dev/null`
|
|
# swallowing the error) — the previous Dockerfile shipped "dev"
|
|
# forever because of this. We don't want to add .git/ to the
|
|
# context just for this; pre-computing on the host is the
|
|
# standard pattern.
|
|
# - `date` would work in-container but a fresh `date` value on
|
|
# every build invalidates layer caching for this RUN. Passing
|
|
# as ARG lets the caller decide cache semantics.
|
|
#
|
|
# Defaults are deliberately ugly-but-honest so a `docker build .`
|
|
# without args produces a binary whose pad_version makes the
|
|
# misconfiguration obvious ("dev (unknown)") rather than hiding it.
|
|
ARG VERSION=dev
|
|
ARG COMMIT=unknown
|
|
ARG BUILD_TIME=
|
|
RUN CGO_ENABLED=0 go build \
|
|
-ldflags="-s -w -X main.version=${VERSION} -X main.commit=${COMMIT} -X main.buildTime=${BUILD_TIME}" \
|
|
-o pad ./cmd/pad
|
|
|
|
# Stage 3: Runtime
|
|
FROM alpine:3.21
|
|
RUN apk add --no-cache ca-certificates tzdata
|
|
COPY --from=go-builder /app/pad /usr/local/bin/pad
|
|
|
|
# Create a non-root user (uid 1000) and data directory owned by it
|
|
RUN adduser -D -u 1000 -h /home/pad pad \
|
|
&& mkdir -p /data \
|
|
&& chown -R pad:pad /data
|
|
ENV PAD_DATA_DIR=/data
|
|
ENV PAD_HOST=0.0.0.0
|
|
|
|
USER pad
|
|
EXPOSE 7777
|
|
VOLUME /data
|
|
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD wget -q --spider http://localhost:7777/api/v1/health || exit 1
|
|
|
|
ENTRYPOINT ["pad"]
|
|
CMD ["server", "start"]
|