func run()

in tools/releaser/main.go [68:223]


func run() (_err error) {
	var (
		repoRoot        string
		tag             string
		skipBranchCheck bool
	)

	flag.StringVar(&repoRoot, "repoRoot", os.Getenv("BUILD_WORKSPACE_DIRECTORY"), "root directory of hermetic_cc_toolchain repo")
	flag.StringVar(&tag, "tag", "", "tag for this release")
	flag.BoolVar(&skipBranchCheck, "skipBranchCheck", false, "skip branch check (for testing the release tool)")

	flag.Usage = func() {
		fmt.Fprint(flag.CommandLine.Output(), `usage: bazel run //tools/releaser -- -repoRoot <repoRoot> -tag <tag>

This utility is intended to handle many of the steps to release a new version.

`)
		flag.PrintDefaults()
	}

	flag.Parse()

	if tag == "" {
		return fmt.Errorf("tag is required")
	}

	if !_tagRegexp.MatchString(tag) {
		return _errTag
	}

	type checkType struct {
		args    []string
		wantOut string
	}

	checks := []checkType{{[]string{"diff", "--stat", "--exit-code"}, ""}}
	if !skipBranchCheck {
		checks = append(
			checks,
			checkType{[]string{"branch", "--show-current"}, "main\n"},
		)
	}

	log("checking if git tree is ready for the release")
	for _, c := range checks {
		out, err := git(repoRoot, c.args...)
		if err != nil {
			return err
		}

		if string(out) == c.wantOut {
			continue
		}

		return fmt.Errorf(
			"unexpected output for %q. Expected %q, got:\n---\n%s\n---\n",
			"git "+strings.Join(c.args, " "),
			c.wantOut,
			out,
		)
	}

	if err := checkZigMirrored(repoRoot); err != nil {
		return fmt.Errorf("zig is correctly mirrored: %w", err)
	}

	// if the tag already exists, do not cut a new one.
	tagAlreadyExists := false
	// cut a new tag if the tag does not already exist.
	if out, err := git(repoRoot, "tag", "-l", tag); err != nil {
		return err
	} else {
		tagAlreadyExists = strings.TrimSpace(out) == tag
	}

	if hash, ok := _tagHashes[tag]; ok {
		log("Asked for a pre-existing release which has a hardcoded hash. " +
			"Running in 'check-only' mode.")
		boilerplate := genBoilerplate(tag, hash)
		if err := updateBoilerplate(repoRoot, boilerplate); err != nil {
			return fmt.Errorf("update boilerplate: %w", err)
		}
		log("updated %s", strings.Join(_boilerplateFiles, " and "))
		sep := strings.Repeat("-", 72)
		log("Release boilerplate:\n%[1]s\n%[2]s%[1]s\n", sep, boilerplate)
		return nil
	}

	if err := updateModuleVersion(repoRoot, tag); err != nil {
		return err
	}

	releaseRef := "HEAD"
	if tagAlreadyExists {
		releaseRef = tag
	}

	hash1, err := makeTgz(io.Discard, repoRoot, releaseRef)
	if err != nil {
		return fmt.Errorf("calculate hash1 of release tarball: %w", err)
	}

	boilerplate := genBoilerplate(tag, hash1)
	if err := updateBoilerplate(repoRoot, boilerplate); err != nil {
		return fmt.Errorf("update boilerplate: %w", err)
	}

	// If tag does not exist, create a new commit with the updated hashes
	// and cut the new tag.
	//
	// If the tag exists, skip committing the tag; we will just verify
	// that the hashes in the README and examples/ are up to date.
	if !tagAlreadyExists {
		commitMsg := fmt.Sprintf("Releasing hermetic_cc_toolchain %s", tag)
		if _, err := git(repoRoot, "commit", "-am", commitMsg); err != nil {
			return err
		}
		if _, err := git(repoRoot, "tag", tag); err != nil {
			return err
		}
	}

	// Cut the final release and compare hash1 and hash2 just in case.
	fpath := path.Join(repoRoot, fmt.Sprintf("hermetic_cc_toolchain-%s.tar.gz", tag))
	tgz, err := os.Create(fpath)
	if err != nil {
		return err
	}

	hash2, err := makeTgz(tgz, repoRoot, tag)
	if err != nil {
		return fmt.Errorf("make release tarball: %w", err)
	}

	if err := tgz.Close(); err != nil {
		return err
	}

	if hash1 != hash2 {
		// This may happen if the release tarball depends on the boilerplate
		// that gets updated with the new tag. Don't do this. We want the
		// release commit to point to the correct hashes for that release.
		return fmt.Errorf(
			"hashes before and after release differ: %s %s",
			hash1,
			hash2,
		)
	}

	log("wrote %s, sha256: %s", fpath, hash2)

	sep := strings.Repeat("-", 72)
	log("Release boilerplate:\n%[1]s\n%[2]s%[1]s\n", sep, boilerplate)

	return nil
}