From 100b944181515dda9e5bc9fe436d5651c168260d Mon Sep 17 00:00:00 2001 From: tmphost-dev Date: Sat, 24 Oct 2026 02:18:20 +0000 Subject: [PATCH 1/2] Cross-compile the Docker image instead of emulating arm64 The arm64 go build ran under QEMU and took ~13 of the publish run's 16 minutes. The web and Go stages now run on $BUILDPLATFORM and Go cross-compiles with GOARCH=$TARGETARCH, so only the scratch stage, which just copies files, is per platform. QEMU is no longer needed. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/docker-publish.yml | 3 --- .github/workflows/docker-release.yml | 3 --- Dockerfile | 12 +++++++++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index c58b6af..6305de6 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -18,9 +18,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 27cef4b..459c065 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -18,9 +18,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/Dockerfile b/Dockerfile index cdf7166..61d7932 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,23 @@ -FROM node:24-alpine AS web +# Both build stages run on the builder's own platform and the Go stage +# cross-compiles, so a multi-platform build never runs a compiler under QEMU +# (the arm64 go build took ~13 minutes emulated). Only the scratch stage is +# per platform, and it only copies files. +FROM --platform=$BUILDPLATFORM node:24-alpine AS web WORKDIR /src/frontend COPY frontend/package.json frontend/package-lock.json ./ RUN npm ci --no-audit --no-fund COPY frontend/ ./ RUN npm run build -FROM golang:1.27-alpine AS api +FROM --platform=$BUILDPLATFORM golang:1.27-alpine AS api +ARG TARGETOS TARGETARCH RUN apk add --no-cache ca-certificates WORKDIR /src/backend COPY backend/go.mod backend/go.sum ./ RUN go mod download COPY backend/ ./ -RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /sql-mcp ./cmd/server +RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \ + go build -trimpath -ldflags "-s -w" -o /sql-mcp ./cmd/server # scratch has no users: name UID 65532 (distroless's nonroot, kept so volumes # chowned for the old image still work) and create the volume it must own. RUN echo 'nonroot:x:65532:65532:nonroot:/:' > /etc_passwd && mkdir -p /data From 9ffc1aee2ad9779e2665b60e37f7c281dfc127d8 Mon Sep 17 00:00:00 2001 From: tmphost-dev Date: Sat, 24 Oct 2026 02:18:38 +0000 Subject: [PATCH 2/2] Note in AGENTS.md why the image cross-compiles Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 2ebf10c..231cd9d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -64,6 +64,9 @@ app state). A handler never calls the repository directly. in: the binary, the web app, CA certs, `/etc/passwd` and `/data`. A new runtime need (`/tmp`, tzdata) must be copied too, or it fails only in Docker. It runs as UID 65532 so volumes chowned for the old distroless image work. +- **The image cross-compiles, never emulates.** Build stages run on + `$BUILDPLATFORM` with `GOARCH=$TARGETARCH`; a compile step in a per-platform + stage runs under QEMU (the arm64 Go build took ~13 minutes that way). - **Vite builds with `base: ''`.** The server rewrites `./` to `/` in `index.html` (`app.spa`). Without that rewrite, a hard reload on a nested route such as `/databases/x` serves a blank page.