Skip to content

Add krel release-notes rerun subcommand with push support and test coverage#4349

Open
kernel-kun wants to merge 6 commits into
kubernetes:masterfrom
kernel-kun:krel-rerun-feature
Open

Add krel release-notes rerun subcommand with push support and test coverage#4349
kernel-kun wants to merge 6 commits into
kubernetes:masterfrom
kernel-kun:krel-rerun-feature

Conversation

@kernel-kun
Copy link
Copy Markdown

@kernel-kun kernel-kun commented Mar 29, 2026

/kind feature

What this PR does / why we need it:

Adds a krel release-notes rerun subcommand that fetches an existing draft branch from a source fork of k/sig-release, regenerates the release notes (applying maps), commits the result, and optionally pushes to a destination fork. This supports iterative release notes workflows where the draft needs to be regenerated multiple times during a release cycle.

Also fixes a bug where SIG labels were dropped from the markdown during ApplyMap re-rendering, and makes DirectoryMapProvider.GetMapsForPR thread-safe.

Changes:

cmd/krel/cmd/release_notes.go

  • Add rerunCmd as a proper cobra subcommand of release-notes (not a flag) with its own flags, validation, help text, and examples
  • Add --draft-pr-source-fork (required): specifies the k/sig-release fork to fetch the existing draft branch from (org or org/repo)
  • Add --draft-pr-source-branch: branch to fetch (defaults to release-notes-draft-<tag>)
  • Add --draft-pr-push-fork: optional destination fork to push regenerated notes to (org or org/repo, omit to skip push)
  • Add --draft-pr-push-branch: branch to push to on the destination fork (defaults to source branch)
  • Add rerunDraftNotes() function: clones upstream k/sig-release, fetches the draft branch from source fork, auto-discovers maps from the branch, regenerates release notes, commits, and optionally pushes with --force-with-lease
  • Add validateRerunOpts(): validates GITHUB_TOKEN, required flags, fork normalization ("myorg""myorg/sig-release"), branch default resolution, and dynamic --maps-from warning (e.g. releases/release-1.32/release-notes/maps instead of release-x.yz)
  • Add detailed Long description with numbered process steps and Example field with realistic invocations

pkg/notes/notes.go

  • Fix SIG labels dropped from markdown during ApplyMap re-render: change len(rn.SIGs) > 1 to len(rn.SIGs) >= 1 so single-SIG notes retain their [SIG Foo] suffix
  • Set reRenderMarkdown = true when noteMap.ReleaseNote.SIGs is applied, ensuring the markdown is rebuilt with the updated SIG list
  • Set rn.PRBody = *noteMap.PRBody so that pr_body is applied from maps to .json file (similar to all other fields)

pkg/notes/notes_map.go

  • Replace nil-check on Maps with sync.Once in DirectoryMapProvider.GetMapsForPR to make concurrent calls thread-safe

cmd/krel/cmd/release_notes_test.go (new file)

  • TestValidateRerunOpts_RequiredFlags: missing --draft-pr-source-fork and missing --tag both error
  • TestValidateRerunOpts_ForkNormalization: org-only source/push fork gets /sig-release appended; full org/repo stays unchanged
  • TestValidateRerunOpts_BranchDefaults: empty source branch defaults to release-notes-draft-<tag>; empty push branch defaults to source branch
  • TestValidateRerunOpts_ValidOptions: all required flags set, validation returns nil
  • TestValidateRerunOpts_DynamicMapsWarning: verifies the warning contains the dynamic path for alpha, beta, and rc tags

pkg/notes/notes_map_test.go

  • TestGetMapsForPR_Concurrent: 100 goroutines calling GetMapsForPR on the same DirectoryMapProvider instance, asserting consistent results and no panics

Help output:

$ krel release-notes rerun -h
krel release-notes rerun

Re-generates the release notes draft against an existing PR branch. The process:

1. Clone upstream k/sig-release and fetch the draft branch from --draft-pr-source-fork.
2. Gather release notes from k/k for the given --tag range.
3. Apply map files (from the branch and any extra --maps-from paths).
4. Write the updated markdown and JSON drafts, then commit.
5. If --draft-pr-push-fork is set, push the branch there (updating any open PR).

The local clone is preserved after the run so you can inspect or push manually.

Examples:
  # Rerun against an existing draft branch and push to a destination fork:
  krel release-notes rerun \
    --tag v1.36.0 \
    --draft-pr-source-fork myorg \
    --draft-pr-push-fork destorg

  # Rerun with additional maps from a local directory:
  krel release-notes rerun \
    --tag v1.36.0 \
    --draft-pr-source-fork myorg/sig-release \
    --maps-from /path/to/extra/maps

Which issue(s) this PR fixes:

#4348 #4121

Special notes for your reviewer:

  • The 9 pre-existing test failures in sut_test.go / ff_test.go are unrelated to this PR — they fail on master as well (git push errors in the test harness).
  • All new tests pass with go test -race.
  • golangci-lint run passes clean.
  • The rerun functionality was initially implemented as a --rerun flag, then extracted into a proper cobra subcommand for better UX (dedicated help, examples, flag scoping).

Does this PR introduce a user-facing change?

Add `krel release-notes rerun` subcommand for iterative release notes regeneration against an existing draft branch.

@k8s-ci-robot
Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@k8s-ci-robot k8s-ci-robot added kind/feature Categorizes issue or PR as related to a new feature. release-note Denotes a PR that will be considered when it comes time to generate release notes. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-priority labels Mar 29, 2026
@k8s-ci-robot k8s-ci-robot added area/release-eng Issues or PRs related to the Release Engineering subproject sig/release Categorizes an issue or PR as relevant to SIG Release. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Mar 29, 2026
Trigger markdown re-render when SIGs change in map files, and fix
threshold from len > 1 to len >= 1 so single-SIG PRs retain their
[SIG XYZ] suffix during krel release-notes rerun.
- Add Example field to rerunCmd with realistic invocations
- Expand Long description with numbered process steps
- Replace static release-x.yz placeholder in maps-from warning
  with dynamic path parsed from --tag (e.g. release-1.32)
- Add GITHUB_TOKEN check to validateRerunOpts for early failure
- Update tests for pre-release tags and proper logrus output capture
@kernel-kun kernel-kun changed the title Add krel release-notes --rerun with push support and test coverage Add krel release-notes rerun subcommand with push support and test coverage Mar 29, 2026
@k8s-ci-robot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: kernel-kun
Once this PR has been reviewed and has the lgtm label, please assign cpanato for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kernel-kun kernel-kun marked this pull request as ready for review March 29, 2026 18:55
Copilot AI review requested due to automatic review settings March 29, 2026 18:55
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 29, 2026
@k8s-ci-robot k8s-ci-robot requested a review from jrsapi March 29, 2026 18:55
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new krel release-notes rerun subcommand to support regenerating release-notes drafts against an existing draft branch (optionally pushing updates), plus fixes/coverage for notes mapping and SIG label rendering.

Changes:

  • Introduce release-notes rerun cobra subcommand with source/push fork+branch flags and validation, plus end-to-end rerun implementation.
  • Fix release-note markdown re-rendering so single-SIG notes retain [SIG ...], and ensure SIG changes trigger markdown rebuild.
  • Make DirectoryMapProvider.GetMapsForPR initialization thread-safe and add tests/docs for the new workflow.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
cmd/krel/cmd/release_notes.go Adds rerun subcommand, rerun implementation, and rerun-specific option validation.
cmd/krel/cmd/release_notes_test.go Adds unit tests for validateRerunOpts (required flags, normalization, defaults, warning).
pkg/notes/notes.go Fixes SIG label retention and markdown re-render when SIGs are mapped.
pkg/notes/notes_map.go Uses sync.Once to make DirectoryMapProvider map loading concurrency-safe.
pkg/notes/notes_map_test.go Adds concurrent test coverage for DirectoryMapProvider.GetMapsForPR.
docs/krel/release-notes.md Documents the new rerun workflow, flags, and examples.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1357 to +1365
// Parse --draft-pr-source-fork: if no "/" present, append "/sig-release"
if !strings.Contains(o.draftPRSourceFork, "/") {
o.draftPRSourceFork = o.draftPRSourceFork + "/" + git.DefaultGithubReleaseRepo
}

// Parse --draft-pr-push-fork: if no "/" present, append "/sig-release"
if o.draftPRPushFork != "" && !strings.Contains(o.draftPRPushFork, "/") {
o.draftPRPushFork = o.draftPRPushFork + "/" + git.DefaultGithubReleaseRepo
}
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validateRerunOpts normalizes --draft-pr-*-fork by checking for the presence of a "/", but it does not validate that the value is actually in org or org/repo form (e.g., it would accept org/, org/repo/extra, or org//repo). Since rerunDraftNotes assumes exactly two non-empty parts when splitting, add explicit validation (e.g., via git.ParseRepoSlug plus checks that both org and repo are non-empty) and return a clear error if the format is invalid.

Copilot uses AI. Check for mistakes.
// The release path inside the sig-release repository
releasePath := filepath.Join("releases", fmt.Sprintf("release-%d.%d", tagVersion.Major, tagVersion.Minor))
releaseDir := filepath.Join(sigReleaseRepo.Dir(), releasePath)

Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rerunDraftNotes does not check whether releaseDir exists before trying to create the work subdirectories. If the releases/release-x.y directory is missing for the provided tag, this will fail with a less actionable mkdir ... no such file or directory error. Mirror createDraftPR by checking helpers.Exists(releaseDir) and returning a targeted error (e.g. "could not find release directory ...").

Suggested change
if !helpers.Exists(releaseDir) {
return fmt.Errorf("could not find release directory %s", releaseDir)
}

Copilot uses AI. Check for mistakes.
Comment thread pkg/notes/notes_map_test.go
Comment on lines +446 to +461
sigReleaseRepo, err := git.CleanCloneGitHubRepo(
git.DefaultGithubOrg, git.DefaultGithubReleaseRepo,
false, true, opts,
)
if err != nil {
return fmt.Errorf("cloning kubernetes/sig-release: %w", err)
}

// Do NOT call Cleanup — preserve the local clone for user inspection

// Add the source fork as a remote named "source"
logrus.Infof("Adding remote 'source' for %s/%s", sourceOrg, sourceRepo)

// Use HTTPS for the source remote — it's a read-only fetch from a public fork
if err := sigReleaseRepo.AddRemote("source", sourceOrg, sourceRepo, false); err != nil {
return fmt.Errorf("adding source remote %s/%s: %w", sourceOrg, sourceRepo, err)
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rerunDraftNotes hard-codes HTTPS (useSSH=false) when cloning and when adding remotes. This ignores the inherited --use-ssh flag and can make pushes fail for users who rely on SSH auth. Thread releaseNotesOpts.useSSH (and likely releaseNotesOpts.updateRepo) through the clone/remote setup, at minimum for the push remote.

Copilot uses AI. Check for mistakes.
ApplyMap() used noteMap.PRBody only for change-detection logging but
never actually assigned it to the release note. Every other mapped
field (Text, Author, SIGs, Areas, Kinds, etc.) had an explicit
`rn.X = *noteMap.X` assignment, but PRBody was missing one.

This caused map entries with `pr_body: ""` to be silently ignored
during rerun: the note kept the full GitHub PR body fetched from
the API, which then appeared in the JSON output despite the map
intending to blank it out.

Add `rn.PRBody = *noteMap.PRBody` so the field is applied like all
other mapped fields. Combined with the existing `omitempty` JSON tag,
an empty pr_body in the map now correctly omits the field from output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/release-eng Issues or PRs related to the Release Engineering subproject cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/feature Categorizes issue or PR as related to a new feature. needs-priority release-note Denotes a PR that will be considered when it comes time to generate release notes. sig/release Categorizes an issue or PR as relevant to SIG Release. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants