Skip to content

chore(ci): build and test every arm64 target - #682

Merged
David-Noble-at-work merged 3 commits into
developfrom
chore/arm64-build-and-test-matrix
Aug 25, 2026
Merged

David-Noble-at-work merged 3 commits into
developfrom
chore/arm64-build-and-test-matrix

Conversation

@David-Noble-at-work

Copy link
Copy Markdown
Contributor

Closes #681

Two build/CI chores. No production code changes.

1. Nothing on the push path compiled for arm64

quality-gate ran make build (host only) plus make build-all, which sweeps GOOS at a
fixed GOARCH
. goreleaser ships all six pairs, so an arm64-only break surfaced at release
rather than at review.

ALL_PLATFORMS already listed all six and make build PLATFORM=all already produced them —
CI never asked. Now it does.

build-all is dropped, and I checked what that costs. build PLATFORM=all links the
120 of 122 packages reachable from ./cmd; the two outside its reach are
pkg/op/provider/elevator and pkg/op/server. But vet-all stays and type-checks all 122
per GOOS including test files, which go build ./... never did — so those two keep their
coverage, and build-all was largely redundant with the step below it. The Makefile target
remains for local use.

2. Linux and Windows arm64 were never executed

Leg Target Command
macos-latest darwin/arm64 make test-race
ubuntu-latest linux/amd64 make test-race
ubuntu-24.04-arm linux/arm64 make test-race
windows-latest windows/amd64 make test-race
windows-11-arm windows/arm64 make test

darwin/arm64 was covered all along — macos-latest is Apple Silicon. The gap was Linux and
Windows arm64, unblocked by arm64 runners reaching GA for private repositories on 2026-01-29.

PLATFORM=host

New keyword beside all. Named host rather than current or local because CROSS is
already defined in this Makefile as target ≠ host — so PLATFORM=host names the one
selection that makes CROSS empty, and every HOST_ variable here comes from Go's
GOHOSTOS/GOHOSTARCH.

PLATFORM stays empty by default. PLATFORM ?= host would have been the obvious way to
write it and would have silently broken dist: a non-empty default is never overridden by a
target, so dist's default becomes unreachable and releases go host-only without saying so.
The defaults now live at the call sites as keywords — $(call select,host) for build,
$(call select,all) for dist.

Three constraints, recorded where they bind

  • No -latest alias exists for arm64. ubuntu-24.04-arm is pinned because ubuntu-latest
    still resolves to Ubuntu 24.04 and ubuntu-26.04-arm is a preview image. Matching
    versions means the arm64 leg varies architecture alone; vary the OS release too and a failure
    has two candidate causes.
  • Go's race detector does not support windows/arm64. That leg runs make test — the full
    suite, only race detection absent. linux/arm64 is supported and runs -race unchanged.
  • The Windows pair cannot be version-matched. windows-latest is Windows Server 2025,
    windows-11-arm is a client OS, and no Windows Server arm64 runner exists. The platform's
    asymmetry, not a choice made here — worth knowing before a Windows leg diverges.

platform-test-matrix.md updated in step

Its phase 4 (the ruleset change on 12426847) must now require five contexts per job, not
three, and the five names are written out there. Requiring three of five would leave both arm64
legs advisory while the rest gate — a gate that looks complete and is not.

Added a warning that wasn't there: the test job now uses matrix.include, and GitHub composes
check names from matrix values. A required context whose name does not match exactly never
arrives, which blocks every merge rather than none. Verify the strings against a real run
before writing them into the ruleset.

Historical "three" references in that plan are deliberately untouched — the three [build failed]
packages, the three uncompilable test files, the three-GOOS analysis sweep. Those are findings and
a still-correct GOOS count, not stale platform arithmetic.

Test plan

  • Makefile expansion verified: build → darwin/arm64; build PLATFORM=host →
    darwin/arm64; build PLATFORM=linux/arm64 → linux/arm64; build PLATFORM=all → all
    six; dist → all six; dist PLATFORM=host → darwin/arm64
  • ci.yaml parses; five legs on both jobs; race flag drives the two conditional steps
  • Coverage of the dropped build-all accounted for (120/122 + vet-all over 122)
  • This PR is the first exercise of the legs it adds. test (ubuntu-24.04-arm) and
    test (windows-11-arm) have never run. Per the plan's phase 3, their first results are
    triage data — failures there are findings to file, not automatically blockers.
  • windows-11-arm symlink behavior unverified. platform-test-matrix flags symlinks as the
    Windows risk; an arm64 image is a fresh instance of that question.

David Noble added 3 commits August 25, 2026 14:15
Two gaps, both on the push path.

Nothing in CI compiled for arm64. quality-gate ran `make build` (host only)
and `make build-all`, which sweeps GOOS at a fixed GOARCH, while goreleaser
ships linux, darwin, and windows across amd64 and arm64. An arm64-only break
therefore surfaced at release rather than at review. ALL_PLATFORMS already
named all six and `make build PLATFORM=all` already produced them; CI simply
never asked. The codebase is pure Go — zero `import "C"` — so one host links
the whole matrix with GOOS/GOARCH alone, ~41s against ~13s host-only.

build-all is dropped rather than kept. `build PLATFORM=all` covers the 120 of
122 packages reachable from ./cmd across all six pairs instead of three GOOS
at the host's arch, and vet-all still type-checks all 122 including test
files, which `go build ./...` never did. The two packages outside cmd's reach,
pkg/op/provider/elevator and pkg/op/server, keep their per-GOOS coverage. The
Makefile target remains for local use.

Linux and Windows arm64 were never executed. Both matrices ran ubuntu-latest,
macos-latest, and windows-latest — three of the five runnable pairs;
darwin/arm64 was always covered because macos-latest is Apple Silicon. arm64
standard runners reached GA for private repositories on 2026-01-29 and are
free-tier eligible, so the blocker present when platform-test-matrix.md was
written is gone. Both jobs now run five legs.

PLATFORM gains `host` beside `all`. The name is `host` rather than `current`
or `local` because CROSS is already defined here as target != host, so
PLATFORM=host names the selection that makes CROSS empty — and every HOST_
variable in this file comes from Go's own GOHOSTOS/GOHOSTARCH. PLATFORM stays
empty by default: a non-empty default is never overridden by a target, so
dist's default would become unreachable and releases would go host-only
without announcing it. The defaults moved to the call sites as keywords.

Three constraints are recorded where they bind rather than in the plan alone.
There is no -latest alias for arm64, so ubuntu-24.04-arm is pinned — it is
what ubuntu-latest resolves to today, and ubuntu-26.04-arm is a preview image;
matching versions means the new leg varies architecture alone. Go's race
detector does not support windows/arm64, so that leg runs `make test` — the
full suite, without race detection. And windows-latest is Windows Server 2025
while windows-11-arm is a client OS, an asymmetry the platform imposes because
no Windows Server arm64 runner exists.

platform-test-matrix.md is updated in step: its phase 4 ruleset change must
now name five contexts per job. Requiring three of five would leave both arm64
legs advisory while the rest gate.

Closes #681
The first run of the five-leg matrix reported contexts like
`test (windows-11-arm, false)`. Without an explicit job name GitHub composes
the context from every matrix value, so the runner image and the race flag
both leaked into a string the ruleset has to match exactly — and adding any
further matrix key would rename all five contexts at once.

Both jobs now set `name: <job> (${{ matrix.platform }})` and carry a platform
key, so the context is the platform descriptor and nothing else:
test (darwin-arm64), test (linux-amd64), test (linux-arm64),
test (windows-amd64), test (windows-arm64), and the same five for scenario.

These match ALL_PLATFORMS and the build/<goos>-<goarch> directories, which is
the vocabulary the rest of the build already speaks. The runner label stays in
runs-on where it belongs: whether linux/arm64 comes from ubuntu-24.04-arm or a
later image is not something a required-check name should encode. It also
hides the one asymmetry the platform forces on us — windows-latest is Windows
Server 2025 while windows-11-arm is a client OS — behind a descriptor that
says only what the binary targets.

platform-test-matrix.md phase 4 carried the wrong strings, written before the
first run showed what GitHub actually emits. Corrected, with the mechanism
recorded so the next person does not rediscover it from a blocked merge: a
required context whose name never arrives blocks every merge rather than none.
Both windows-arm64 legs failed, and all four failures shared one cause:

    git [init --quiet --initial-branch=main]: exit status 128
    fatal: unable to access 'NUL': Invalid argument

TestRepo_Add_ClonesURL_DefaultHome, TestRepo_Add_ClonesURL_PositionalDestination,
TestWritDeployScenario_Harness, and TestWritDeployScenario_Deploy each die at the
same line: two test helpers pointed git's global and system config at
os.DevNull, which is "NUL" on Windows, and the git build on the windows-11-arm
runner image refuses to open it as a config path.

The identical command succeeds on windows-latest, so this is a property of that
image's git rather than of the architecture — which is exactly why it stayed
hidden until windows/arm64 joined the matrix, and why it is worth having found.

What the helpers actually want is a config git can open and find nothing in.
The null device was a convenient stand-in for that, and it is the one form
whose semantics vary by platform and by git build. An empty regular file in the
test's temp directory is the same thing with no special behavior anywhere.

Both call sites now share an isolatedGitEnv helper carrying the explanation, so
the next person reaching for os.DevNull sees why it is not used here.

Rejected alternatives: a nonexistent config path works today but depends on how
each git version treats an unreadable config; `git -c` overrides individual keys
without stopping the global config being read, which is whack-a-mole against
core.hooksPath; and setting HOME covers global config but not GIT_CONFIG_SYSTEM.

Verified on darwin/arm64: TestWritDeployScenario_Harness, _Deploy,
TestSelfInstallScenario (lore, star, writ), and TestRepo_Add_ClonesURL_* all
pass; gofmt and go vet clean. That proves the change breaks nothing here. It
cannot prove it fixes windows/arm64 — only that leg can, which is why this
commit is pushed rather than merged.
@David-Noble-at-work
David-Noble-at-work merged commit fd8c551 into develop Aug 25, 2026
12 checks passed
@David-Noble-at-work
David-Noble-at-work deleted the chore/arm64-build-and-test-matrix branch August 25, 2026 23:39
David-Noble-at-work pushed a commit that referenced this pull request Aug 26, 2026
One conflict, in pkg/op/provider/function/provider.go. Both sides were right about
different things: develop carried #676's sealing of function.Resource, and this
branch had added +devlore:claim=deterministic to Call afterward. The claim is kept;
develop had nothing at that position, having been squashed before it existed.

Everything else auto-merged, including the two files most likely to break quietly:

  .github/workflows/ci.yaml   #682's arm64 build-and-test matrix alongside this
                              branch's go-version 1.27 and golangci-lint v2.13.1.
                              All three go-version sites read 1.27, including the
                              matrix jobs #682 added.

  Makefile                    #682's build-system work alongside this branch's
                              GOLANGCI_LINT_VERSION pin, the golangci-lint-pinned
                              prerequisite, and lint routing through star.

Verified rather than assumed: make check exits 0 -- 104 packages, no lint issues --
and every claim still holds against host, darwin, linux, and windows build tags.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Build and test the arm64 targets: nothing on the push path compiles for arm64, and no arm64 Linux/Windows leg runs

1 participant