Skip to content

Commit 204f087

Browse files
committed
Works around missing Module.dir for vendored Modules
1 parent 92428a6 commit 204f087

File tree

1 file changed

+70
-34
lines changed

1 file changed

+70
-34
lines changed

licenses/library.go

Lines changed: 70 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"fmt"
2020
"go/build"
21+
"path"
2122
"path/filepath"
2223
"sort"
2324
"strings"
@@ -117,7 +118,33 @@ func Libraries(ctx context.Context, classifier Classifier, includeTests bool, ig
117118
klog.Errorf("Package %s does not have module info. Non go modules projects are no longer supported. For feedback, refer to https://github.com/google/go-licenses/issues/128.", p.PkgPath)
118119
return false
119120
}
120-
licensePath, err := Find(pkgDir, p.Module.Dir, classifier)
121+
122+
stopAt := p.Module.Dir
123+
if isVendorModule(newModule(p.Module), pkgDir) {
124+
splits := strings.SplitN(pkgDir, "/vendor/", 2)
125+
moduleSplits := strings.Split(p.Module.Path, "/")
126+
// There seems to be 3 common cases in terms of go module directory structure:
127+
// - "github" case follows github.com/<org>/<repo>
128+
// - "k8s.io" case follow k8s.io/<module>
129+
// - "go.starlark.net" (and probably lots others not exactly sure why/when this happens) case where go.starlark.net is the only folder in the structure
130+
// To account for the above, the stopAt is set to the the greatest depth up to 3 subfolders
131+
// Alternative: Since license files should only exists in the context of module folders within
132+
// the vendor directory, and not repo (github.com) folders or the vendor folder itself, we could
133+
// set stopAt to vendor mainly just to ensure traversal never goes above vendor, finding the main module license
134+
switch {
135+
case len(moduleSplits) > 2:
136+
stopAt = path.Join(splits[0], "vendor", moduleSplits[0], moduleSplits[1], moduleSplits[2])
137+
case len(moduleSplits) == 2:
138+
stopAt = path.Join(splits[0], "vendor", moduleSplits[0], moduleSplits[1])
139+
case len(moduleSplits) == 1:
140+
stopAt = path.Join(splits[0], "vendor", moduleSplits[0])
141+
default:
142+
klog.Errorf("module %s has an unexpected number of directories. Report to go-licenses developer if you see this.", p.Module.Path)
143+
return false
144+
}
145+
}
146+
147+
licensePath, err := Find(pkgDir, stopAt, classifier)
121148
if err != nil {
122149
klog.Errorf("Failed to find license for %s: %v", p.PkgPath, err)
123150
}
@@ -156,43 +183,25 @@ func Libraries(ctx context.Context, classifier Classifier, includeTests bool, ig
156183
lib.module = newModule(pkg.Module)
157184
}
158185
}
159-
if lib.module != nil && lib.module.Path != "" && lib.module.Dir == "" {
160-
// A known cause is that the module is vendored, so some information is lost.
186+
if isVendorModule(lib.module, lib.LicensePath) {
161187
splits := strings.SplitN(lib.LicensePath, "/vendor/", 2)
162-
if len(splits) != 2 {
163-
klog.Warningf("module %s does not have dir and it's not vendored, cannot discover the license URL. Report to go-licenses developer if you see this.", lib.module.Path)
164-
} else {
165-
// This is vendored. Handle this known special case.
166-
167-
// Extra note why we identify a vendored package like this.
168-
//
169-
// For a normal package:
170-
// * if it's not in a module, lib.module == nil
171-
// * if it's in a module, lib.module.Dir != ""
172-
// Only vendored modules will have lib.module != nil && lib.module.Path != "" && lib.module.Dir == "" as far as I know.
173-
// So the if condition above is already very strict for vendored packages.
174-
// On top of it, we checked the lib.LicensePath contains a vendor folder in it.
175-
// So it's rare to have a false positive for both conditions at the same time, although it may happen in theory.
176-
//
177-
// These assumptions may change in the future,
178-
// so we need to keep this updated with go tooling changes.
179-
parentModDir := splits[0]
180-
var parentPkg *packages.Package
181-
for _, rootPkg := range rootPkgs {
182-
if rootPkg.Module != nil && rootPkg.Module.Dir == parentModDir {
183-
parentPkg = rootPkg
184-
break
185-
}
186-
}
187-
if parentPkg == nil {
188-
klog.Warningf("cannot find parent package of vendored module %s", lib.module.Path)
189-
} else {
190-
// Vendored modules should be commited in the parent module, so it counts as part of the
191-
// parent module.
192-
lib.module = newModule(parentPkg.Module)
188+
parentModDir := splits[0]
189+
var parentPkg *packages.Package
190+
for _, rootPkg := range rootPkgs {
191+
if rootPkg.Module != nil && rootPkg.Module.Dir == parentModDir {
192+
parentPkg = rootPkg
193+
break
193194
}
194195
}
196+
if parentPkg == nil {
197+
klog.Warningf("cannot find parent package of vendored module %s", lib.module.Path)
198+
} else {
199+
// Vendored modules should be commited in the parent module, so it counts as part of the
200+
// parent module.
201+
lib.module = newModule(parentPkg.Module)
202+
}
195203
}
204+
196205
libraries = append(libraries, lib)
197206
}
198207
// Sort libraries to produce a stable result for snapshot diffing.
@@ -228,6 +237,33 @@ func commonAncestor(paths []string) string {
228237
return min
229238
}
230239

240+
func isVendorModule(module *Module, path string) bool {
241+
if module != nil && module.Path != "" && module.Dir == "" {
242+
// A known cause is that the module is vendored, so some information is lost.
243+
splits := strings.SplitN(path, "/vendor/", 2)
244+
if len(splits) != 2 {
245+
klog.Warningf("module %s does not have dir and it's not vendored, cannot discover the license URL. Report to go-licenses developer if you see this.", module.Path)
246+
} else {
247+
// This is vendored. Handle this known special case.
248+
249+
// Extra note why we identify a vendored package like this.
250+
//
251+
// For a normal package:
252+
// * if it's not in a module, lib.module == nil
253+
// * if it's in a module, lib.module.Dir != ""
254+
// Only vendored modules will have lib.module != nil && lib.module.Path != "" && lib.module.Dir == "" as far as I know.
255+
// So the if condition above is already very strict for vendored packages.
256+
// On top of it, we checked the lib.LicensePath contains a vendor folder in it.
257+
// So it's rare to have a false positive for both conditions at the same time, although it may happen in theory.
258+
//
259+
// These assumptions may change in the future,
260+
// so we need to keep this updated with go tooling changes.
261+
return true
262+
}
263+
}
264+
return false
265+
}
266+
231267
func (l *Library) String() string {
232268
return l.Name()
233269
}

0 commit comments

Comments
 (0)