@@ -17,6 +17,7 @@ package parser
1717import (
1818 "bufio"
1919 "bytes"
20+ "encoding/json"
2021 "fmt"
2122 "go/ast"
2223 "go/parser"
@@ -94,12 +95,11 @@ func newGoParser(name string, homePageDir string, opts Options) *GoParser {
9495}
9596
9697func (p * GoParser ) collectGoMods (startDir string ) error {
97-
9898 err := filepath .Walk (startDir , func (path string , info fs.FileInfo , err error ) error {
9999 if err != nil || ! strings .HasSuffix (path , "go.mod" ) {
100100 return nil
101101 }
102- name , content , err := getModuleName (path )
102+ name , _ , err := getModuleName (path )
103103 if err != nil {
104104 return err
105105 }
@@ -109,7 +109,8 @@ func (p *GoParser) collectGoMods(startDir string) error {
109109 }
110110 p .repo .Modules [name ] = newModule (name , rel )
111111 p .modules = append (p .modules , newModuleInfo (name , rel , name ))
112- deps , err := parseModuleFile (content )
112+
113+ deps , err := getDeps (filepath .Dir (path ))
113114 if err != nil {
114115 return err
115116 }
@@ -126,6 +127,75 @@ func (p *GoParser) collectGoMods(startDir string) error {
126127 return nil
127128}
128129
130+ type replace struct {
131+ Path string `json:"Path"`
132+ Version string `json:"Version"`
133+ Dir string `json:"Dir"`
134+ GoMod string `json:"GoMod"`
135+ }
136+
137+ type dep struct {
138+ Module struct {
139+ Path string `json:"Path"`
140+ Version string `json:"Version"`
141+ Replace * replace `json:"Replace,omitempty"`
142+ Indirect bool `json:"Indirect"`
143+ Dir string `json:"Dir"`
144+ GoMod string `json:"GoMod"`
145+ } `json:"Module"`
146+ }
147+
148+ func getDeps (dir string ) (map [string ]string , error ) {
149+ // run go mod tidy first to ensure all dependencies are resolved
150+ cmd := exec .Command ("go" , "mod" , "tidy" )
151+ cmd .Dir = dir
152+ output , err := cmd .CombinedOutput ()
153+ if err != nil {
154+ return nil , fmt .Errorf ("failed to execute 'go mod tidy', err: %v, output: %s" , err , string (output ))
155+ }
156+
157+ cmd = exec .Command ("go" , "list" , "-json" , "all" )
158+ cmd .Dir = dir
159+ output , err = cmd .CombinedOutput ()
160+ if err != nil {
161+ return nil , fmt .Errorf ("failed to execute 'go list -json all', err: %v, output: %s" , err , string (output ))
162+ }
163+
164+ deps := make (map [string ]string )
165+ decoder := json .NewDecoder (bytes .NewReader (output ))
166+ for {
167+ var mod dep
168+ if err := decoder .Decode (& mod ); err != nil {
169+ if err .Error () == "EOF" {
170+ break
171+ }
172+ return nil , fmt .Errorf ("failed to decode json: %v" , err )
173+ }
174+ module := mod .Module
175+ // golang internal package, ignore it.
176+ if module .Path == "" {
177+ continue
178+ }
179+ if module .Replace != nil {
180+ deps [module .Path ] = module .Replace .Path + "@" + module .Replace .Version
181+ } else {
182+ if module .Version != "" {
183+ deps [module .Path ] = module .Path + "@" + module .Version
184+ } else {
185+ // If no version, it's a local package. So we use local commit as version
186+ commit , err := getCommitHash (dir )
187+ if err != nil {
188+ deps [module .Path ] = module .Path
189+ } else {
190+ deps [module .Path ] = module .Path + "@" + commit
191+ }
192+ }
193+ }
194+ }
195+
196+ return deps , nil
197+ }
198+
129199// ParseRepo parse the entiry repo from homePageDir recursively until end
130200func (p * GoParser ) ParseRepo () (Repository , error ) {
131201 for _ , lib := range p .modules {
0 commit comments