Skip to content

Commit a852173

Browse files
committed
Fix behaviour for single non-root go.mod
Also add telemetry so we can prioritise future work on the autobuilder.
1 parent e43fd72 commit a852173

File tree

12 files changed

+369
-42
lines changed

12 files changed

+369
-42
lines changed

go/extractor/cli/go-autobuilder/go-autobuilder.go

Lines changed: 117 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"path/filepath"
1111
"regexp"
1212
"runtime"
13+
"sort"
1314
"strings"
1415

1516
"golang.org/x/mod/semver"
@@ -240,28 +241,109 @@ func getSourceDir() string {
240241
return srcdir
241242
}
242243

244+
func getDirs(paths []string) []string {
245+
dirs := make([]string, len(paths))
246+
for i, path := range paths {
247+
dirs[i] = filepath.Dir(path)
248+
}
249+
return dirs
250+
}
251+
252+
// Note this has the side effect of sorting `dirs`
253+
func checkDirsNested(dirs []string) bool {
254+
// the paths were generated by a depth-first search so I think they might
255+
// be sorted, but we sort them just in case
256+
sort.Strings(dirs)
257+
for _, dir := range dirs {
258+
if !strings.HasPrefix(dir, dirs[0]) {
259+
return false
260+
}
261+
}
262+
return true
263+
}
264+
265+
func findGoModFiles(emitDiagnostics bool) (string, bool) {
266+
goModPaths := util.FindAllFilesWithName(".", "go.mod", "vendor")
267+
if len(goModPaths) == 0 {
268+
// preserve current behaviour
269+
return ".", false
270+
}
271+
goModDirs := getDirs(goModPaths)
272+
if util.AnyGoFilesOutsideDirs(".", goModDirs...) {
273+
if emitDiagnostics {
274+
diagnostics.EmitGoFilesOutsideGoModules(goModPaths)
275+
}
276+
// preserve current behaviour
277+
return ".", true
278+
}
279+
if len(goModPaths) > 1 {
280+
// currently not supported
281+
if emitDiagnostics {
282+
if checkDirsNested(goModDirs) {
283+
diagnostics.EmitMultipleGoModFoundNested(goModPaths)
284+
} else {
285+
diagnostics.EmitMultipleGoModFoundNotNested(goModPaths)
286+
}
287+
}
288+
// preserve current behaviour
289+
return ".", true
290+
}
291+
if emitDiagnostics {
292+
if goModDirs[0] == "." {
293+
diagnostics.EmitSingleRootGoModFound(goModPaths[0])
294+
} else {
295+
diagnostics.EmitSingleNonRootGoModFound(goModPaths[0])
296+
}
297+
}
298+
return goModDirs[0], true
299+
}
300+
243301
// Returns the appropriate DependencyInstallerMode for the current project
244-
func getDepMode() DependencyInstallerMode {
245-
if util.FileExists("go.mod") {
302+
func getDepMode(emitDiagnostics bool) (DependencyInstallerMode, string) {
303+
if util.FileExists("BUILD") {
304+
// currently not supported
305+
if emitDiagnostics {
306+
diagnostics.EmitBazelBuildFileFound()
307+
}
308+
}
309+
310+
goWorkPaths := util.FindAllFilesWithName(".", "go.work", "vendor")
311+
if len(goWorkPaths) > 0 {
312+
// currently not supported
313+
if emitDiagnostics {
314+
diagnostics.EmitGoWorkFound(goWorkPaths)
315+
}
316+
}
317+
318+
baseDir, goModFound := findGoModFiles(emitDiagnostics)
319+
if goModFound {
246320
log.Println("Found go.mod, enabling go modules")
247-
return GoGetWithModules
321+
return GoGetWithModules, baseDir
248322
}
323+
249324
if util.FileExists("Gopkg.toml") {
325+
if emitDiagnostics {
326+
diagnostics.EmitGopkgTomlFound()
327+
}
250328
log.Println("Found Gopkg.toml, using dep instead of go get")
251-
return Dep
329+
return Dep, "."
252330
}
331+
253332
if util.FileExists("glide.yaml") {
333+
if emitDiagnostics {
334+
diagnostics.EmitGlideYamlFound()
335+
}
254336
log.Println("Found glide.yaml, using Glide instead of go get")
255-
return Glide
337+
return Glide, "."
256338
}
257-
return GoGetNoModules
339+
return GoGetNoModules, "."
258340
}
259341

260342
// Tries to open `go.mod` and read a go directive, returning the version and whether it was found.
261-
func tryReadGoDirective(depMode DependencyInstallerMode) (string, bool) {
343+
func tryReadGoDirective(depMode DependencyInstallerMode, baseDir string) (string, bool) {
262344
if depMode == GoGetWithModules {
263345
versionRe := regexp.MustCompile(`(?m)^go[ \t\r]+([0-9]+\.[0-9]+)$`)
264-
goMod, err := os.ReadFile("go.mod")
346+
goMod, err := os.ReadFile(filepath.Join(baseDir, "go.mod"))
265347
if err != nil {
266348
log.Println("Failed to read go.mod to check for missing Go version")
267349
} else {
@@ -277,13 +359,13 @@ func tryReadGoDirective(depMode DependencyInstallerMode) (string, bool) {
277359
}
278360

279361
// Returns the appropriate ModMode for the current project
280-
func getModMode(depMode DependencyInstallerMode) ModMode {
362+
func getModMode(depMode DependencyInstallerMode, baseDir string) ModMode {
281363
if depMode == GoGetWithModules {
282364
// if a vendor/modules.txt file exists, we assume that there are vendored Go dependencies, and
283365
// skip the dependency installation step and run the extractor with `-mod=vendor`
284-
if util.FileExists("vendor/modules.txt") {
366+
if util.FileExists(baseDir + "/vendor/modules.txt") {
285367
return ModVendor
286-
} else if util.DirExists("vendor") {
368+
} else if util.DirExists(baseDir + "/vendor") {
287369
return ModMod
288370
}
289371
}
@@ -344,25 +426,29 @@ func getNeedGopath(depMode DependencyInstallerMode, importpath string) bool {
344426
}
345427

346428
// Try to update `go.mod` and `go.sum` if the go version is >= 1.16.
347-
func tryUpdateGoModAndGoSum(modMode ModMode, depMode DependencyInstallerMode) {
429+
func tryUpdateGoModAndGoSum(modMode ModMode, depMode DependencyInstallerMode, baseDir string) {
348430
// Go 1.16 and later won't automatically attempt to update go.mod / go.sum during package loading, so try to update them here:
349431
if modMode != ModVendor && depMode == GoGetWithModules && semver.Compare(getEnvGoSemVer(), "v1.16") >= 0 {
350432
// stat go.mod and go.sum
351-
beforeGoModFileInfo, beforeGoModErr := os.Stat("go.mod")
433+
goModPath := filepath.Join(baseDir, "go.mod")
434+
beforeGoModFileInfo, beforeGoModErr := os.Stat(goModPath)
352435
if beforeGoModErr != nil {
353436
log.Println("Failed to stat go.mod before running `go mod tidy -e`")
354437
}
355438

356-
beforeGoSumFileInfo, beforeGoSumErr := os.Stat("go.sum")
439+
goSumPath := filepath.Join(baseDir, "go.sum")
440+
beforeGoSumFileInfo, beforeGoSumErr := os.Stat(goSumPath)
357441

358442
// run `go mod tidy -e`
359-
res := util.RunCmd(exec.Command("go", "mod", "tidy", "-e"))
443+
cmd := exec.Command("go", "mod", "tidy", "-e")
444+
cmd.Dir = baseDir
445+
res := util.RunCmd(cmd)
360446

361447
if !res {
362448
log.Println("Failed to run `go mod tidy -e`")
363449
} else {
364450
if beforeGoModFileInfo != nil {
365-
afterGoModFileInfo, afterGoModErr := os.Stat("go.mod")
451+
afterGoModFileInfo, afterGoModErr := os.Stat(goModPath)
366452
if afterGoModErr != nil {
367453
log.Println("Failed to stat go.mod after running `go mod tidy -e`")
368454
} else if afterGoModFileInfo.ModTime().After(beforeGoModFileInfo.ModTime()) {
@@ -371,7 +457,7 @@ func tryUpdateGoModAndGoSum(modMode ModMode, depMode DependencyInstallerMode) {
371457
}
372458
}
373459

374-
afterGoSumFileInfo, afterGoSumErr := os.Stat("go.sum")
460+
afterGoSumFileInfo, afterGoSumErr := os.Stat(goSumPath)
375461
if afterGoSumErr != nil {
376462
log.Println("Failed to stat go.sum after running `go mod tidy -e`")
377463
} else {
@@ -560,7 +646,7 @@ func buildWithCustomCommands(inst string) {
560646
}
561647

562648
// Install dependencies using the given dependency installer mode.
563-
func installDependencies(depMode DependencyInstallerMode) {
649+
func installDependencies(depMode DependencyInstallerMode, baseDir string) {
564650
// automatically determine command to install dependencies
565651
var install *exec.Cmd
566652
if depMode == Dep {
@@ -606,31 +692,28 @@ func installDependencies(depMode DependencyInstallerMode) {
606692

607693
// get dependencies
608694
install = exec.Command("go", "get", "-v", "./...")
609-
log.Println("Installing dependencies using `go get -v ./...`.")
695+
install.Dir = baseDir
696+
log.Printf("Installing dependencies using `go get -v ./...` in `%s`.\n", baseDir)
610697
}
611698
util.RunCmd(install)
612699
}
613700

614701
// Run the extractor.
615-
func extract(depMode DependencyInstallerMode, modMode ModMode) {
702+
func extract(depMode DependencyInstallerMode, modMode ModMode, baseDir string) {
616703
extractor, err := util.GetExtractorPath()
617704
if err != nil {
618705
log.Fatalf("Could not determine path of extractor: %v.\n", err)
619706
}
620707

621-
cwd, err := os.Getwd()
622-
if err != nil {
623-
log.Fatalf("Unable to determine current directory: %s\n", err.Error())
624-
}
625-
626708
extractorArgs := []string{}
627709
if depMode == GoGetWithModules {
628710
extractorArgs = append(extractorArgs, modMode.argsForGoVersion(getEnvGoSemVer())...)
629711
}
630712
extractorArgs = append(extractorArgs, "./...")
631713

632-
log.Printf("Running extractor command '%s %v' from directory '%s'.\n", extractor, extractorArgs, cwd)
714+
log.Printf("Running extractor command '%s %v' from directory '%s'.\n", extractor, extractorArgs, baseDir)
633715
cmd := exec.Command(extractor, extractorArgs...)
716+
cmd.Dir = baseDir
634717
cmd.Stdout = os.Stdout
635718
cmd.Stderr = os.Stderr
636719
err = cmd.Run()
@@ -650,21 +733,21 @@ func installDependenciesAndBuild() {
650733

651734
// determine how to install dependencies and whether a GOPATH needs to be set up before
652735
// extraction
653-
depMode := getDepMode()
736+
depMode, baseDir := getDepMode(true)
654737
if _, present := os.LookupEnv("GO111MODULE"); !present {
655738
os.Setenv("GO111MODULE", "auto")
656739
}
657740

658-
goModVersion, goModVersionFound := tryReadGoDirective(depMode)
741+
goModVersion, goModVersionFound := tryReadGoDirective(depMode, baseDir)
659742

660743
if semver.Compare("v"+goModVersion, getEnvGoSemVer()) >= 0 {
661744
diagnostics.EmitNewerGoVersionNeeded()
662745
}
663746

664-
modMode := getModMode(depMode)
747+
modMode := getModMode(depMode, baseDir)
665748
modMode = fixGoVendorIssues(modMode, depMode, goModVersionFound)
666749

667-
tryUpdateGoModAndGoSum(modMode, depMode)
750+
tryUpdateGoModAndGoSum(modMode, depMode, baseDir)
668751

669752
importpath := getImportPath()
670753
needGopath := getNeedGopath(depMode, importpath)
@@ -707,11 +790,11 @@ func installDependenciesAndBuild() {
707790
if modMode == ModVendor {
708791
log.Printf("Skipping dependency installation because a Go vendor directory was found.")
709792
} else {
710-
installDependencies(depMode)
793+
installDependencies(depMode, baseDir)
711794
}
712795
}
713796

714-
extract(depMode, modMode)
797+
extract(depMode, modMode, baseDir)
715798
}
716799

717800
const minGoVersion = "1.11"
@@ -976,8 +1059,8 @@ func isGoInstalled() bool {
9761059
// Get the version of Go to install and output it to stdout as json.
9771060
func identifyEnvironment() {
9781061
var v versionInfo
979-
depMode := getDepMode()
980-
v.goModVersion, v.goModVersionFound = tryReadGoDirective(depMode)
1062+
depMode, baseDir := getDepMode(false)
1063+
v.goModVersion, v.goModVersionFound = tryReadGoDirective(depMode, baseDir)
9811064

9821065
v.goEnvVersionFound = isGoInstalled()
9831066
if v.goEnvVersionFound {

0 commit comments

Comments
 (0)