@@ -5,10 +5,10 @@ import (
5
5
"fmt"
6
6
"os"
7
7
"os/exec"
8
- "regexp"
9
8
"strings"
10
9
11
10
"go.uber.org/zap"
11
+ "golang.org/x/mod/modfile"
12
12
"sigs.k8s.io/yaml"
13
13
)
14
14
@@ -17,8 +17,8 @@ const (
17
17
)
18
18
19
19
type config struct {
20
- UpstreamRefs []string `yaml :"upstreamRefs"`
21
- ExcludedModules []string `yaml :"excludedModules"`
20
+ UpstreamRefs []string `json :"upstreamRefs"`
21
+ ExcludedModules []string `json :"excludedModules"`
22
22
}
23
23
24
24
type upstream struct {
@@ -45,12 +45,12 @@ func main() {
45
45
// --- 1. parse config
46
46
b , err := os .ReadFile (os .Args [1 ])
47
47
if err != nil {
48
- logger . Fatal (err . Error () )
48
+ fatal (err )
49
49
}
50
50
51
51
cfg := new (config )
52
52
if err := yaml .Unmarshal (b , cfg ); err != nil {
53
- logger . Fatal (err . Error () )
53
+ fatal (err )
54
54
}
55
55
56
56
excludedMods := make (map [string ]any )
@@ -59,15 +59,15 @@ func main() {
59
59
}
60
60
61
61
// --- 2. project mods
62
- deps , err := parseModFile ()
62
+ projectModules , err := modulesFromGoModFile ()
63
63
if err != nil {
64
- logger . Fatal (err . Error () )
64
+ fatal (err )
65
65
}
66
66
67
- // --- 3. upstream mods (holding upstream refs)
68
- upstreamModGraph , err := getUpstreamModGraph (cfg .UpstreamRefs )
67
+ // --- 3. upstream mods
68
+ upstreamModules , err := modulesFromUpstreamModGraph (cfg .UpstreamRefs )
69
69
if err != nil {
70
- logger . Fatal (err . Error () )
70
+ fatal (err )
71
71
}
72
72
73
73
oosMods := make ([]oosMod , 0 )
@@ -78,13 +78,13 @@ func main() {
78
78
// then for each upstream module,
79
79
// if project module version doesn't match upstream version,
80
80
// then we add the version and the ref to the list of out of sync modules.
81
- for mod , version := range deps {
81
+ for mod , version := range projectModules {
82
82
if _ , ok := excludedMods [mod ]; ok {
83
83
logger .Infof ("skipped excluded module: %s" , mod )
84
84
continue
85
85
}
86
86
87
- if versionToRef , ok := upstreamModGraph [mod ]; ok {
87
+ if versionToRef , ok := upstreamModules [mod ]; ok {
88
88
upstreams := make ([]upstream , 0 )
89
89
90
90
for upstreamVersion , upstreamRef := range versionToRef {
@@ -107,97 +107,94 @@ func main() {
107
107
}
108
108
109
109
if len (oosMods ) == 0 {
110
- fmt .Println ("Success! 🎉 " )
110
+ fmt .Println ("🎉 Success!" )
111
111
os .Exit (0 )
112
112
}
113
113
114
114
b , err = json .MarshalIndent (map [string ]any {"outOfSyncModules" : oosMods }, "" , " " )
115
115
if err != nil {
116
- panic (err )
116
+ fatal (err )
117
117
}
118
118
119
119
fmt .Println (string (b ))
120
120
os .Exit (1 )
121
121
}
122
122
123
- var (
124
- cleanMods = regexp .MustCompile (`\t| *//.*` )
125
- modDelimStart = regexp .MustCompile (`^require.*` )
126
- modDelimEnd = ")"
127
- )
128
-
129
- func parseModFile () (map [string ]string , error ) {
123
+ func modulesFromGoModFile () (map [string ]string , error ) {
130
124
b , err := os .ReadFile (modFile )
131
125
if err != nil {
132
126
return nil , err
133
127
}
134
128
135
- in := string (cleanMods .ReplaceAll (b , []byte ("" )))
136
- out := make (map [string ]string )
137
-
138
- start := false
139
- for _ , s := range strings .Split (in , "\n " ) {
140
- switch {
141
- case modDelimStart .MatchString (s ) && ! start :
142
- start = true
143
- case s == modDelimEnd :
144
- return out , nil
145
- case start :
146
- kv := strings .SplitN (s , " " , 2 )
147
- if len (kv ) < 2 {
148
- return nil , fmt .Errorf ("unexpected format for module: %q" , s )
149
- }
129
+ f , err := modfile .Parse (modFile , b , nil )
130
+ if err != nil {
131
+ return nil , err
132
+ }
150
133
151
- out [kv [0 ]] = kv [1 ]
152
- }
134
+ out := make (map [string ]string )
135
+ for _ , mod := range f .Require {
136
+ out [mod .Mod .Path ] = mod .Mod .Version
153
137
}
154
138
155
139
return out , nil
156
140
}
157
141
158
- func getUpstreamModGraph ( upstreamRefs []string ) (map [string ]map [string ]string , error ) {
142
+ func modulesFromUpstreamModGraph ( upstreamRefList []string ) (map [string ]map [string ]string , error ) {
159
143
b , err := exec .Command ("go" , "mod" , "graph" ).Output ()
160
144
if err != nil {
161
145
return nil , err
162
146
}
163
147
164
148
graph := string (b )
165
- o1Refs := make (map [string ]bool )
166
- for _ , upstreamRef := range upstreamRefs {
167
- o1Refs [upstreamRef ] = false
149
+
150
+ // upstreamRefs is a set of user specified upstream modules.
151
+ // The set has 2 functions:
152
+ // 1. Check if `go mod graph` modules are one of the user specified upstream modules.
153
+ // 2. Mark if a user specified upstream module was found in the module graph.
154
+ // If a user specified upstream module is not found, gomodcheck will exit with an error.
155
+ upstreamRefs := make (map [string ]bool )
156
+ for _ , ref := range upstreamRefList {
157
+ upstreamRefs [ref ] = false
168
158
}
169
159
170
160
modToVersionToUpstreamRef := make (map [string ]map [string ]string )
171
-
172
161
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
- }
162
+ ref := strings .SplitN (line , "@" , 2 )[0 ]
163
+
164
+ if _ , ok := upstreamRefs [ref ]; ! ok {
165
+ continue
186
166
}
167
+
168
+ upstreamRefs [ref ] = true // mark the ref as found
169
+
170
+ kv := strings .SplitN (strings .SplitN (line , " " , 2 )[1 ], "@" , 2 )
171
+ name := kv [0 ]
172
+ version := kv [1 ]
173
+
174
+ if _ , ok := modToVersionToUpstreamRef [name ]; ! ok {
175
+ modToVersionToUpstreamRef [name ] = make (map [string ]string )
176
+ }
177
+
178
+ modToVersionToUpstreamRef [name ][version ] = ref
187
179
}
188
180
189
- notFound := ""
190
- for ref , found := range o1Refs {
181
+ notFoundErr := ""
182
+ for ref , found := range upstreamRefs {
191
183
if ! found {
192
- notFound = fmt .Sprintf ("%s%s, " , notFound , ref )
184
+ notFoundErr = fmt .Sprintf ("%s%s, " , notFoundErr , ref )
193
185
}
194
186
}
195
187
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 , ", " ))
188
+ if notFoundErr != "" {
189
+ return nil , fmt .Errorf ("cannot verify modules: " +
190
+ "the following specified upstream module(s) cannot be found in go.mod: [ %s ]" ,
191
+ strings .TrimSuffix (notFoundErr , ", " ))
200
192
}
201
193
202
194
return modToVersionToUpstreamRef , nil
203
195
}
196
+
197
+ func fatal (err error ) {
198
+ fmt .Printf ("❌ %s\n " , err .Error ())
199
+ os .Exit (1 )
200
+ }
0 commit comments