func runPrepare()

in go/tools/releaser/prepare.go [69:241]


func runPrepare(ctx context.Context, stderr io.Writer, args []string) error {
	// Parse arguments.
	flags := flag.NewFlagSet("releaser prepare", flag.ContinueOnError)
	var rnotesPath, version string
	var githubToken githubTokenFlag
	var uploadToMirror 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.StringVar(&rnotesPath, "rnotes", "", "Name of file containing release notes in Markdown")
	flags.StringVar(&version, "version", "", "Version to release")
	if err := flags.Parse(args); err != nil {
		return err
	}
	if flags.NArg() > 0 {
		return usageErrorf(&prepareCmd, "No arguments expected")
	}
	if githubToken == "" {
		return usageErrorf(&prepareCmd, "-githubtoken must be set")
	}
	if rnotesPath == "" {
		return usageErrorf(&prepareCmd, "-rnotes must be set")
	}
	if version == "" {
		return usageErrorf(&prepareCmd, "-version must be set")
	}
	if semver.Canonical(version) != version || semver.Prerelease(version) != "" || semver.Build(version) != "" {
		return usageErrorf(&prepareCmd, "-version must be a canonical version, like v1.2.3")
	}

	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: string(githubToken)})
	tc := oauth2.NewClient(ctx, ts)
	gh := &githubClient{Client: github.NewClient(tc)}

	// Get the GitHub release.
	fmt.Fprintf(stderr, "checking if release %s exists...\n", version)
	release, err := gh.getReleaseByTagIncludingDraft(ctx, "bazelbuild", "rules_go", version)
	if err != nil && !errors.Is(err, errReleaseNotFound) {
		return err
	}
	if release != nil && !release.GetDraft() {
		return fmt.Errorf("release %s was already published", version)
	}

	// Check that RULES_GO_VERSION is set correctly on the release branch.
	// If this is a minor release (x.y.0), create the release branch if it
	// does not exist.
	fmt.Fprintf(stderr, "checking RULES_GO_VERSION...\n")
	rootDir, err := repoRoot()
	if err != nil {
		return err
	}
	if err := checkNoGitChanges(ctx, rootDir); err != nil {
		return err
	}
	majorMinor := semver.MajorMinor(version)
	isMinorRelease := semver.Canonical(majorMinor) == version
	branchName := "release-" + majorMinor[len("v"):]
	if !gitBranchExists(ctx, rootDir, branchName) {
		if !isMinorRelease {
			return fmt.Errorf("release branch %q does not exist locally. Fetch it, set RULES_GO_VERSION, add commits, and run this command again.")
		}
		if err := checkRulesGoVersion(ctx, rootDir, "HEAD", version); err != nil {
			return err
		}
		fmt.Fprintf(stderr, "creating branch %s...\n", branchName)
		if err := gitCreateBranch(ctx, rootDir, branchName, "HEAD"); err != nil {
			return err
		}
	} else {
		if err := checkRulesGoVersion(ctx, rootDir, branchName, version); err != nil {
			return err
		}
	}

	// Create an archive.
	fmt.Fprintf(stderr, "creating archive...\n")
	arcFile, err := os.CreateTemp("", "rules_go-%s-*.zip")
	if err != nil {
		return err
	}
	arcName := arcFile.Name()
	arcFile.Close()
	defer func() {
		if rerr := os.Remove(arcName); err == nil && rerr != nil {
			err = rerr
		}
	}()
	if err := gitCreateArchive(ctx, rootDir, branchName, arcName); err != nil {
		return err
	}
	arcSum, err := sha256SumFile(arcName)
	if err != nil {
		return err
	}

	// Read release notes, append boilerplate.
	rnotesData, err := os.ReadFile(rnotesPath)
	if err != nil {
		return err
	}
	rnotesData = bytes.TrimSpace(rnotesData)
	goVersion, err := findLatestGoVersion()
	if err != nil {
		return err
	}
	boilerplate := genBoilerplate(version, arcSum, goVersion)
	rnotesStr := string(rnotesData) + "\n\n## `WORKSPACE` code\n\n```\n" + boilerplate + "\n```\n"

	// Push the release branch.
	fmt.Fprintf(stderr, "pushing branch %s to origin...\n", branchName)
	if err := gitPushBranch(ctx, rootDir, branchName); err != nil {
		return err
	}

	// Upload to mirror.bazel.build.
	arcGHURLWithoutScheme := fmt.Sprintf("github.com/bazelbuild/rules_go/releases/download/%[1]s/rules_go-%[1]s.zip", version)
	if uploadToMirror {
		fmt.Fprintf(stderr, "uploading archive to mirror.bazel.build...\n")
		if err := copyFileToMirror(ctx, arcGHURLWithoutScheme, arcName); err != nil {
			return err
		}
	}

	// Create or update the GitHub release.
	if release == nil {
		fmt.Fprintf(stderr, "creating draft release...\n")
		draft := true
		release = &github.RepositoryRelease{
			TagName:         &version,
			TargetCommitish: &branchName,
			Name:            &version,
			Body:            &rnotesStr,
			Draft:           &draft,
		}
		if release, _, err = gh.Repositories.CreateRelease(ctx, "bazelbuild", "rules_go", release); err != nil {
			return err
		}
	} else {
		fmt.Fprintf(stderr, "updating release...\n")
		release.Body = &rnotesStr
		if release, _, err = gh.Repositories.EditRelease(ctx, "bazelbuild", "rules_go", release.GetID(), release); err != nil {
			return err
		}
		for _, asset := range release.Assets {
			if _, err := gh.Repositories.DeleteReleaseAsset(ctx, "bazelbuild", "rules_go", asset.GetID()); err != nil {
				return err
			}
		}
	}
	arcFile, err = os.Open(arcName)
	if err != nil {
		return err
	}
	defer arcFile.Close()
	uploadOpts := &github.UploadOptions{
		Name:      "rules_go-" + version + ".zip",
		MediaType: "application/zip",
	}
	if _, _, err := gh.Repositories.UploadReleaseAsset(ctx, "bazelbuild", "rules_go", release.GetID(), uploadOpts, arcFile); err != nil {
		return err
	}

	testURL := fmt.Sprintf("https://buildkite.com/bazel/rules-go-golang/builds?branch=%s", branchName)
	fmt.Fprintf(stderr, `
Release %s has been prepared and uploaded.

* Ensure that all tests pass in CI at %s.
* Review and publish the release at %s.
* Update README.rst and WORKSPACE if necessary.
`, version, testURL, release.GetHTMLURL())

	return nil
}