# syntax=docker/dockerfile:1.7 ############################ # Frontend build (Vite/React) ############################ # Run toolchains on the build machine, not target arch FROM --platform=$BUILDPLATFORM node:20-alpine AS fe WORKDIR /src/frontend COPY frontend/package*.json ./ RUN --mount=type=cache,target=/root/.npm npm ci COPY frontend/ . RUN npm run build # expose artifacts in a neutral location RUN mkdir -p /out && cp -r dist/* /out/ ############################ # Backend build (Go) ############################ FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS be RUN apk add --no-cache ca-certificates upx git ARG TARGETOS ARG TARGETARCH ENV CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ GOPROXY=https://proxy.golang.org,direct \ GOPRIVATE=scm.bstein.dev WORKDIR /src/backend # 1) Cache modules COPY backend/go.mod backend/go.sum ./ RUN --mount=type=cache,target=/go/pkg/mod go mod download # 2) Source COPY backend/ . # 3) FE assets where the embed expects them (//go:embed web/dist/**) COPY --from=fe /out ./web/dist # 4) Tidy (in case new deps appeared) RUN --mount=type=cache,target=/go/pkg/mod go mod tidy # 5) Build the binary; fail if build fails; allow UPX to fail only. RUN --mount=type=cache,target=/go/pkg/mod \ set -eux; \ mkdir -p /out; \ go build -trimpath -ldflags="-s -w" -o /out/pegasus ./main.go; \ test -f /out/pegasus; \ upx -q --lzma /out/pegasus || true ############################ # Final, minimal image ############################ FROM gcr.io/distroless/static:nonroot AS final COPY --from=be /out/pegasus /pegasus USER nonroot:nonroot ENTRYPOINT ["/pegasus"]