diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9c1f974..f70ced3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -26,9 +26,16 @@ updates: # Go modules were not covered above, so Go advisories reached us only as a red # trivy-scan gate and had to be bumped by hand -- CVE-2026-84304 (grpc) in #203. - # Dependabot needs one entry per go.mod; there is no recursive directory match. + # + # `directories` is a shorthand for the two go.mod files, NOT a way to bundle them: + # Dependabot still opens one PR per directory. Bundling would need + # `group-by: dependency-name`, which is version-updates-only and splits one PR per + # dependency -- so it would leave security bumps, the reason this entry exists, + # unbundled. The root go.work is what keeps those per-directory PRs green; see #212. - package-ecosystem: gomod - directory: /gen/go + directories: + - /gen/go + - /remote-worker schedule: interval: weekly groups: @@ -39,14 +46,3 @@ updates: - 'google.golang.org/grpc*' - 'google.golang.org/protobuf*' - 'google.golang.org/genproto*' - - - package-ecosystem: gomod - directory: /remote-worker - schedule: - interval: weekly - groups: - grpc-protobuf: - patterns: - - 'google.golang.org/grpc*' - - 'google.golang.org/protobuf*' - - 'google.golang.org/genproto*' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77a7736..e0c0161 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -145,6 +145,19 @@ jobs: git diff --cached --exit-code -- packages/k8s-sandbox/src/gen gen \ || { echo "::error::Generated stubs are out of date or untracked. Run 'buf generate' and commit."; exit 1; } + # A module left out of go.work falls back to standalone resolution and brings + # back the stale-go.mod failure the workspace exists to prevent (#212). + - name: Verify go.work covers every module + run: | + covered="$(go work edit -json | jq -r '.Use[].DiskPath')" + rc=0 + while read -r mod; do + dir="./$(dirname "$mod")" + grep -qxF "$dir" <<<"$covered" \ + || { echo "::error file=$mod::$mod is not listed in go.work -- add \"$dir\" to the use block."; rc=1; } + done < <(git ls-files | grep -E '(^|/)go\.mod$') + exit $rc + - name: Build and test Go stubs run: cd gen/go && go build ./... && go test ./... @@ -154,3 +167,15 @@ jobs: test -z "$(gofmt -l .)" || { echo "::error::gofmt needs running:"; gofmt -l .; exit 1; } go vet ./... go test -race ./... + + # Advisory only. In workspace mode a stale go.mod no longer breaks the build, so + # this is hygiene rather than correctness -- and failing here would put Dependabot's + # coupled bumps back in the red, which is the whole point of #212. Neither module is + # consumed externally (gen/go is reached only through the local replace), so drift + # can be tidied whenever convenient. + - name: Check go.mod tidiness (advisory) + run: | + for mod in gen/go remote-worker; do + (cd "$mod" && go mod tidy -diff) \ + || echo "::warning file=$mod/go.mod::$mod/go.mod is not tidy -- run 'cd $mod && go mod tidy'" + done diff --git a/.gitignore b/.gitignore index fbfa053..114c5b8 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,7 @@ docs/plans/* # E2 writes each run's measured table here; experiments/RESULTS.md is the committed # baseline and is no longer overwritten by a test run (see experiments/README.md). experiments/.results/ + +# Workspace-only checksums. Redundant with each module go.sum, and the checksum +# database still verifies downloads without it -- one less sum file to keep in sync. +go.work.sum diff --git a/go.work b/go.work new file mode 100644 index 0000000..b662f44 --- /dev/null +++ b/go.work @@ -0,0 +1,15 @@ +// Both Go modules are coupled: remote-worker consumes gen/go through a local +// `replace`, and the `proto` CI job builds both. Without a workspace, any bump to a +// gen/go dependency leaves remote-worker/go.mod stale and the build fails with +// "updates to go.mod needed" -- which Dependabot cannot fix, because the tidy has to +// land in the same commit as the bump and its branches are not maintainer-writable. +// In workspace mode the go command resolves one MVS across both modules, so the +// modules can be bumped independently. See issue #212. +// +// Keep this `go` line in sync with both go.mod files (and CI's go-version-file). +go 1.25.0 + +use ( + ./gen/go + ./remote-worker +) diff --git a/remote-worker/Dockerfile b/remote-worker/Dockerfile index b2ff5f0..5d822a5 100644 --- a/remote-worker/Dockerfile +++ b/remote-worker/Dockerfile @@ -8,6 +8,9 @@ # RELAY_ADDR=sandbox-relay.default.svc:8443 (no port-forward needed in-cluster). FROM golang:1.25-alpine AS build WORKDIR /src +# go.work first: it is what lets the two modules be bumped independently (see #212). +# Without it the build here resolves each go.mod on its own and fails on a stale one. +COPY go.work ./ COPY gen/go/ ./gen/go/ COPY remote-worker/ ./remote-worker/ WORKDIR /src/remote-worker