diff --git a/language/go/modules.go b/language/go/modules.go index 828cb8da7..e1dd31f3e 100644 --- a/language/go/modules.go +++ b/language/go/modules.go @@ -16,49 +16,104 @@ limitations under the License. package golang import ( - "bytes" + "bufio" "fmt" + "io" "os" "path/filepath" "strings" "github.com/bazelbuild/bazel-gazelle/language" + "golang.org/x/mod/modfile" ) -func importReposFromModules(args language.ImportReposArgs) language.ImportReposResult { - // run go list in the dir where go.mod is located - data, err := goListModules(filepath.Dir(args.Path)) +func copyFile(src, dst string) error { + source, err := os.Open(src) + if err != nil { + return err + } + defer source.Close() + + destination, err := os.Create(dst) if err != nil { - return language.ImportReposResult{Error: processGoListError(err, data)} + return err } + defer destination.Close() - pathToModule, err := extractModules(data) + _, err = io.Copy(destination, source) if err != nil { - return language.ImportReposResult{Error: err} + return err } - // Load sums from go.sum. Ideally, they're all there. + return nil +} + +func importReposFromModules(args language.ImportReposArgs) language.ImportReposResult { + // Parse go.sum for checksum + pathToSum := make(map[string]string) goSumPath := filepath.Join(filepath.Dir(args.Path), "go.sum") - data, _ = os.ReadFile(goSumPath) - lines := bytes.Split(data, []byte("\n")) - for _, line := range lines { - line = bytes.TrimSpace(line) - fields := bytes.Fields(line) + goSumFile, err := os.Open(goSumPath) + if err != nil { + return language.ImportReposResult{ + Error: fmt.Errorf("failed to open go.sum file at %s: %v", goSumPath, err), + } + } + scanner := bufio.NewScanner(goSumFile) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + fields := strings.Fields(line) if len(fields) != 3 { continue } - path, version, sum := string(fields[0]), string(fields[1]), string(fields[2]) + path, version, sum := fields[0], fields[1], fields[2] if strings.HasSuffix(version, "/go.mod") { continue } - if mod, ok := pathToModule[path+"@"+version]; ok { - mod.Sum = sum + pathToSum[path+"@"+version] = sum + } + if scanner.Err() != nil { + return language.ImportReposResult{ + Error: fmt.Errorf("failed to parse go.sum file at %s: %v", goSumPath, scanner.Err()), } } - pathToModule, err = fillMissingSums(pathToModule) + // Parse go.mod for modules information + b, err := os.ReadFile(args.Path) if err != nil { - return language.ImportReposResult{Error: fmt.Errorf("finding module sums: %v", err)} + return language.ImportReposResult{ + Error: fmt.Errorf("failed to read go.mod file at %s: %v", args.Path, err), + } + } + modFile, err := modfile.Parse(filepath.Base(args.Path), b, nil) + if err != nil { + return language.ImportReposResult{ + Error: fmt.Errorf("failed to parse go.mod file at %s: %v", args.Path, err), + } + } + pathToModule := make(map[string]*moduleFromList, len(modFile.Require)) + pathToModule[modFile.Module.Mod.String()] = &moduleFromList{ + Path: modFile.Module.Mod.Path, + Version: modFile.Module.Mod.Version, + Main: true, + } + for _, require := range modFile.Require { + sum, ok := pathToSum[require.Mod.String()] + if !ok { + return language.ImportReposResult{ + Error: fmt.Errorf("module %s is missing from go.sum. Run 'go mod tidy' to fix.", require.Mod.String()), + } + } + pathToModule[require.Mod.String()] = &moduleFromList{ + Path: require.Mod.Path, + Version: require.Mod.Version, + Sum: sum, + } + } + for _, replace := range modFile.Replace { + if oldMod, ok := pathToModule[replace.Old.String()]; ok { + oldMod.Replace.Path = replace.New.Path + oldMod.Replace.Version = replace.New.Version + } } return language.ImportReposResult{Gen: toRepositoryRules(pathToModule)} diff --git a/language/go/update.go b/language/go/update.go index d1be928f4..23280c5b9 100644 --- a/language/go/update.go +++ b/language/go/update.go @@ -69,6 +69,9 @@ func (*goLang) CanImport(path string) bool { func (*goLang) ImportRepos(args language.ImportReposArgs) language.ImportReposResult { res := repoImportFuncs[filepath.Base(args.Path)](args) + if res.Error != nil { + return res + } for _, r := range res.Gen { setBuildAttrs(getGoConfig(args.Config), r) } diff --git a/language/go/update_import_test.go b/language/go/update_import_test.go index 86ac4d7b6..4043df585 100644 --- a/language/go/update_import_test.go +++ b/language/go/update_import_test.go @@ -128,11 +128,24 @@ go_repository( ) go_repository( - name = "com_github_pelletier_go_toml", - importpath = "github.com/pelletier/go-toml", - replace = "github.com/fork/go-toml", - sum = "h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=", - version = "v0.0.0-20190425002759-70bc0436ed16", + name = "com_github_fsnotify_fsnotify", + importpath = "github.com/fsnotify/fsnotify", + sum = "h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=", + version = "v1.4.7", +) + +go_repository( + name = "com_github_kr_pretty", + importpath = "github.com/kr/pretty", + sum = "h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=", + version = "v0.1.0", +) + +go_repository( + name = "com_github_pmezard_go_difflib", + importpath = "github.com/pmezard/go-difflib", + sum = "h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=", + version = "v1.0.0", ) go_repository( @@ -150,10 +163,10 @@ go_repository( ) go_repository( - name = "org_golang_x_tools", - importpath = "golang.org/x/tools", - sum = "h1:FkAkwuYWQw+IArrnmhGlisKHQF4MsZ2Nu/fX4ttW55o=", - version = "v0.0.0-20190122202912-9c309ee22fab", + name = "org_golang_x_sys", + importpath = "golang.org/x/sys", + sum = "h1:Lk4tbZFnlyPgV+sLgTw5yGfzrlOn9kx4vSombi2FFlY=", + version = "v0.0.0-20190122071731-054c452bb702", ) `, wantErr: "", diff --git a/repo/remote.go b/repo/remote.go index 1b2805854..cd1bc3763 100644 --- a/repo/remote.go +++ b/repo/remote.go @@ -607,7 +607,7 @@ func (rc *RemoteCache) initTmp() { if rc.tmpErr != nil { return } - rc.tmpErr = os.WriteFile(filepath.Join(rc.tmpDir, "go.mod"), []byte("module gazelle_remote_cache\ngo 1.15\n"), 0o666) + rc.tmpErr = os.WriteFile(filepath.Join(rc.tmpDir, "go.mod"), []byte("module gazelle_remote_cache\ngo 1.22\n"), 0o666) }) }