in go/tools/releaser/upgradedep.go [83:240]
func runUpgradeDep(ctx context.Context, stderr io.Writer, args []string) error {
// Parse arguments.
flags := flag.NewFlagSet("releaser upgrade-dep", flag.ContinueOnError)
var githubToken githubTokenFlag
var uploadToMirror, leaveWorkDir bool
flags.Var(&githubToken, "githubtoken", "GitHub personal access token or path to a file containing it")
flags.BoolVar(&uploadToMirror, "mirror", true, "whether to upload dependency archives to mirror.bazel.build")
flags.BoolVar(&leaveWorkDir, "work", false, "don't delete temporary work directory (for debugging)")
if err := flags.Parse(args); err != nil {
return err
}
if flags.NArg() == 0 {
return usageErrorf(&upgradeDepCmd, "No dependencies specified")
}
upgradeAll := false
for _, arg := range flags.Args() {
if arg == "all" {
upgradeAll = true
break
}
}
if upgradeAll && flags.NArg() != 1 {
return usageErrorf(&upgradeDepCmd, "When 'all' is specified, it must be the only argument")
}
httpClient := http.DefaultClient
if githubToken != "" {
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: string(githubToken)})
httpClient = oauth2.NewClient(ctx, ts)
}
gh := &githubClient{Client: github.NewClient(httpClient)}
workDir, err := os.MkdirTemp("", "releaser-upgrade-dep-*")
if leaveWorkDir {
fmt.Fprintf(stderr, "work dir: %s\n", workDir)
} else {
defer func() {
if rerr := os.RemoveAll(workDir); err == nil && rerr != nil {
err = rerr
}
}()
}
// Make sure we have everything we need.
// upgrade-dep must be run inside rules_go (though we just check for
// WORKSPACE), and a few tools must be available.
rootDir, err := repoRoot()
if err != nil {
return err
}
for _, tool := range []string{"diff", "gazelle", "gsutil", "patch"} {
if _, err := exec.LookPath(tool); err != nil {
return fmt.Errorf("%s must be installed in PATH", tool)
}
}
// Parse and index files we might want to update.
type file struct {
path string
funcName string
parsed *bzl.File
body []bzl.Expr
}
files := []file{
{path: filepath.Join(rootDir, "WORKSPACE")},
{path: filepath.Join(rootDir, "go/private/repositories.bzl"), funcName: "go_rules_dependencies"},
}
depIndex := make(map[string]*bzl.CallExpr)
for i := range files {
f := &files[i]
data, err := os.ReadFile(f.path)
if err != nil {
return err
}
f.parsed, err = bzl.Parse(f.path, data)
if err != nil {
return err
}
if f.funcName == "" {
f.body = f.parsed.Stmt
} else {
for _, expr := range f.parsed.Stmt {
def, ok := expr.(*bzl.DefStmt)
if !ok {
continue
}
if def.Name == f.funcName {
f.body = def.Body
break
}
}
if f.body == nil {
return fmt.Errorf("in file %s, could not find function %s", f.path, f.funcName)
}
}
for _, expr := range f.body {
call, ok := expr.(*bzl.CallExpr)
if !ok {
continue
}
for _, arg := range call.List {
kwarg, ok := arg.(*bzl.AssignExpr)
if !ok {
continue
}
key := kwarg.LHS.(*bzl.Ident) // required by parser
if key.Name != "name" {
continue
}
value, ok := kwarg.RHS.(*bzl.StringExpr)
if !ok {
continue
}
depIndex[value.Value] = call
}
}
}
// Update dependencies in those files.
eg, egctx := errgroup.WithContext(ctx)
if upgradeAll {
for name := range depIndex {
name := name
if _, _, err := parseUpgradeDepDirective(depIndex[name]); err != nil {
continue
}
eg.Go(func() error {
return upgradeDepDecl(egctx, gh, workDir, name, depIndex[name], uploadToMirror)
})
}
} else {
for _, arg := range flags.Args() {
if depIndex[arg] == nil {
return fmt.Errorf("could not find dependency %s", arg)
}
}
for _, arg := range flags.Args() {
arg := arg
eg.Go(func() error {
return upgradeDepDecl(egctx, gh, workDir, arg, depIndex[arg], uploadToMirror)
})
}
}
if err := eg.Wait(); err != nil {
return err
}
// Format and write files back to disk.
for _, f := range files {
if err := os.WriteFile(f.path, bzl.Format(f.parsed), 0666); err != nil {
return err
}
}
return nil
}