From bd24ec3baf1389414d8a37d473968b80d8445d45 Mon Sep 17 00:00:00 2001 From: Chris Fordham Date: Mon, 24 Aug 2026 19:08:59 +1000 Subject: [PATCH 1/2] ci: publish linux/arm64 images as well as amd64 [main] Buildx was already being set up but no platforms list was ever passed, so every published image was amd64-only. That breaks Apple Silicon and ARM runners, which have to fall back to emulation or simply cannot run the image. The Dockerfile now pins the builder to $BUILDPLATFORM and passes TARGETARCH through to the compiler, so the Go build runs natively and cross-compiles rather than emulating the whole toolchain under QEMU. Verified locally: a --platform linux/arm64 build compiles in ~30s and produces a genuine ELF aarch64 static binary in an arm64 image. --- .github/workflows/docker.yml | 4 ++++ Dockerfile | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 016b139..5f97e95 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -50,6 +50,10 @@ jobs: uses: docker/build-push-action@v7 with: context: . + # linux/amd64 only until now, which broke Apple Silicon and ARM + # runners. Buildx was already set up; the platforms list was simply + # never passed. + platforms: linux/amd64,linux/arm64 push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/Dockerfile b/Dockerfile index d59dd1f..e8c1b1c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,19 @@ -FROM golang:1.24-alpine AS builder +# --platform=$BUILDPLATFORM keeps the toolchain running natively; the target +# architecture is passed to the compiler instead. Without this, a multi-arch +# build emulates the whole Go compile under QEMU, which is an order of magnitude +# slower and occasionally flaky. +FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder + +ARG TARGETOS +ARG TARGETARCH WORKDIR /workspace COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN CGO_ENABLED=0 GOOS=linux go build -a -o manager ./cmd/main.go +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} \ + go build -a -o manager ./cmd/main.go FROM gcr.io/distroless/static:nonroot LABEL org.opencontainers.image.source="https://github.com/kube-workspaces/controller" From f330c70b25cc37029a9875bd534df24152ecb1bd Mon Sep 17 00:00:00 2001 From: Chris Fordham Date: Mon, 24 Aug 2026 19:25:07 +1000 Subject: [PATCH 2/2] ci: verify the published image actually deploys [main] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This repo proved its image builds and nothing more. A change here that breaks the deployment manifests — a renamed CRD field, a moved route, a changed port — stayed invisible until somebody happened to touch the deploy repo. After pushing an image, call the reusable deployment test in kube-workspaces/deploy: it stands up a kind cluster, installs the CRDs and all four components with this build pinned to the commit under test, and runs the smoke and functional suites. Skipped on pull_request, since nothing is pushed there for the test to pull. The image tag comes from a build-job output rather than being reconstructed in the caller: docker/metadata-action is configured with type=sha,prefix= so the tag is the bare 7-character SHA (confirmed against the tags currently in GHCR), and duplicating that formatting would eventually drift. --- .github/workflows/docker.yml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5f97e95..f5a8c26 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -18,6 +18,9 @@ jobs: permissions: contents: read packages: write + outputs: + # The bare short SHA tag, consumed by the downstream deployment test. + image_tag: ${{ steps.tag.outputs.value }} steps: - name: Checkout @@ -35,6 +38,10 @@ jobs: type=raw,value=latest,enable={{is_default_branch}} type=sha,prefix= + - name: Derive the short SHA tag + id: tag + run: echo "value=$(echo ${{ github.sha }} | cut -c1-7)" >> "$GITHUB_OUTPUT" + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 @@ -60,4 +67,17 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max - + # Verify the freshly published image actually deploys. Without this, a change + # here that breaks the deployment manifests stays invisible until somebody + # happens to touch the deploy repo. + deployment-test: + name: Deployment test + needs: build + if: github.event_name != 'pull_request' + uses: kube-workspaces/deploy/.github/workflows/deployment-test.yaml@main + with: + component: controller + # docker/metadata-action is configured with type=sha,prefix= so the tag is + # the bare short SHA. Take it from the build job rather than reconstructing + # it here, so the two cannot drift. + image_tag: ${{ needs.build.outputs.image_tag }}