Skip to content

Commit 75371f3

Browse files
🌱 improve gomodcheck
Signed-off-by: Alexandre Mahdhaoui <[email protected]>
1 parent f7d8d75 commit 75371f3

File tree

4 files changed

+145
-56
lines changed

4 files changed

+145
-56
lines changed

.gomodcheck.ignore

Lines changed: 0 additions & 9 deletions
This file was deleted.

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,9 @@ GOLANGCI_LINT_PKG := github.com/golangci/golangci-lint/cmd/golangci-lint
9292
$(GOLANGCI_LINT): # Build golangci-lint from tools folder.
9393
GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(GOLANGCI_LINT_PKG) $(GOLANGCI_LINT_BIN) $(GOLANGCI_LINT_VER)
9494

95-
GO_MOD_CHECK_DIR := $(abspath ./tools/cmd/gomodcheck)
95+
GO_MOD_CHECK_DIR := $(abspath ./hack/tools/cmd/gomodcheck)
9696
GO_MOD_CHECK := $(abspath $(TOOLS_BIN_DIR)/gomodcheck)
97-
GO_MOD_CHECK_IGNORE := $(abspath ./.gomodcheck.ignore)
97+
GO_MOD_CHECK_IGNORE := $(abspath ./hack/.gomodcheck.yaml)
9898
$(GO_MOD_CHECK): # Build gomodcheck
9999
go build -C $(GO_MOD_CHECK_DIR) -o $(GO_MOD_CHECK)
100100

@@ -145,7 +145,7 @@ verify-modules: modules $(GO_MOD_CHECK) ## Verify go modules are up to date
145145
git diff; \
146146
echo "go module files are out of date, please run 'make modules'"; exit 1; \
147147
fi
148-
cat $(GO_MOD_CHECK_IGNORE) | xargs $(GO_MOD_CHECK)
148+
$(GO_MOD_CHECK) $(GO_MOD_CHECK_IGNORE)
149149

150150
APIDIFF_OLD_COMMIT ?= $(shell git rev-parse origin/main)
151151

hack/.gomodcheck.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
upstreamRefs:
2+
- k8s.io/api
3+
- k8s.io/apiextensions-apiserver
4+
- k8s.io/apimachinery
5+
- k8s.io/apiserver
6+
- k8s.io/client-go
7+
- k8s.io/component-base
8+
- k8s.io/klog/v2
9+
- k8s.io/utils
10+
11+
excludedModules:
12+
# --- test dependencies:
13+
- github.com/onsi/ginkgo/v2
14+
- github.com/onsi/gomega

hack/tools/cmd/gomodcheck/main.go

Lines changed: 128 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,70 +3,106 @@ package main
33
import (
44
"encoding/json"
55
"fmt"
6-
"io"
7-
"net/http"
86
"os"
7+
"os/exec"
98
"regexp"
109
"strings"
1110

12-
"sigs.k8s.io/controller-runtime/pkg/log/zap"
11+
"go.uber.org/zap"
12+
"sigs.k8s.io/yaml"
1313
)
1414

1515
const (
16-
kkModUrl = "https://raw.githubusercontent.com/kubernetes/kubernetes/master/go.mod"
17-
modFile = "./go.mod"
16+
modFile = "./go.mod"
1817
)
1918

20-
var (
21-
cleanMods = regexp.MustCompile(`\t| *//.*`)
22-
modDelimStart = regexp.MustCompile(`^require.*`)
23-
modDelimEnd = ")"
24-
)
19+
type config struct {
20+
UpstreamRefs []string `yaml:"upstreamRefs"`
21+
ExcludedModules []string `yaml:"excludedModules"`
22+
}
23+
24+
type upstream struct {
25+
Ref string `json:"ref"`
26+
Version string `json:"version"`
27+
}
2528

29+
// representation of an out of sync module
2630
type oosMod struct {
27-
Name string `json:"name"`
28-
Version string `json:"version"`
29-
UpstreamVersion string `json:"upstreamVersion"`
31+
Name string `json:"name"`
32+
Version string `json:"version"`
33+
Upstreams []upstream `json:"upstreams"`
3034
}
3135

3236
func main() {
33-
logger := zap.New()
34-
excludedMods := getExcludedMods()
37+
l, _ := zap.NewProduction()
38+
logger := l.Sugar()
3539

36-
// 1. kk
37-
resp, err := http.Get(kkModUrl)
38-
if err != nil {
39-
panic(err.Error())
40+
if len(os.Args) < 2 {
41+
fmt.Printf("USAGE: %s [PATH_TO_CONFIG_FILE]\n", os.Args[0])
42+
os.Exit(1)
4043
}
4144

42-
b, err := io.ReadAll(resp.Body)
45+
// --- 1. parse config
46+
b, err := os.ReadFile(os.Args[1])
4347
if err != nil {
44-
panic(err.Error())
48+
logger.Fatal(err.Error())
49+
}
50+
51+
cfg := new(config)
52+
if err := yaml.Unmarshal(b, cfg); err != nil {
53+
logger.Fatal(err.Error())
54+
}
55+
56+
excludedMods := make(map[string]any)
57+
for _, mod := range cfg.ExcludedModules {
58+
excludedMods[mod] = nil
4559
}
4660

47-
kkMods := parseMod(b)
61+
// --- 2. project mods
62+
deps, err := parseModFile()
63+
if err != nil {
64+
logger.Fatal(err.Error())
65+
}
4866

49-
// 2. go.mod
50-
b, err = os.ReadFile(modFile)
67+
// --- 3. upstream mods (holding upstream refs)
68+
upstreamModGraph, err := getUpstreamModGraph(cfg.UpstreamRefs)
5169
if err != nil {
52-
return
70+
logger.Fatal(err.Error())
5371
}
5472

5573
oosMods := make([]oosMod, 0)
5674

57-
mods := parseMod(b)
58-
for k, v := range mods {
59-
if _, ok := excludedMods[k]; ok {
60-
logger.Info(fmt.Sprintf("skipped module: %s", k))
75+
// --- 4. validate
76+
// for each module in our project,
77+
// if it matches an upstream module,
78+
// then for each upstream module,
79+
// if project module version doesn't match upstream version,
80+
// then we add the version and the ref to the list of out of sync modules.
81+
for mod, version := range deps {
82+
if _, ok := excludedMods[mod]; ok {
83+
logger.Infof("skipped excluded module: %s", mod)
6184
continue
6285
}
6386

64-
if upstreamVersion, ok := kkMods[k]; ok && v != upstreamVersion {
65-
oosMods = append(oosMods, oosMod{
66-
Name: k,
67-
Version: v,
68-
UpstreamVersion: upstreamVersion,
69-
})
87+
if versionToRef, ok := upstreamModGraph[mod]; ok {
88+
upstreams := make([]upstream, 0)
89+
90+
for upstreamVersion, upstreamRef := range versionToRef {
91+
if version != upstreamVersion {
92+
upstreams = append(upstreams, upstream{
93+
Ref: upstreamRef,
94+
Version: upstreamVersion,
95+
})
96+
}
97+
}
98+
99+
if len(upstreams) > 0 {
100+
oosMods = append(oosMods, oosMod{
101+
Name: mod,
102+
Version: version,
103+
Upstreams: upstreams,
104+
})
105+
}
70106
}
71107
}
72108

@@ -84,7 +120,18 @@ func main() {
84120
os.Exit(1)
85121
}
86122

87-
func parseMod(b []byte) map[string]string {
123+
var (
124+
cleanMods = regexp.MustCompile(`\t| *//.*`)
125+
modDelimStart = regexp.MustCompile(`^require.*`)
126+
modDelimEnd = ")"
127+
)
128+
129+
func parseModFile() (map[string]string, error) {
130+
b, err := os.ReadFile(modFile)
131+
if err != nil {
132+
return nil, err
133+
}
134+
88135
in := string(cleanMods.ReplaceAll(b, []byte("")))
89136
out := make(map[string]string)
90137

@@ -94,26 +141,63 @@ func parseMod(b []byte) map[string]string {
94141
case modDelimStart.MatchString(s) && !start:
95142
start = true
96143
case s == modDelimEnd:
97-
return out
144+
return out, nil
98145
case start:
99146
kv := strings.SplitN(s, " ", 2)
100147
if len(kv) < 2 {
101-
panic(fmt.Sprintf("unexpected format for module: %q", s))
148+
return nil, fmt.Errorf("unexpected format for module: %q", s)
102149
}
103150

104151
out[kv[0]] = kv[1]
105152
}
106153
}
107154

108-
return out
155+
return out, nil
109156
}
110157

111-
func getExcludedMods() map[string]any {
112-
out := make(map[string]any)
158+
func getUpstreamModGraph(upstreamRefs []string) (map[string]map[string]string, error) {
159+
b, err := exec.Command("go", "mod", "graph").Output()
160+
if err != nil {
161+
return nil, err
162+
}
163+
164+
graph := string(b)
165+
o1Refs := make(map[string]bool)
166+
for _, upstreamRef := range upstreamRefs {
167+
o1Refs[upstreamRef] = false
168+
}
169+
170+
modToVersionToUpstreamRef := make(map[string]map[string]string)
171+
172+
for _, line := range strings.Split(graph, "\n") {
173+
upstreamRef := strings.SplitN(line, "@", 2)[0]
174+
if _, ok := o1Refs[upstreamRef]; ok {
175+
o1Refs[upstreamRef] = true
176+
kv := strings.SplitN(strings.SplitN(line, " ", 2)[1], "@", 2)
177+
name := kv[0]
178+
version := kv[1]
179+
180+
if m, ok := modToVersionToUpstreamRef[kv[0]]; ok {
181+
m[version] = upstreamRef
182+
} else {
183+
versionToRef := map[string]string{version: upstreamRef}
184+
modToVersionToUpstreamRef[name] = versionToRef
185+
}
186+
}
187+
}
188+
189+
notFound := ""
190+
for ref, found := range o1Refs {
191+
if !found {
192+
notFound = fmt.Sprintf("%s%s, ", notFound, ref)
193+
}
194+
}
113195

114-
for _, mod := range os.Args[1:] {
115-
out[mod] = nil
196+
if notFound != "" {
197+
return nil, fmt.Errorf("cannot verify modules;"+
198+
"the following specified upstream module cannot be found in go.mod: [ %s ]",
199+
strings.TrimSuffix(notFound, ", "))
116200
}
117201

118-
return out
202+
return modToVersionToUpstreamRef, nil
119203
}

0 commit comments

Comments
 (0)