tools/bump: drop dependencies that outrun the nixpkgs Go

tailscale.com@main raised the go directive to 1.27.1 while nixpkgs was
still on 1.27.0. go build downloads the newer toolchain and looks fine;
the nix builders set GOTOOLCHAIN=local and fail, so the whole bump was
being thrown away one area at a time.
This commit is contained in:
Kristoffer Dalby
2026-09-04 14:59:01 +00:00
parent 30d10640ea
commit 1449b95f1e
2 changed files with 43 additions and 19 deletions
+39
View File
@@ -9,6 +9,7 @@ import (
"strings"
"golang.org/x/mod/modfile"
"golang.org/x/mod/semver"
)
// Modules whose versions are not independent. Each pair moves as one unit or
@@ -335,6 +336,39 @@ func noteAttached(f *modfile.File, module, needle string) bool {
return false
}
var errToolchainAhead = errors.New("dependencies require a newer Go than the devShell provides")
// checkToolchain catches a dependency that dragged go.mod's go directive above
// the toolchain nixpkgs ships.
//
// The go command papers over this by downloading the newer toolchain, so
// `go build` succeeds and nothing looks wrong. The nix builders set
// GOTOOLCHAIN=local and fail outright, which is why this has to be an explicit
// check rather than something the build would surface on its own.
func checkToolchain(ctx context.Context, r *repo) error {
goMod, err := r.readFile("go.mod")
if err != nil {
return err
}
want, err := goDirective(goMod)
if err != nil {
return err
}
have, err := goVersion(ctx, r)
if err != nil {
return err
}
if semver.Compare("v"+want, "v"+have) > 0 {
return fmt.Errorf("%w: go.mod now requires go %s, the devShell provides %s",
errToolchainAhead, want, have)
}
return nil
}
// settle runs the steps every dependency change needs before it can be judged:
// tidy, restore the lockstep pins that the upgrade may have disturbed, tidy
// again, then assert go.mod's hand-written rules survived.
@@ -359,6 +393,11 @@ func settle(ctx context.Context, r *repo) error {
return err
}
err = checkToolchain(ctx, r)
if err != nil {
return err
}
return checkModComments(r)
}
+4 -19
View File
@@ -116,30 +116,15 @@ func verifyBuilders(ctx context.Context, r *repo) []finding {
return findings
}
// verifyToolchain reports, but never edits, a go.mod directive that has fallen
// behind the toolchain the build actually uses. Raising it is a promise to
// downstream packagers, so it stays a human decision.
// verifyToolchain reports, but never edits, a go directive that has outrun the
// toolchain nixpkgs ships. Raising it is a promise to downstream packagers, so
// the decision stays with a human; lowering it is not this tool's call either.
func verifyToolchain(ctx context.Context, r *repo) []finding {
nixGo, err := goVersion(ctx, r)
err := checkToolchain(ctx, r)
if err != nil {
return []finding{finding(err.Error())}
}
ourMod, err := r.readFile("go.mod")
if err != nil {
return []finding{finding(err.Error())}
}
ourGo, err := goDirective(ourMod)
if err != nil {
return []finding{finding(err.Error())}
}
if semver.Compare("v"+ourGo, "v"+nixGo) > 0 {
return []finding{finding(fmt.Sprintf(
"go.mod requires go %s but the devShell provides %s", ourGo, nixGo))}
}
return nil
}