func runUpgradeDep()

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
}