Class ReleaseNotesSupport
Queries the GitHub REST API, categorizes issues by label into Fixes, Enhancements, and Internal sections, and produces markdown. JSON responses are parsed via SnakeYAML (JSON is valid YAML).
Used by both ws:release-notes (standalone) and
ike:release (integrated into the release workflow).
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordA repo released as part of a cascade — for the completion summary.static final recordA closed issue from a GitHub milestone.static final recordA GitHub issue reference parsed from a closing-keyword commit trailer.static interfaceSupplies the commit messages for one subproject's pin advance, soformatWorkspaceChangelog(Manifest, Manifest, SubprojectCommits)is unit-testable without a git checkout.static final recordA snapshot of milestone state for checkpoint testing context. -
Method Summary
Modifier and TypeMethodDescriptionstatic StringcascadeLabelOf(String topic) Recover thecascadeTopicLabel(Instant, ZoneId)from an in-progress topic name, so the terminal build can derive the completed name from the topic it found, ornulliftopicis not an in-progress cascade topic.static StringcascadeMetaEnv(String label) A shell-sourceable env snippet carrying the cascade label and in-progress topic name, so a notify step gets everything from oneike:release-changelogcall without recomputing the date in shell.static StringcascadeTopicLabel(Instant now, ZoneId zone) The human label for a cascade topic — the creating build's date in its own zone, with the zone abbreviation appended so it is unambiguous across the operators' time zones (e.g.static booleancloseMilestone(String repo, String milestone, org.apache.maven.api.plugin.Log log) Close a GitHub milestone by title.static intcloseReferencedIssues(File gitDir, String previousTag, String headRef, String fallbackRepo, org.apache.maven.api.plugin.Log log) Close every open issue referenced by a release-closing trailer (Fixes,Closes,Resolvesand grammatical variants) in commits betweenpreviousTagandheadRef.commitMessagesBetween(File gitDir, String fromRef, String toRef) The full commit messages (subject + body) infromRef..toRef, newest first — the inputformatChangelog(List)consumes.static StringcompletedCascadeTopic(String label, String parentVersion) The completed-cascade topic name, carrying the ike-parent version the terminal release establishes, e.g.static StringformatAsciidoc(String milestoneName, List<ReleaseNotesSupport.Issue> issues, String repo) Format release notes as AsciiDoc for site integration.static StringformatCascadeSummary(String parentVersion, List<ReleaseNotesSupport.CascadeMember> members) The cascade-completion summary the terminal build posts, naming the ike-parent version and listing every repo released in the cascade.static StringformatChangelog(List<String> commitMessages) Compose a "What's changed" changelog from a list of commit messages: one bullet per substantive commit (release machinery filtered out), each annotated with the full-form issue references parsed from its trailers.static StringformatNotes(String milestoneName, List<ReleaseNotesSupport.Issue> issues) Format release notes as Markdown for GitHub Release bodies.static StringformatNotes(String milestoneName, List<ReleaseNotesSupport.Issue> issues, List<CascadeBump> upgrades) Format release notes as Markdown, including a "Foundation upgrades" section for cascade version bumps (IKE-Network/ike-issues#706).static StringformatWorkspaceChangelog(File aggregatorGitDir, String fromRef, String toRef) Git-backedformatWorkspaceChangelog(Manifest, Manifest, SubprojectCommits): readsworkspace.yamlfrom the aggregator atfromRefandtoRef, and reads each changed subproject's commits from its worktree under the aggregator root.static StringformatWorkspaceChangelog(Manifest from, Manifest to, ReleaseNotesSupport.SubprojectCommits commits) Compose a per-subproject "What's changed" changelog for a workspace checkpoint by diffing each subproject'sworkspace.yamlpin between two checkpoints (IKE-Network/ike-issues#792).static Stringgenerate(String repo, String milestone, List<CascadeBump> upgrades, org.apache.maven.api.plugin.Log log) Generate release notes markdown for a named milestone, including a "Foundation upgrades" section for any cascade version bumps the release applied (IKE-Network/ike-issues#706).static StringGenerate release notes markdown for a named milestone.static PathgenerateAsciidocToFile(String repo, String milestone, Path outputDir, org.apache.maven.api.plugin.Log log) Generate release notes as AsciiDoc and write to a file, suitable for inclusion in the Maven site build.static PathgenerateFullHistory(String repo, Path outputDir, org.apache.maven.api.plugin.Log log) Generate a full release history page as AsciiDoc, covering all closed milestones and any closed issues without a milestone.static PathgenerateFullHistoryXhtml(String repo, Path outputDir, org.apache.maven.api.plugin.Log log) Generate a full release history as an XHTML fragment suitable for inclusion in the Maven site viageneratedSiteDirectory.static PathgenerateToFile(String repo, String milestone, List<CascadeBump> upgrades, File gitDir, String toRef, org.apache.maven.api.plugin.Log log) LikegenerateToFile(String, String, List, Log), but when the milestone and foundation upgrades yield nothing to report, falls back to the commit-message changelog forpreviousTag..toRefinstead of returningnull— so a standalone, un-milestoned release still describes itself from its own commits rather than degrading to GitHub's bare auto-generated notes (IKE-Network/ike-issues#775).static PathgenerateToFile(String repo, String milestone, List<CascadeBump> upgrades, org.apache.maven.api.plugin.Log log) Try to generate release notes (with a "Foundation upgrades" section sourced fromupgrades, #706), writing to a temp file suitable forgh release create --notes-file.static PathgenerateToFile(String repo, String milestone, org.apache.maven.api.plugin.Log log) Try to generate release notes, writing to a temp file suitable forgh release create --notes-file.static booleanhasAnyIssueTrailer(String commitMessage) ReturnstrueifcommitMessagecontains at least one IKE-COMMITS.md issue trailer (Fixes,Closes,Resolves,Refsand grammatical variants) with a#Nor<owner>/<repo>#Nreference.static StringinProgressCascadeTopic(String label) The open-cascade topic name for a label, e.g.static booleanisMachineryCommit(String subject) Whether a commit subject is release machinery (a cadence commit a release itself produces), and so should be filtered from a changelog.static Set<ReleaseNotesSupport.IssueRef> parseClosingTrailers(String commitMessages, String fallbackRepo) Parse closing-keyword trailers (e.g.,Fixes,Closes,Resolvesand grammatical variants) from a block of commit message text.parseIssueRefs(String commitMessage) Extract every issue reference from a commit message's trailers, in display form, de-duplicated and in first-seen order.static intremovePendingReleaseLabels(File gitDir, String previousTag, String headRef, String fallbackRepo, org.apache.maven.api.plugin.Log log) Remove thepending-releaselabel from every issue referenced by a release-closing trailer (Fixes,Closes,Resolvesand grammatical variants) in commits betweenpreviousTagandheadRef.snapshotMilestone(String repo, String milestone, org.apache.maven.api.plugin.Log log) Fetch all issues (open and closed) for a milestone, returning them categorized for a checkpoint testing context snapshot.
-
Method Details
-
generate
public static String generate(String repo, String milestone, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Generate release notes markdown for a named milestone.- Parameters:
repo- GitHub repository in owner/repo formatmilestone- milestone title (e.g., "ike-tooling v57")log- Maven logger (may be null for non-Maven callers)- Returns:
- formatted markdown, or null if the milestone is not found
- Throws:
org.apache.maven.api.plugin.MojoException- if the GitHub API call fails
-
generate
public static String generate(String repo, String milestone, List<CascadeBump> upgrades, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Generate release notes markdown for a named milestone, including a "Foundation upgrades" section for any cascade version bumps the release applied (IKE-Network/ike-issues#706).Returns
nullonly when there is genuinely nothing to report — no milestone and no foundation upgrades — so the caller can fall back to GitHub's auto-generated notes. A cascade-only rebuild (no milestone, but real upstream bumps) yields a non-null body announcing what it was rebuilt against, rather than a silent "no changes."- Parameters:
repo- GitHub repository in owner/repo formatmilestone- milestone title (e.g., "ike-docs v76")upgrades- the upstream-version bumps the release applied (may be empty)log- Maven logger (may be null for non-Maven callers)- Returns:
- formatted markdown, or null if there is nothing to report
- Throws:
org.apache.maven.api.plugin.MojoException- if the GitHub API call fails
-
generateToFile
public static Path generateToFile(String repo, String milestone, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Try to generate release notes, writing to a temp file suitable forgh release create --notes-file. Returns the path, or null if notes could not be generated.- Parameters:
repo- GitHub repository in owner/repo formatmilestone- milestone titlelog- Maven logger (may be null)- Returns:
- path to the temp file, or null on failure
- Throws:
org.apache.maven.api.plugin.MojoException- if the GitHub API call fails
-
generateToFile
public static Path generateToFile(String repo, String milestone, List<CascadeBump> upgrades, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Try to generate release notes (with a "Foundation upgrades" section sourced fromupgrades, #706), writing to a temp file suitable forgh release create --notes-file.- Parameters:
repo- GitHub repository in owner/repo formatmilestone- milestone titleupgrades- the upstream-version bumps the release applied (may be empty)log- Maven logger (may be null)- Returns:
- path to the temp file, or null if there was nothing to report
- Throws:
org.apache.maven.api.plugin.MojoException- if the GitHub API call fails
-
generateToFile
public static Path generateToFile(String repo, String milestone, List<CascadeBump> upgrades, File gitDir, String toRef, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException LikegenerateToFile(String, String, List, Log), but when the milestone and foundation upgrades yield nothing to report, falls back to the commit-message changelog forpreviousTag..toRefinstead of returningnull— so a standalone, un-milestoned release still describes itself from its own commits rather than degrading to GitHub's bare auto-generated notes (IKE-Network/ike-issues#775).Curated milestone notes still take precedence: the changelog is consulted only when
generate(String, String, Log)reports nothing. Machinery commits (release:,post-release:, merges) are filtered byformatChangelog(List). If the changelog is also empty (a genuine first release, or an unreadable shallow range) this returnsnulland the caller falls back togh --generate-notesas before.- Parameters:
repo- GitHub repository in owner/repo formatmilestone- milestone title (also the changelog heading)upgrades- the upstream-version bumps the release applied (may be empty)gitDir- the release repo's git root, for the changelog fallbacktoRef- the release ref/tag the changelog runs up to (e.g.v117)log- Maven logger (may be null)- Returns:
- path to the notes temp file, or
nullif there is genuinely nothing to report - Throws:
org.apache.maven.api.plugin.MojoException- if the GitHub API call fails
-
closeMilestone
public static boolean closeMilestone(String repo, String milestone, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Close a GitHub milestone by title. Warns if the milestone has open issues remaining.- Parameters:
repo- GitHub repository in owner/repo formatmilestone- milestone titlelog- Maven logger- Returns:
- true if closed, false if not found
- Throws:
org.apache.maven.api.plugin.MojoException- if the GitHub API call fails
-
snapshotMilestone
public static ReleaseNotesSupport.TestingContext snapshotMilestone(String repo, String milestone, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Fetch all issues (open and closed) for a milestone, returning them categorized for a checkpoint testing context snapshot.- Parameters:
repo- GitHub repository in owner/repo formatmilestone- milestone titlelog- Maven logger- Returns:
- snapshot with closed (ready to test) and open (in progress) issues, or null if milestone not found
- Throws:
org.apache.maven.api.plugin.MojoException- if the GitHub API call fails
-
removePendingReleaseLabels
public static int removePendingReleaseLabels(File gitDir, String previousTag, String headRef, String fallbackRepo, org.apache.maven.api.plugin.Log log) Remove thepending-releaselabel from every issue referenced by a release-closing trailer (Fixes,Closes,Resolvesand grammatical variants) in commits betweenpreviousTagandheadRef.Implements the "label = live state" half of the
pending-releasepattern defined inIKE-COMMITS.md: a commit lands marking an issueFixes …, the issue gets thepending-releaselabel as a not-yet-shipped marker, and when the release actually ships the label comes off sois:closed label:pending-releaseaccurately reflects fixes still awaiting a release.Trailer references must use the full
<owner>/<repo>#Nform; bare#Nreferences are resolved againstfallbackRepo.Pass
nullforpreviousTagto auto-derive it viagit describe --tags --abbrev=0 <headRef>^. If no previous tag is reachable, label removal is skipped with an informational message.Non-fatal: any failure (missing
ghCLI, missing label, network error, auth error) is logged and the method continues processing the remaining references. The release is already done at this point.- Parameters:
gitDir- the git working treepreviousTag- the previous release tag, or null to auto-deriveheadRef- the new release commit or tag (e.g., "v57")fallbackRepo-owner/repofor bare#Nrefs; may be null to ignore bare refslog- Maven logger (may be null)- Returns:
- number of issues from which the label was actually removed
-
commitMessagesBetween
The full commit messages (subject + body) infromRef..toRef, newest first — the inputformatChangelog(List)consumes.Reads via
git log, so it works on the release worktree (a full checkout). Returns an empty list if the range can't be read (e.g. a shallow checkout, or no previous tag).- Parameters:
gitDir- the git working treefromRef- the exclusive lower bound (e.g. the previous tag)toRef- the inclusive upper bound (e.g.HEADor a tag)- Returns:
- the commit messages, newest first
-
hasAnyIssueTrailer
ReturnstrueifcommitMessagecontains at least one IKE-COMMITS.md issue trailer (Fixes,Closes,Resolves,Refsand grammatical variants) with a#Nor<owner>/<repo>#Nreference.Used by release-time preflight to flag commits that violate the "every commit references a tracked issue" rule.
- Parameters:
commitMessage- commit message body, including subject and trailers- Returns:
- true if any issue trailer is present
-
parseClosingTrailers
public static Set<ReleaseNotesSupport.IssueRef> parseClosingTrailers(String commitMessages, String fallbackRepo) Parse closing-keyword trailers (e.g.,Fixes,Closes,Resolvesand grammatical variants) from a block of commit message text. Returns unique references in encounter order.Trailers without an explicit
owner/repoprefix are resolved againstfallbackRepo; iffallbackRepois null, bare references are ignored.Public so the workspace plugin (in a different module) can call this from
ws:checkpoint-publishper IKE-Network/ike-issues#394 — checkpoint reporting needs the same trailer parser that release-time label removal uses.- Parameters:
commitMessages- concatenated commit message bodiesfallbackRepo-owner/repofor bare references, or null- Returns:
- ordered set of unique issue references found
-
closeReferencedIssues
public static int closeReferencedIssues(File gitDir, String previousTag, String headRef, String fallbackRepo, org.apache.maven.api.plugin.Log log) Close every open issue referenced by a release-closing trailer (Fixes,Closes,Resolvesand grammatical variants) in commits betweenpreviousTagandheadRef.GitHub's native
Fixes #Nauto-close only fires when the issue lives in the commit's own repository. IKE centralizes issues in a separate tracker repo, so cross-repo trailers never auto-close — this redeems that trailer contract at release time so fixed issues don't dangle open (IKE-Network/ike-issues#799). Call it before milestone-notes generation so the notes reflect what shipped.Idempotent (issues already closed are skipped) and non-fatal: the artifact has already deployed at this point, so any failure is logged and the remaining references are still processed. Trailer references use the full
<owner>/<repo>#Nform; bare#Nreferences resolve againstfallbackRepo.- Parameters:
gitDir- the git working treepreviousTag- the previous release tag, or null to auto-deriveheadRef- the new release commit or tag (e.g., "v57")fallbackRepo-owner/repofor bare#Nrefs, or nulllog- Maven logger (may be null)- Returns:
- number of issues actually closed
-
generateFullHistory
public static Path generateFullHistory(String repo, Path outputDir, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Generate a full release history page as AsciiDoc, covering all closed milestones and any closed issues without a milestone. Each milestone becomes a section with categorized issues.- Parameters:
repo- GitHub repository in owner/repo formatoutputDir- directory to write release-notes.adoc intolog- Maven logger- Returns:
- the written file path, or null on failure
- Throws:
org.apache.maven.api.plugin.MojoException- if the GitHub API call fails
-
generateFullHistoryXhtml
public static Path generateFullHistoryXhtml(String repo, Path outputDir, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Generate a full release history as an XHTML fragment suitable for inclusion in the Maven site viageneratedSiteDirectory. The fragment is wrapped in a root<div>with an<h1>title, matching the format thatmaven-site-pluginexpects from generated content.- Parameters:
repo- GitHub repository in owner/repo formatoutputDir- directory to write release-notes.xhtml intolog- Maven logger- Returns:
- the written file path, or null on failure
- Throws:
org.apache.maven.api.plugin.MojoException- if the GitHub API call fails
-
generateAsciidocToFile
public static Path generateAsciidocToFile(String repo, String milestone, Path outputDir, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Generate release notes as AsciiDoc and write to a file, suitable for inclusion in the Maven site build. Returns the path, or null if the milestone is not found.- Parameters:
repo- GitHub repository in owner/repo formatmilestone- milestone titleoutputDir- directory to write the AsciiDoc file intolog- Maven logger- Returns:
- path to the written file, or null on failure
- Throws:
org.apache.maven.api.plugin.MojoException- if the GitHub API call fails
-
formatAsciidoc
public static String formatAsciidoc(String milestoneName, List<ReleaseNotesSupport.Issue> issues, String repo) Format release notes as AsciiDoc for site integration.- Parameters:
milestoneName- the milestone titleissues- closed issues from the milestonerepo- GitHub repository (e.g., "IKE-Network/ike-issues")- Returns:
- AsciiDoc-formatted release notes
-
formatNotes
Format release notes as Markdown for GitHub Release bodies.- Parameters:
milestoneName- the milestone titleissues- closed issues from the milestone- Returns:
- Markdown-formatted release notes
-
formatNotes
public static String formatNotes(String milestoneName, List<ReleaseNotesSupport.Issue> issues, List<CascadeBump> upgrades) Format release notes as Markdown, including a "Foundation upgrades" section for cascade version bumps (IKE-Network/ike-issues#706).- Parameters:
milestoneName- the milestone titleissues- closed issues from the milestoneupgrades- the upstream-version bumps the release applied (may be empty)- Returns:
- Markdown-formatted release notes
-
isMachineryCommit
Whether a commit subject is release machinery (a cadence commit a release itself produces), and so should be filtered from a changelog.- Parameters:
subject- the commit subject line- Returns:
truefor release/merge/post-release/bump machinery
-
parseIssueRefs
Extract every issue reference from a commit message's trailers, in display form, de-duplicated and in first-seen order.The full
owner/repo#Nform is preserved verbatim (so a consumer — a Zulip linkifier, a GitHub release body — links it in whatever repo it lives, with no tracker assumed). A bare#Nreference is returned as#N: its repository is ambiguous and is deliberately not guessed.- Parameters:
commitMessage- the full commit message (subject + body)- Returns:
- the referenced issues in display form, e.g.
["IKE-Network/ike-issues#705", "ikmdev/komet-desktop#12"]
-
formatChangelog
Compose a "What's changed" changelog from a list of commit messages: one bullet per substantive commit (release machinery filtered out), each annotated with the full-form issue references parsed from its trailers.Repo-agnostic: no issue tracker is assumed or hardcoded — each reference carries its own
owner/repofrom the commit trailer (IKE-COMMITS.mdmandates the full form for exactly this cross-repo reason). A trailing bare-#Nparenthetical on the subject is stripped so refs aren't shown twice.Returns the empty string when nothing substantive remains — the caller omits the whole "What's changed" block in that case.
- Parameters:
commitMessages- full commit messages (subject + body) in display order (typically newest-first from agit log/compare range)- Returns:
- the Markdown bullet list, or
""if empty
-
formatWorkspaceChangelog
public static String formatWorkspaceChangelog(Manifest from, Manifest to, ReleaseNotesSupport.SubprojectCommits commits) Compose a per-subproject "What's changed" changelog for a workspace checkpoint by diffing each subproject'sworkspace.yamlpin between two checkpoints (IKE-Network/ike-issues#792).This is the workspace-aware counterpart to the single-repo
formatChangelog(List)path. Run at an aggregator checkpoint it sees every subproject's code change, not just the aggregator's ownworkspace.yaml/merge commits — the gap that made the checkpoint Zulip note omit subproject changes while the GitHub release body (computed per-subproject from the pins) showed them.For each subproject in
towhose pinnedshadiffers from itsfrompin, the suppliedReleaseNotesSupport.SubprojectCommitsyields that subproject's commit messages, whichformatChangelog(List)filters and formats into a### <name>section with a GitHub compare link. Subprojects with an unchanged pin, no prior pin needed for a link, or no substantive commits are handled gracefully; a subproject contributing no substantive commits is omitted entirely.- Parameters:
from- the previous checkpoint's manifest, ornullto treat every subproject as newto- the current checkpoint's manifestcommits- supplies each changed subproject's commit messages- Returns:
- the per-subproject Markdown (subprojects in manifest order), or
""when nothing substantive changed
-
formatWorkspaceChangelog
Git-backedformatWorkspaceChangelog(Manifest, Manifest, SubprojectCommits): readsworkspace.yamlfrom the aggregator atfromRefandtoRef, and reads each changed subproject's commits from its worktree under the aggregator root. A subproject with no present worktree (or no prior pin) contributes no bullets and is therefore omitted.Returns
""whentoRefhas noworkspace.yaml(not an aggregator), so the caller can fall back to the single-repo changelog.- Parameters:
aggregatorGitDir- the workspace aggregator git working treefromRef- the previous checkpoint ref/tagtoRef- the current ref (a checkpoint tag orHEAD)- Returns:
- the per-subproject Markdown, or
""when unavailable
-
cascadeTopicLabel
The human label for a cascade topic — the creating build's date in its own zone, with the zone abbreviation appended so it is unambiguous across the operators' time zones (e.g."2026-06-19 PDT").Computed once, by the build that creates the topic; never recomputed by other builds (they find the open topic instead), so there is no need for a midnight-safe rollover.
- Parameters:
now- the creating build's instantzone- the release server's zone (e.g.ZoneId.systemDefault())- Returns:
- the label, e.g.
"2026-06-19 PDT"
-
inProgressCascadeTopic
The open-cascade topic name for a label, e.g."2026-06-19 PDT Release Cascade — in progress". The"— in progress"suffix is the correlation key: at most one such topic exists at a time (releases run serially and completion renames it away), so a build finds the cascade by matching it.- Parameters:
label- thecascadeTopicLabel(Instant, ZoneId)value- Returns:
- the in-progress topic name
-
completedCascadeTopic
The completed-cascade topic name, carrying the ike-parent version the terminal release establishes, e.g."2026-06-19 PDT ike-parent Release Cascade v66".- Parameters:
label- thecascadeTopicLabel(Instant, ZoneId)valueparentVersion- the released ike-parent (ike-platform) version- Returns:
- the completed topic name
-
cascadeLabelOf
Recover thecascadeTopicLabel(Instant, ZoneId)from an in-progress topic name, so the terminal build can derive the completed name from the topic it found, ornulliftopicis not an in-progress cascade topic.- Parameters:
topic- a Zulip topic name- Returns:
- the label, or
nullif it isn't an in-progress cascade topic
-
cascadeMetaEnv
A shell-sourceable env snippet carrying the cascade label and in-progress topic name, so a notify step gets everything from oneike:release-changelogcall without recomputing the date in shell. Values are single-quoted; safe because a label is only digits, hyphens, spaces, and a zone abbreviation — never a quote.CASCADE_LABEL='2026-06-19 PDT' CASCADE_TOPIC_INPROGRESS='2026-06-19 PDT Release Cascade — in progress'
- Parameters:
label- thecascadeTopicLabel(Instant, ZoneId)value- Returns:
- the env-file content (newline-terminated)
-
formatCascadeSummary
public static String formatCascadeSummary(String parentVersion, List<ReleaseNotesSupport.CascadeMember> members) The cascade-completion summary the terminal build posts, naming the ike-parent version and listing every repo released in the cascade.- Parameters:
parentVersion- the released ike-parent (ike-platform) versionmembers- the repos released, in cascade order- Returns:
- the Markdown summary
-