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
}