From 45d6bc2ec36ac8080d0fb7d6d548b6b6dbe9a247 Mon Sep 17 00:00:00 2001 From: Enrico Candino Date: Wed, 19 Aug 2026 16:30:04 +0200 Subject: [PATCH] Add gomodcheck script to compare `go.mod` dependencies (#1161) * Add gomodcheck script to compare go.mod dependencies * fix: update go-logr and k8s.io/utils dependencies to latest versions --- Makefile | 7 ++- pkg/apis/go.mod | 4 +- pkg/apis/go.sum | 8 ++-- scripts/gomodcheck.go | 102 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 scripts/gomodcheck.go diff --git a/Makefile b/Makefile index c41133b6..265953fd 100644 --- a/Makefile +++ b/Makefile @@ -143,12 +143,17 @@ endif lint-yaml: ## Lint the GitHub Actions YAML files $(YAMLLINT) .github/ +.PHONY: check-gomod +check-gomod: ## Check that the root and pkg/apis go.mod agree on common dependency versions + go run scripts/gomodcheck.go go.mod pkg/apis/go.mod + .PHONY: validate validate: generate docs fmt ## Validate the project checking for any dependency or doc mismatch $(GINKGO) unfocus go mod tidy go mod verify - git status --porcelain + $(MAKE) check-gomod + git status --porcelain git --no-pager diff --exit-code .PHONY: install diff --git a/pkg/apis/go.mod b/pkg/apis/go.mod index e44ead2a..89645fe7 100644 --- a/pkg/apis/go.mod +++ b/pkg/apis/go.mod @@ -9,7 +9,7 @@ require ( require ( github.com/fxamacker/cbor/v2 v2.9.0 // indirect - github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/logr v1.4.4 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kr/text v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -21,7 +21,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect k8s.io/klog/v2 v2.140.0 // indirect k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect - k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect + k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.3 // indirect diff --git a/pkg/apis/go.sum b/pkg/apis/go.sum index 5f3a98aa..621fbc09 100644 --- a/pkg/apis/go.sum +++ b/pkg/apis/go.sum @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -56,8 +56,8 @@ k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg= k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0= -k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= -k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= +k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 h1:kBawHLSnx/mYHmRnNUf9d4CpjREbeZuxoSGOX/J+aYM= +k8s.io/utils v0.0.0-20260319190234-28399d86e0b5/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= diff --git a/scripts/gomodcheck.go b/scripts/gomodcheck.go new file mode 100644 index 00000000..13cdfd66 --- /dev/null +++ b/scripts/gomodcheck.go @@ -0,0 +1,102 @@ +//go:build ignore + +// gomodcheck compares the require sets of two go.mod files and reports every +// module required by both at a different version. +// +// Usage: +// +// go run scripts/gomodcheck.go go.mod pkg/apis/go.mod +// +// Exit codes: 0 in sync, 1 version mismatch, 2 usage/read/parse error. +package main + +import ( + "fmt" + "os" + "sort" + + "golang.org/x/mod/modfile" +) + +func main() { + if len(os.Args) != 3 { + fmt.Fprintf(os.Stderr, "usage: go run scripts/gomodcheck.go \n") + os.Exit(2) + } + + pathA, pathB := os.Args[1], os.Args[2] + + reqA, err := requires(pathA) + if err != nil { + fmt.Fprintf(os.Stderr, "gomodcheck: %v\n", err) + os.Exit(2) + } + + reqB, err := requires(pathB) + if err != nil { + fmt.Fprintf(os.Stderr, "gomodcheck: %v\n", err) + os.Exit(2) + } + + var common, mismatched []string + + for mod, verA := range reqA { + verB, ok := reqB[mod] + if !ok { + continue + } + + common = append(common, mod) + + if verA != verB { + mismatched = append(mismatched, mod) + } + } + + sort.Strings(mismatched) + + if len(mismatched) == 0 { + fmt.Printf("all %d common modules match\n", len(common)) + return + } + + pad := max(len(pathA), len(pathB)) + + for _, mod := range mismatched { + fmt.Printf("MISMATCH %s\n", mod) + fmt.Printf(" %-*s %s\n", pad, pathA, reqA[mod]) + fmt.Printf(" %-*s %s\n\n", pad, pathB, reqB[mod]) + } + + fmt.Printf("%s across %d common modules\n", plural(len(mismatched), "mismatch", "mismatches"), len(common)) + os.Exit(1) +} + +// requires parses the go.mod file at path and returns its require directives, +// both direct and indirect, as a module path to version map. +func requires(path string) (map[string]string, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + file, err := modfile.Parse(path, data, nil) + if err != nil { + return nil, err + } + + reqs := make(map[string]string, len(file.Require)) + for _, req := range file.Require { + reqs[req.Mod.Path] = req.Mod.Version + } + + return reqs, nil +} + +func plural(n int, singular, plural string) string { + if n == 1 { + return fmt.Sprintf("%d %s", n, singular) + } + + return fmt.Sprintf("%d %s", n, plural) +}