@@ -5,10 +5,10 @@ import (
55 "fmt"
66 "os"
77 "os/exec"
8- "regexp"
98 "strings"
109
1110 "go.uber.org/zap"
11+ "golang.org/x/mod/modfile"
1212 "sigs.k8s.io/yaml"
1313)
1414
@@ -17,8 +17,8 @@ const (
1717)
1818
1919type config struct {
20- UpstreamRefs []string `yaml :"upstreamRefs"`
21- ExcludedModules []string `yaml :"excludedModules"`
20+ UpstreamRefs []string `json :"upstreamRefs"`
21+ ExcludedModules []string `json :"excludedModules"`
2222}
2323
2424type upstream struct {
@@ -45,12 +45,12 @@ func main() {
4545 // --- 1. parse config
4646 b , err := os .ReadFile (os .Args [1 ])
4747 if err != nil {
48- logger . Fatal (err . Error () )
48+ fatal (err )
4949 }
5050
5151 cfg := new (config )
5252 if err := yaml .Unmarshal (b , cfg ); err != nil {
53- logger . Fatal (err . Error () )
53+ fatal (err )
5454 }
5555
5656 excludedMods := make (map [string ]any )
@@ -59,15 +59,15 @@ func main() {
5959 }
6060
6161 // --- 2. project mods
62- deps , err := parseModFile ()
62+ projectModules , err := modulesFromGoModFile ()
6363 if err != nil {
64- logger . Fatal (err . Error () )
64+ fatal (err )
6565 }
6666
67- // --- 3. upstream mods (holding upstream refs)
68- upstreamModGraph , err := getUpstreamModGraph (cfg .UpstreamRefs )
67+ // --- 3. upstream mods
68+ upstreamModules , err := modulesFromUpstreamModGraph (cfg .UpstreamRefs )
6969 if err != nil {
70- logger . Fatal (err . Error () )
70+ fatal (err )
7171 }
7272
7373 oosMods := make ([]oosMod , 0 )
@@ -78,13 +78,13 @@ func main() {
7878 // then for each upstream module,
7979 // if project module version doesn't match upstream version,
8080 // 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 {
8282 if _ , ok := excludedMods [mod ]; ok {
8383 logger .Infof ("skipped excluded module: %s" , mod )
8484 continue
8585 }
8686
87- if versionToRef , ok := upstreamModGraph [mod ]; ok {
87+ if versionToRef , ok := upstreamModules [mod ]; ok {
8888 upstreams := make ([]upstream , 0 )
8989
9090 for upstreamVersion , upstreamRef := range versionToRef {
@@ -107,97 +107,94 @@ func main() {
107107 }
108108
109109 if len (oosMods ) == 0 {
110- fmt .Println ("Success! 🎉 " )
110+ fmt .Println ("🎉 Success!" )
111111 os .Exit (0 )
112112 }
113113
114114 b , err = json .MarshalIndent (map [string ]any {"outOfSyncModules" : oosMods }, "" , " " )
115115 if err != nil {
116- panic (err )
116+ fatal (err )
117117 }
118118
119119 fmt .Println (string (b ))
120120 os .Exit (1 )
121121}
122122
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 ) {
130124 b , err := os .ReadFile (modFile )
131125 if err != nil {
132126 return nil , err
133127 }
134128
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+ }
150133
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
153137 }
154138
155139 return out , nil
156140}
157141
158- func getUpstreamModGraph ( upstreamRefs []string ) (map [string ]map [string ]string , error ) {
142+ func modulesFromUpstreamModGraph ( upstreamRefList []string ) (map [string ]map [string ]string , error ) {
159143 b , err := exec .Command ("go" , "mod" , "graph" ).Output ()
160144 if err != nil {
161145 return nil , err
162146 }
163147
164148 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
168158 }
169159
170160 modToVersionToUpstreamRef := make (map [string ]map [string ]string )
171-
172161 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
186166 }
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
187179 }
188180
189- notFound := ""
190- for ref , found := range o1Refs {
181+ notFoundErr := ""
182+ for ref , found := range upstreamRefs {
191183 if ! found {
192- notFound = fmt .Sprintf ("%s%s, " , notFound , ref )
184+ notFoundErr = fmt .Sprintf ("%s%s, " , notFoundErr , ref )
193185 }
194186 }
195187
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 , ", " ))
200192 }
201193
202194 return modToVersionToUpstreamRef , nil
203195}
196+
197+ func fatal (err error ) {
198+ fmt .Printf ("❌ %s\n " , err .Error ())
199+ os .Exit (1 )
200+ }
0 commit comments