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
}