Skip to content

Commit

Permalink
update-repos: add -parse_only flag
Browse files Browse the repository at this point in the history
  • Loading branch information
sluongng committed Jul 5, 2024
1 parent 13ccb7b commit a718352
Show file tree
Hide file tree
Showing 5 changed files with 486 additions and 4 deletions.
11 changes: 7 additions & 4 deletions cmd/gazelle/update-repos.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type updateReposConfig struct {
macroFileName string
macroDefName string
pruneRules bool
parseOnly bool
workspace *rule.File
repoFileMap map[string]*rule.File
}
Expand Down Expand Up @@ -80,6 +81,7 @@ func (*updateReposConfigurer) RegisterFlags(fs *flag.FlagSet, cmd string, c *con
fs.StringVar(&uc.repoFilePath, "from_file", "", "Gazelle will translate repositories listed in this file into repository rules in WORKSPACE or a .bzl macro function. Gopkg.lock and go.mod files are supported")
fs.Var(macroFlag{macroFileName: &uc.macroFileName, macroDefName: &uc.macroDefName}, "to_macro", "Tells Gazelle to write repository rules into a .bzl macro function rather than the WORKSPACE file. . The expected format is: macroFile%defName")
fs.BoolVar(&uc.pruneRules, "prune", false, "When enabled, Gazelle will remove rules that no longer have equivalent repos in the go.mod file. Can only used with -from_file.")
fs.BoolVar(&uc.parseOnly, "parse_only", false, "When enabled, Gazelle will derive the repository rules from parsing the given config file (i.e. go.mod) and lock file (i.e. go.sum) without making any additional network calls.")
}

func (*updateReposConfigurer) CheckFlags(fs *flag.FlagSet, c *config.Config) error {
Expand Down Expand Up @@ -425,10 +427,11 @@ func importRepos(c *config.Config, rc *repo.RemoteCache) (gen, empty []*rule.Rul
}
}
res := importer.ImportRepos(language.ImportReposArgs{
Config: c,
Path: uc.repoFilePath,
Prune: uc.pruneRules,
Cache: rc,
Config: c,
Path: uc.repoFilePath,
Prune: uc.pruneRules,
ParseOnly: uc.parseOnly,
Cache: rc,
})
return res.Gen, res.Empty, res.Error
}
Expand Down
98 changes: 98 additions & 0 deletions language/go/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,114 @@ limitations under the License.
package golang

import (
"bufio"
"bytes"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/bazelbuild/bazel-gazelle/language"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
"golang.org/x/tools/go/packages"
)

func importReposFromParse(args language.ImportReposArgs) language.ImportReposResult {
// Parse go.sum for checksum
checksumIdx := make(map[string]string)
goSumPath := filepath.Join(filepath.Dir(args.Path), "go.sum")
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 := fields[0], fields[1], fields[2]
if strings.HasSuffix(version, "/go.mod") {
continue
}
checksumIdx[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()),
}
}

// Parse go.mod for modules information
b, err := os.ReadFile(args.Path)
if err != nil {
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),
}
}

// Build an index of 'replace' directives
replaceIdx := make(map[string]module.Version, len(modFile.Replace))
for _, replace := range modFile.Replace {
replaceIdx[replace.Old.String()] = replace.New
}

pathToModule := make(map[string]*moduleFromList, len(modFile.Require))
for _, require := range modFile.Require {
modKey := require.Mod.String()
pathToModule[modKey] = &moduleFromList{
Module: packages.Module{
Path: require.Mod.Path,
Version: require.Mod.Version,
},
}

// If there is a match replacement, add .Replace and change .Sum to the checksum of the new module
replace, foundReplace := replaceIdx[modKey]
if !foundReplace {
replace, foundReplace = replaceIdx[require.Mod.Path]
}
if foundReplace {
replaceChecksum, foundReplaceChecksum := checksumIdx[replace.String()]
if !foundReplaceChecksum {
return language.ImportReposResult{
Error: fmt.Errorf("module %s is missing from go.sum. Run 'go mod tidy' to fix.", replace.String()),
}
}
pathToModule[modKey].Replace = &packages.Module{
Path: replace.Path,
Version: replace.Version,
}
pathToModule[modKey].Sum = replaceChecksum
continue
}

checksum, ok := checksumIdx[modKey]
if !ok {
return language.ImportReposResult{
Error: fmt.Errorf("module %s is missing from go.sum. Run 'go mod tidy' to fix.", modKey),
}
}
pathToModule[modKey].Sum = checksum
}

return language.ImportReposResult{Gen: toRepositoryRules(pathToModule)}
}

func importReposFromModules(args language.ImportReposArgs) language.ImportReposResult {
if args.ParseOnly {
return importReposFromParse(args)
}
// run go list in the dir where go.mod is located
data, err := goListModules(filepath.Dir(args.Path))
if err != nil {
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
Loading

0 comments on commit a718352

Please sign in to comment.