feat(image): build multi-arch Image from the Pin and smoke-test it #12

Merged
piscis merged 2 commits from piscis/forgejo-4-multi-arch-implement into main 2026-09-30 13:31:17 +00:00
Owner

Summary

Tracer bullet: build the Image for linux/amd64 + linux/arm64 from the Pin, and smoke-test it locally.

+pin.env                # the Pin: UPSTREAM_VERSION=v3.2.0, UPSTREAM_COMMIT=e30bb7e…, REBUILD=1
+Dockerfile             # clone Upstream at tag → verify commit → cross-compile → distroless nonroot
+Makefile               # make build / make smoke-test (reads pin.env)
+scripts/smoke-test.sh  # <image-ref> <platform>, standalone for CI reuse
+.dockerignore
build (on $BUILDPLATFORM, no QEMU)
  git clone --branch $UPSTREAM_VERSION canonical Upstream
  fail unless HEAD == $UPSTREAM_COMMIT
  GOOS/GOARCH go build -ldflags "-X main.Version=3.2.0"
runtime (distroless/static:nonroot, USER 65532)
  ENTRYPOINT forgejo-mcp   CMD --transport stdio
  OCI labels: source=<Upstream tag URL> version=3.2.0 revision=<Upstream commit> licenses=GPL-3.0
  /usr/share/licenses/forgejo-mcp/LICENSE

Upstream checks GET /api/v1/version on its Forgejo before serving stdio, so the smoke test runs a stub Forgejo (busybox httpd) on a private internal network. No token, nothing outside the host is contacted.

Evidence

  • Wrong Pin commit (UPSTREAM_COMMIT=000…0): make build exits 2 with
    ERROR: Upstream tag v3.2.0 resolves to commit e30bb7e2…, but the Pin expects 0000000…. The tag was moved or the Pin is wrong.
  • Build: make build on an arm64 Mac → x86-64, statically linked and ARM aarch64, statically linked; amd64 build stage reports uname -m = aarch64.
  • Runtime: User=65532:65532, docker top shows uid 65532 on both archs; --version → forgejo-mcp 3.2.0 on both.
  • Smoke test:
    make smoke-test
      linux/amd64: PASS: 156 tools listed
      linux/arm64: PASS: 156 tools listed
    smoke-test busybox (never answers)      → FAIL: initialize got no response        (exit 1)
    smoke-test fake server, {"tools":[]}    → FAIL: tools/list returned an empty tool list (exit 1)
    
  • Metadata: docker inspect shows all four OCI labels; LICENSE in both images is byte-identical to Upstream's.

Merge Danger

Door: two-way

Nothing is published yet; no CI workflow in this PR.

Blast Radius: none

Local build tooling only. CI (later) will read pin.env and call scripts/smoke-test.sh, and needs docker networking for the stub Forgejo.

Closes #4

## Summary Tracer bullet: build the Image for linux/amd64 + linux/arm64 from the Pin, and smoke-test it locally. ```diff +pin.env # the Pin: UPSTREAM_VERSION=v3.2.0, UPSTREAM_COMMIT=e30bb7e…, REBUILD=1 +Dockerfile # clone Upstream at tag → verify commit → cross-compile → distroless nonroot +Makefile # make build / make smoke-test (reads pin.env) +scripts/smoke-test.sh # <image-ref> <platform>, standalone for CI reuse +.dockerignore ``` ```text build (on $BUILDPLATFORM, no QEMU) git clone --branch $UPSTREAM_VERSION canonical Upstream fail unless HEAD == $UPSTREAM_COMMIT GOOS/GOARCH go build -ldflags "-X main.Version=3.2.0" runtime (distroless/static:nonroot, USER 65532) ENTRYPOINT forgejo-mcp CMD --transport stdio OCI labels: source=<Upstream tag URL> version=3.2.0 revision=<Upstream commit> licenses=GPL-3.0 /usr/share/licenses/forgejo-mcp/LICENSE ``` Upstream checks `GET /api/v1/version` on its Forgejo before serving stdio, so the smoke test runs a stub Forgejo (busybox httpd) on a private internal network. No token, nothing outside the host is contacted. ## Evidence - **Wrong Pin commit** (`UPSTREAM_COMMIT=000…0`): `make build` exits 2 with `ERROR: Upstream tag v3.2.0 resolves to commit e30bb7e2…, but the Pin expects 0000000…. The tag was moved or the Pin is wrong.` - **Build:** `make build` on an arm64 Mac → `x86-64, statically linked` and `ARM aarch64, statically linked`; amd64 build stage reports `uname -m` = `aarch64`. - **Runtime:** `User=65532:65532`, `docker top` shows uid 65532 on both archs; `--version` → `forgejo-mcp 3.2.0` on both. - **Smoke test:** ```text make smoke-test linux/amd64: PASS: 156 tools listed linux/arm64: PASS: 156 tools listed smoke-test busybox (never answers) → FAIL: initialize got no response (exit 1) smoke-test fake server, {"tools":[]} → FAIL: tools/list returned an empty tool list (exit 1) ``` - **Metadata:** `docker inspect` shows all four OCI labels; LICENSE in both images is byte-identical to Upstream's. ## Merge Danger **Door:** two-way Nothing is published yet; no CI workflow in this PR. **Blast Radius:** none Local build tooling only. CI (later) will read `pin.env` and call `scripts/smoke-test.sh`, and needs docker networking for the stub Forgejo. Closes #4
Add the Pin (pin.env: Upstream Version v3.2.0, its commit, Rebuild 1) and a
Dockerfile that clones the canonical Upstream at the pinned tag, fails if the
tag no longer resolves to the pinned commit, cross-compiles natively via
BUILDPLATFORM/GOOS/GOARCH with Upstream's version ldflags, and runs on
distroless static as non-root with OCI labels and Upstream's LICENSE.

`make build` builds linux/amd64 and linux/arm64 with buildx.

Refs #4
scripts/smoke-test.sh <image-ref> <platform> runs the Image over stdio with no
token against a stub Forgejo, sends initialize, notifications/initialized and
tools/list, and fails on a timeout, an error or an empty tool list.
`make smoke-test` runs it for both architectures.

Refs #4
piscis merged commit 3f9b2c63e2 into main 2026-09-30 13:31:17 +00:00
Sign in to join this conversation.
No description provided.