BUILD.md — SunSail Build & Harness Contract¶
Living build documentation. Grows with each sprint. Tier-1 authority remains
docs/ARCHITECTURE.md; this file records the how of building and verifying.
Toolchain¶
- Language floor:
go 1.26(go.modgodirective). - Pinned build version:
toolchain go1.26.5. WithGOTOOLCHAIN=auto(the default), the firstgocommand in this module auto-downloadsgo1.26.5if the host toolchain is older. The pin is bumped in later sprints on each Go release; the floor is not. - Zero CGo: every build sets
CGO_ENABLED=0. This is what makes the binaries statically linked (no libc) and freely cross-compilable.
staticcheck gotcha¶
staticcheck refuses to analyze a module whose required Go version is newer than
the Go release staticcheck itself was built with. A staticcheck built with an
older Go (e.g. 1.25.x) fails on this go 1.26 module with:
module requires at least go1.26, but Staticcheck was built with go1.25.3
Fix — build staticcheck with this module's toolchain:
make install-tools # go install honnef.co/go/tools/cmd/staticcheck@latest
Run from the repo root so the pinned toolchain go1.26.5 is used.
Make targets¶
| Target | What it does |
|---|---|
make build |
Host-platform binary → out/sun. CGO_ENABLED=0 -trimpath -ldflags="-s -w -X main.version". |
make check |
Pre-commit gate: go build ./... → go vet ./... → staticcheck ./... → go test ./.... |
make e2e |
go test -tags e2e ./test/e2e/... — builds the real binary and drives full flows in a tmpdir. |
make differential |
go test -tags differential ./... — real-network Nile validation (placeholder until S6). |
make proto |
Regenerates internal/rpc/pb/ from the vendored protos. Requires protoc + protoc-gen-go + protoc-gen-go-grpc on PATH (see proto/README.md for pinned versions). Only needed when proto/ changes — the generated *.pb.go are committed. |
make release |
Cross-compiles the 6-binary static matrix → release/ + SHA256SUMS. |
make clean |
Removes out/ and release/. |
make install-tools |
Installs staticcheck built with the module toolchain. |
Build tags¶
e2e— the end-to-end harness (test/e2e/harness_test.go). Excluded from the hermeticgo test ./...somake checkstays fast; an untaggedtest/e2e/doc.gokeeps the package buildable undergo build ./....differential— real-network tests against Nile. Never part ofgo test ./...(hermeticity invariant, CLAUDE.md §3.12).
Static-binary guarantees & verification¶
make release produces linux|darwin|windows × amd64|arm64 (6 binaries). Guarantees:
- linux/amd64 < 18 MB (currently ~15.9 MB — ARCHITECTURE Erratum E5: the ~2.4 MB of the S0 skeleton predated the gRPC + protobuf + vendored-proto stack that lands with the network surface).
- No libc linkage — statically linked, a consequence of
CGO_ENABLED=0.
Verifying "not a dynamic executable"¶
macOS has no ldd and cannot run a Linux binary, so use one of:
-
Always available (host):
file release/sun-linux-amd64 # => "... statically linked ..."
-
Literal
ldd(Linux container, Docker required):docker run --rm --platform linux/amd64 -v "$PWD/release":/r \ debian:stable-slim ldd /r/sun-linux-amd64 # => "not a dynamic executable" (ldd exits non-zero for static binaries — expected)
On a native Linux CI host, ldd release/sun-linux-amd64 is used directly.
Version stamping¶
The binary version is injected at link time:
-ldflags "-X main.version=<v>"
VERSION defaults to git describe --tags --always --dirty, falling back to dev
before any tags/git exist. sun --version prints sun version <v>.
SAIL_HOME contract (ARCHITECTURE §2)¶
The data directory for managed binaries and caches resolves, cross-platform, as:
$SAIL_HOMEif set (non-empty); otherwise<os.UserHomeDir()>/.sail.
A literal ~ is never used. Managed binaries live at
<SAIL_HOME>/bin/<name>-<version>[.exe] (.exe only on Windows). Implemented in
internal/config/paths.go (Home, ExeSuffix, ManagedBinPath).
Continuous Integration¶
Three workflows live under .github/workflows/:
ci.yml— onpushandpull_requesttomain:make check→make e2e, fully hermetic (no network, no secrets).differential.yml— nightly (03:00 UTC):make differentialagainst Nile plusmake fuzz, in parallel jobs.release.yml— onv*tags:make release(the 6-binary matrix +SHA256SUMS), attached to the GitHub Release.
The same make targets documented above run in CI. Full reference —
triggers, permissions, the nightly secrets, and platform coverage — in
docs/CI.md.