diff --git a/scripts/releaser/release.go b/scripts/releaser/release.go index 67f58c5d53..618766d7d9 100644 --- a/scripts/releaser/release.go +++ b/scripts/releaser/release.go @@ -68,17 +68,17 @@ func runRelease(ctx context.Context, inv *serpent.Invocation, executor ReleaseEx return xerrors.Errorf("detecting branch: %w", err) } - // Match standard release branches (release/2.32) and RC - // branches (release/2.32-rc.0). - branchRe := regexp.MustCompile(`^release/(\d+)\.(\d+)(?:-rc\.(\d+))?$`) + // Match release branches (release/X.Y). RCs are tagged + // from main, not from release branches. + branchRe := regexp.MustCompile(`^release/(\d+)\.(\d+)$`) m := branchRe.FindStringSubmatch(currentBranch) if m == nil { - warnf(w, "Current branch %q is not a release branch (release/X.Y or release/X.Y-rc.N).", currentBranch) + warnf(w, "Current branch %q is not a release branch (release/X.Y).", currentBranch) branchInput, err := cliui.Prompt(inv, cliui.PromptOptions{ - Text: "Enter the release branch to use (e.g. release/2.21 or release/2.21-rc.0)", + Text: "Enter the release branch to use (e.g. release/2.21)", Validate: func(s string) error { if !branchRe.MatchString(s) { - return xerrors.New("must be in format release/X.Y or release/X.Y-rc.N (e.g. release/2.21 or release/2.21-rc.0)") + return xerrors.New("must be in format release/X.Y (e.g. release/2.21)") } return nil }, @@ -91,10 +91,6 @@ func runRelease(ctx context.Context, inv *serpent.Invocation, executor ReleaseEx } branchMajor, _ := strconv.Atoi(m[1]) branchMinor, _ := strconv.Atoi(m[2]) - branchRC := -1 // -1 means not an RC branch. - if m[3] != "" { - branchRC, _ = strconv.Atoi(m[3]) - } successf(w, "Using release branch: %s", currentBranch) // --- Fetch & sync check --- @@ -138,31 +134,44 @@ func runRelease(ctx context.Context, inv *serpent.Invocation, executor ReleaseEx } } + // changelogBaseRef is the git ref used as the starting point + // for release notes generation. When a tag already exists in + // this minor series we use it directly. For the first release + // on a new minor no matching tag exists, so we compute the + // merge-base with the previous minor's release branch instead. + // This works even when that branch has no tags yet (it was + // just cut and pushed). As a last resort we fall back to the + // latest reachable tag from a previous minor. + var changelogBaseRef string + if prevVersion != nil { + changelogBaseRef = prevVersion.String() + } else { + prevReleaseBranch := fmt.Sprintf("release/%d.%d", branchMajor, branchMinor-1) + if err := gitRun("fetch", "--quiet", "origin", prevReleaseBranch); err != nil { + warnf(w, "Could not fetch %s: %v", prevReleaseBranch, err) + } + if mb, mbErr := gitOutput("merge-base", "HEAD", "origin/"+prevReleaseBranch); mbErr == nil && mb != "" { + changelogBaseRef = mb + infof(w, "Using merge-base with %s as changelog base: %s", prevReleaseBranch, mb[:12]) + } else { + // No previous release branch found; fall back to + // the latest reachable tag from a previous minor. + for _, t := range mergedTags { + if t.Major == branchMajor && t.Minor < branchMinor { + changelogBaseRef = t.String() + break + } + } + } + } + var suggested version if prevVersion == nil { infof(w, "No previous release tag found on this branch.") suggested = version{Major: branchMajor, Minor: branchMinor, Patch: 0} - if branchRC >= 0 { - suggested.Pre = fmt.Sprintf("rc.%d", branchRC) - } } else { infof(w, "Previous release tag: %s", prevVersion.String()) - if branchRC >= 0 { - // On an RC branch, suggest the next RC for - // the same base version. - nextRC := 0 - if prevVersion.IsRC() { - nextRC = prevVersion.rcNumber() + 1 - } - suggested = version{ - Major: prevVersion.Major, - Minor: prevVersion.Minor, - Patch: prevVersion.Patch, - Pre: fmt.Sprintf("rc.%d", nextRC), - } - } else { - suggested = version{Major: prevVersion.Major, Minor: prevVersion.Minor, Patch: prevVersion.Patch + 1} - } + suggested = version{Major: prevVersion.Major, Minor: prevVersion.Minor, Patch: prevVersion.Patch + 1} } fmt.Fprintln(w) @@ -366,8 +375,8 @@ func runRelease(ctx context.Context, inv *serpent.Invocation, executor ReleaseEx infof(w, "Generating release notes...") commitRange := "HEAD" - if prevVersion != nil { - commitRange = prevVersion.String() + "..HEAD" + if changelogBaseRef != "" { + commitRange = changelogBaseRef + "..HEAD" } commits, err := commitLog(commitRange) @@ -473,16 +482,16 @@ func runRelease(ctx context.Context, inv *serpent.Invocation, executor ReleaseEx } if !hasContent { prevStr := "the beginning of time" - if prevVersion != nil { - prevStr = prevVersion.String() + if changelogBaseRef != "" { + prevStr = changelogBaseRef } fmt.Fprintf(¬es, "\n_No changes since %s._\n", prevStr) } // Compare link. - if prevVersion != nil { + if changelogBaseRef != "" { fmt.Fprintf(¬es, "\nCompare: [`%s...%s`](https://github.com/%s/%s/compare/%s...%s)\n", - prevVersion, newVersion, owner, repo, prevVersion, newVersion) + changelogBaseRef, newVersion, owner, repo, changelogBaseRef, newVersion) } // Container image.