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/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. 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