Skip to content

Commit 26a9963

Browse files
committed
build: unify the test+lint gate under nix flake check (closes #347, #348)
`just test-go`/`lint-go` ran go test/vet/golangci-lint in the devshell against the real environment. Two bugs this session (the discovery_test HOME/cwd env-leak and the SSE data-race/hang) were invisible there and only surfaced in the merge hook. Make `nix flake check` the single hermetic gate. New checks (mirroring igloo's buildGoRace, used by madder): - go-test-race: `go test -race ./...` on a lean base off `moxy` (doCheck on, plugin postInstall dropped, MOXIN_PATH unset, CGO+race via buildGoRace). The sandbox's fresh HOME makes env-dependent tests isolated by construction — it immediately caught the asyncjob clown stub's `/usr/bin/env bash` shebang (no /usr/bin/env in the pure sandbox), now resolved via exec.LookPath. Folds in #348 (-race lane). - go-vet: `go vet ./...` hermetically. - go-lint: golangci-lint run offline against the buildGoApplication module graph, --config pointed at the flake's .golangci.yml (the dotfile isn't in the filtered moxySrc). Hermetic golangci-lint-in-nix is novel across the eng fleet — proven feasible here. - bats: the comprehensive bats-default lane (net_cap/host_only need sandbox capabilities a flake check can't grant — they stay explicit recipes; per-tag lanes stay packages for `just test-bats-tag`). justfile: `default`/`test` route through `nix flake check`; the devshell test-go/test-bats/lint recipes stay as fast iteration loops, with the flake check as the source of truth. test-bats-net_cap + the runtime smokes (validate-mcp, status) run alongside. 🤡 Generated with [Clown 0.3.10+44f75d5](amarbel-llc/clown@44f75d5)
1 parent 85a0a99 commit 26a9963

3 files changed

Lines changed: 89 additions & 7 deletions

File tree

flake.nix

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,52 @@
969969
bats-host_only = mkBatsLane { filter = "host_only"; };
970970
};
971971

972+
# Hermetic Go test + vet checks (#347, folds #348). A lean base off
973+
# `moxy`: doCheck on, the plugin-gen postInstall dropped (irrelevant to
974+
# a test run), MOXIN_PATH unset to match `just test-go`. The build
975+
# sandbox gives each a fresh HOME/TMPDIR, so env-dependent tests (e.g.
976+
# discovery's MOXIN_PATH/HOME fallback) are isolated by construction —
977+
# the class that leaked into the merge hook. buildGoRace flips
978+
# CGO_ENABLED=1 + -race for the race deriv only, leaving the shipped
979+
# binary untouched.
980+
goCheckBase = moxy.overrideAttrs (_: {
981+
doCheck = true;
982+
postInstall = "";
983+
preCheck = "export MOXIN_PATH=";
984+
});
985+
goTestRace = pkgs.buildGoRace { base = goCheckBase; };
986+
goVet = goCheckBase.overrideAttrs (_: {
987+
pname = "moxy-govet";
988+
checkPhase = ''
989+
runHook preCheck
990+
go vet ./...
991+
runHook postCheck
992+
'';
993+
});
994+
# golangci-lint as a hermetic check. moxy's .golangci.yml uses only the
995+
# standard built-in analyzers (no external plugins), so it typechecks
996+
# offline against the buildGoApplication module graph. Caches go to
997+
# $TMPDIR (the sandbox HOME is read-only). golangci-lint from
998+
# pkgs-master matches the devshell `lint-go` binary (config schema v2).
999+
goLint = goCheckBase.overrideAttrs (old: {
1000+
pname = "moxy-golangci-lint";
1001+
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [
1002+
pkgs-master.golangci-lint
1003+
];
1004+
# --config points at the flake's copy of .golangci.yml: the dotfile
1005+
# is not in the filtered moxySrc, so without this golangci-lint runs
1006+
# with defaults (errcheck on, no exclusions) and flags ~50 idiomatic
1007+
# sites the repo config deliberately suppresses. Path-based
1008+
# exclusions still resolve against the analyzed paths (cwd-relative).
1009+
checkPhase = ''
1010+
runHook preCheck
1011+
export HOME="$TMPDIR"
1012+
export GOLANGCI_LINT_CACHE="$TMPDIR/golangci-lint-cache"
1013+
golangci-lint run --config ${./.golangci.yml} --timeout 10m ./...
1014+
runHook postCheck
1015+
'';
1016+
});
1017+
9721018
in
9731019
{
9741020
packages = batsLaneOutputs // {
@@ -983,7 +1029,21 @@
9831029
# `conformist check`, which also runs the dead-jq linter over
9841030
# zz-tests_bats/*.bats.
9851031
formatter = conformistFormatter;
986-
checks.conformist = conformistCheck;
1032+
# `nix flake check` is the single hermetic gate: conformist (fmt +
1033+
# dead-jq), the Go test (-race) / vet / golangci-lint checks, and the
1034+
# comprehensive `bats-default` lane (every test except the net_cap and
1035+
# host_only tags, which need sandbox capabilities a flake check can't
1036+
# grant — those stay as their own `nix build` recipes). The per-tag
1037+
# lanes remain in `packages` for focused `just test-bats-tag` runs;
1038+
# bats-default already covers them in aggregate, so re-listing each as
1039+
# a check would only double-build.
1040+
checks = {
1041+
conformist = conformistCheck;
1042+
go-test-race = goTestRace;
1043+
go-vet = goVet;
1044+
go-lint = goLint;
1045+
bats = batsLaneOutputs.bats-default;
1046+
};
9871047

9881048
devShells.default = pkgs-master.mkShell {
9891049
packages = [

internal/asyncjob/asyncjob_test.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"errors"
77
"os"
8+
"os/exec"
89
"path/filepath"
910
"regexp"
1011
"strings"
@@ -19,10 +20,19 @@ import (
1920
// signature: exit 0, no output).
2021
func writeClownStub(t *testing.T, startOutput string) (bin, record string) {
2122
t.Helper()
23+
// Resolve a real interpreter rather than hardcoding `/usr/bin/env bash`:
24+
// the hermetic nix-check sandbox has bash on PATH but no /usr/bin/env, so
25+
// an env-shebang stub silently fails to exec and `startJob` mints a local
26+
// id instead of running the stub. A direct shebang to the resolved bash
27+
// works in both the sandbox and the devshell.
28+
shell, err := exec.LookPath("bash")
29+
if err != nil {
30+
t.Skipf("no bash on PATH for clown stub: %v", err)
31+
}
2232
dir := t.TempDir()
2333
record = filepath.Join(dir, "record")
2434
bin = filepath.Join(dir, "clown")
25-
script := "#!/usr/bin/env bash\n" +
35+
script := "#!" + shell + "\n" +
2636
"printf '%s\\n' \"$*\" >> " + record + "\n" +
2737
"if [ \"$1\" = job ] && [ \"$2\" = start ]; then\n"
2838
if startOutput != "" {

justfile

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
export MOXIN_PATH := justfile_directory() / "result-moxins" / "share" / "moxy" / "moxins"
22

3-
default: lint build test test-status-clean-env
4-
5-
# Pre-build gate aggregate: treefmt formatting check + golangci-lint. A hard
6-
# CI gate via `default`.
3+
# The hermetic gate is `nix flake check` (via `test`): it runs conformist
4+
# (fmt + dead-jq), go-test-race, go-vet, go-lint, and every bats lane in the
5+
# build sandbox — so the formatting/lint/go-test/bats coverage `lint` and the
6+
# old devshell `test-*` recipes provided is now subsumed there and not
7+
# repeated here. `lint`, `test-go`, `test-bats`, etc. remain as fast
8+
# devshell loops for iteration; `nix flake check` is the source of truth.
9+
default: build test test-status-clean-env
10+
11+
# Fast devshell lint loop (treefmt check + golangci-lint). NOT the gate — the
12+
# hermetic equivalents (conformist + go-lint) run inside `nix flake check`.
713
[group("pre-build")]
814
lint: lint-fmt lint-go
915

@@ -107,8 +113,14 @@ lint-go:
107113

108114
dir_build := "build"
109115

116+
# The gate. `test-flake-check` (= `nix flake check`) runs the hermetic
117+
# go-test-race / go-vet / go-lint / conformist / bats-default checks, so the
118+
# devshell test-go/test-bats and lint recipes are NOT repeated here (they stay
119+
# as standalone fast loops). test-bats-net_cap stays explicit: the loopback
120+
# lane needs a sandbox capability a flake check can't grant. The runtime
121+
# smokes (validate-mcp, status) aren't expressible as flake checks either.
110122
[group("post-build")]
111-
test: test-go test-bats test-bats-net_cap test-validate-mcp test-status test-flake-check
123+
test: test-flake-check test-bats-net_cap test-validate-mcp test-status
112124

113125
# Run the bats integration suite inside the nix build sandbox via
114126
# `nix build .#bats-default`. The default lane filters

0 commit comments

Comments
 (0)