Skip to content

Commit

Permalink
update-repos: use pruned graph
Browse files Browse the repository at this point in the history
Change update-repos to assume that go.mod and go.sum contains all the
information needed. This implies that the user has already run `go mod
tidy` or `go mod tidy -e` prior to running `gazelle update-repos`.

There are a few benefits with this new approach:

1. No network call. This allows enterprise setup to make network call
   from a separate environment while running `go mod tidy`. And
   update-repos is massively simplify to no network call and thus, much
   faster.

2. Benefit from `go mod tidy` graph pruning. Unused transitive
   dependencies will not longer be generated as extra `go_repository`.
   In some setup, this could reduce the amount of `go_repository` by
   6-10x.
  • Loading branch information
sluongng committed Jul 5, 2024
1 parent 852fdcf commit bb80a30
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 28 deletions.
91 changes: 73 additions & 18 deletions language/go/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)}
Expand Down
3 changes: 3 additions & 0 deletions language/go/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
31 changes: 22 additions & 9 deletions language/go/update_import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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: "",
Expand Down
2 changes: 1 addition & 1 deletion repo/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
}

Expand Down

0 comments on commit bb80a30

Please sign in to comment.