Class WsReleaseDraftMojo
- All Implemented Interfaces:
org.apache.maven.api.plugin.Mojo
- Direct Known Subclasses:
WsReleasePublishMojo
Scans checked-out components for commits since their last release tag. The release set is the union of:
- source-changed — subprojects with unreleased commits since their last tag (or never released);
- transitive downstream — every checked-out subproject that depends, directly or transitively, on a source-changed subproject. Catches workspaces where a mid-graph change forces downstream re-publish even though those downstream subprojects have no source changes of their own.
<X.version> property)
to the upstream's current target version — this-cycle's new version
if the upstream is releasing this cycle, otherwise the upstream's
current published version on disk. All upstream bumps for a single
subproject batch into one commit (never two).
Catch-up never expands the release set: a subproject with stale upstream properties but no source changes and no upstream releasing in this cycle is not pulled in.
If catch-up alignment fails for any subproject (POM rewrite or
commit error), the release halts at that subproject with a
MojoException naming the failing subproject and property —
never silently continues.
What it does, per subproject:
- Detect latest release tag (
v*) - Check for commits since that tag
- If source-changed or cascade-induced: catch-up upstream version
references in this subproject's POM (single commit), then run
mvn ike:release-publishin that subproject's directory
Workspace-level preflight (applied before any subproject is released):
PreflightCondition.WORKING_TREE_CLEAN— every checked-out subproject (and the workspace root) must have no uncommitted changes.PreflightCondition.NO_SNAPSHOT_PROPERTIES— no root POM may carry a<properties>value ending in-SNAPSHOT. Catches theike-parent-105.pomleakage class of bug at its source (see issues #175, #177).
Per-subproject preflight (javadoc warnings, git push auth, SSH
proxy, gh CLI auth, Maven wrapper, post-mutation SNAPSHOT
<version> scan) runs inside each
ike:release-publish invocation — see ReleaseDraftMojo
in the ike-maven-plugin module. This ensures the same
gates apply whether a release is invoked workspace-level or
directly inside a single subproject.
The cascade is self-limiting: only checked-out components with changes since their last release are candidates. Components not present in the aggregator are not considered.
mvn ws:release-draft # preview what would be released
mvn ws:release-publish # release all release-pending components
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstatic StringbuildPreReleaseCheckpointYaml(String name, String timestamp, List<String[]> componentData) Build pre-release checkpoint YAML content from pre-gathered subproject data.computeReleaseSet(network.ike.workspace.WorkspaceGraph graph, Set<String> sourceChanged) Compute the release set: source-changed subprojects union the transitive downstream cascade of each.protected booleanPrompt the user with a yes/no question, accepting "y"/"yes"/"n"/"no" (case-insensitive).final voidexecute()Run the goal and write its report.static StringextractVersionFromPom(String pomContent) Extract the project's own<version>value from POM XML content.protected org.apache.maven.api.plugin.LoggetLog()Access the Maven logger.protected network.ike.plugin.support.IkePrompterTheIkePrompterfor this goal — built lazily from the session's interactive flag, or the test-injected instance.protected org.apache.maven.api.SessionAccess the Maven session injected by Maven 4's plugin DI.protected StringGet the current branch of a subproject directory.protected StringgitShortSha(File subprojectDir) Get the short SHA of HEAD for a subproject directory.protected StringRungit status --porcelainon a subproject directory and return the output (empty string = clean).protected StringFormat a goal header line using the workspace name.static booleanisReleaseCadenceCommit(String subject) Test whether a commit subject matches a release-cadence pattern (would be filtered out bymeaningfulCommitsSinceTag(File, String)).protected booleanCheck whether a workspace.yaml exists in the directory hierarchy.protected network.ike.workspace.WorkspaceGraphLoad the manifest and build the workspace graph.protected StringrequireParam(String currentValue, String propertyName, String promptLabel) Prompt the user interactively for a required parameter when it was not supplied on the command line.protected PathResolve the manifest path — explicit parameter, or search upward.static StringresolveMvnCommand(File subDir) Resolve the Maven executable for a subproject directory.protected WorkspaceReportSpecrunGoal()Run this goal's work and return the report it produced.protected StringselectFromList(String label, List<String> options) Prompt the user to pick from a numbered list.protected voidsetLog(org.apache.maven.api.plugin.Log log) Replace the logger.protected static network.ike.workspace.FeatureNamevalidateFeatureName(String feature) Validate a feature-name string withFeatureName.of(String)and surface any rule violation as a cleanMojoException(the rawIllegalArgumentExceptionwould otherwise bubble up as a Maven internal error).protected static network.ike.workspace.MavenVersionvalidateMavenVersion(String version) Validate a Maven version string withMavenVersion.of(String)and surface any rule violation as aMojoException(ike-issues#295).protected static network.ike.workspace.SubprojectNamevalidateSubprojectName(String subproject) Validate a subproject-name string withSubprojectName.of(String)and surface any rule violation as aMojoException(ike-issues#295).protected StringRead the workspace name from the root POM's artifactId.protected FileResolve the workspace root directory (parent of workspace.yaml).
-
Constructor Details
-
WsReleaseDraftMojo
public WsReleaseDraftMojo()Creates this goal instance.
-
-
Method Details
-
runGoal
Run this goal's work and return the report it produced.Implementations do the goal's actual work here and return a
WorkspaceReportSpec— the goal identity and the Markdown body. The base class resolves the workspace root and writes the file. A goal cannot be implemented without producing a report, which is the structural fix for the missing-report bug class (IKE-Network/ike-issues#413 / #407).On failure, throw
MojoExceptionas usual — a failed goal produces no report, and Maven surfaces the exception.- Returns:
- the report this goal produced (never
null) - Throws:
org.apache.maven.api.plugin.MojoException- if the goal fails
-
computeReleaseSet
public static Set<String> computeReleaseSet(network.ike.workspace.WorkspaceGraph graph, Set<String> sourceChanged) Compute the release set: source-changed subprojects union the transitive downstream cascade of each.This is a pure function over the workspace graph and the set of source-changed subprojects. Cascade is computed via
WorkspaceGraph.cascade(String)(BFS on reverse edges). Order in the returned set follows the graph's topological sort.By construction, the release set contains every member of
sourceChangedplus everything that depends on any of them (directly or transitively). It never contains a subproject that has neither a source change nor a release-set upstream — stale properties alone cannot expand the release set (see #192).- Parameters:
graph- the workspace dependency graphsourceChanged- subproject names whose own commits warrant a release this cycle- Returns:
- release set in topological order (dependencies first)
-
isReleaseCadenceCommit
Test whether a commit subject matches a release-cadence pattern (would be filtered out bymeaningfulCommitsSinceTag(File, String)). Public for unit testing.- Parameters:
subject- the commit subject line- Returns:
truewhen the subject is cadence-emitted
-
extractVersionFromPom
Extract the project's own<version>value from POM XML content.Strips any
<parent>...</parent>block before scanning so we don't accidentally return the inherited parent's version for projects that declare a parent (like the workspace root pom inheritingike-parent). Then takes the first remaining<version>, which is the project's own.- Parameters:
pomContent- raw POM XML as a string- Returns:
- the version string, or
"unknown"if not found
-
buildPreReleaseCheckpointYaml
public static String buildPreReleaseCheckpointYaml(String name, String timestamp, List<String[]> componentData) Build pre-release checkpoint YAML content from pre-gathered subproject data.This is a pure function with no git or I/O dependencies, suitable for direct unit testing.
- Parameters:
name- checkpoint nametimestamp- ISO-8601 UTC timestampcomponentData- list of[name, branch, sha, version, modified]arrays for each present subproject- Returns:
- YAML checkpoint content
-
resolveMvnCommand
Resolve the Maven executable for a subproject directory.Checks for
mvnw(executable) andmvnw.cmdin the given directory. Falls back to"mvn"from the system PATH if no wrapper is found.- Parameters:
subDir- the subproject directory to check- Returns:
- absolute path to mvnw/mvnw.cmd, or
"mvn"
-
getLog
protected org.apache.maven.api.plugin.Log getLog()Access the Maven logger.- Returns:
- the logger instance
-
getSession
protected org.apache.maven.api.Session getSession()Access the Maven session injected by Maven 4's plugin DI.- Returns:
- the injected session (may be
nullin unit tests)
-
setLog
protected void setLog(org.apache.maven.api.plugin.Log log) Replace the logger. Used when a mojo is constructed directly (not via Maven's DI container) and so never had a logger injected —WsSyncMojodrivesPullWorkspaceMojoandPushMojoinstances it created itself.- Parameters:
log- the replacement logger
-
getPrompter
protected network.ike.plugin.support.IkePrompter getPrompter()TheIkePrompterfor this goal — built lazily from the session's interactive flag, or the test-injected instance. Callers that pass it to a static helper (e.g.FeatureFinishSupport.promptStaleBranchCleanup(File, List, String, String, IkePrompter, Log)) use this.- Returns:
- the prompter (never
null)
-
loadGraph
protected network.ike.workspace.WorkspaceGraph loadGraph()Load the manifest and build the workspace graph.- Returns:
- the workspace dependency graph
- Throws:
org.apache.maven.api.plugin.MojoException- if the manifest cannot be read
-
resolveManifest
Resolve the manifest path — explicit parameter, or search upward.- Returns:
- path to the workspace manifest file
- Throws:
org.apache.maven.api.plugin.MojoException- if the manifest cannot be found
-
workspaceRoot
Resolve the workspace root directory (parent of workspace.yaml).- Returns:
- the workspace root directory
- Throws:
org.apache.maven.api.plugin.MojoException- if the manifest cannot be found
-
gitStatus
-
gitBranch
-
gitShortSha
-
isWorkspaceMode
protected boolean isWorkspaceMode()Check whether a workspace.yaml exists in the directory hierarchy. Does not throw — returns false if no manifest is found.- Returns:
- true if running inside a workspace, false for a bare repo
-
requireParam
Prompt the user interactively for a required parameter when it was not supplied on the command line.Delegates to the
IkePrompter(IKE-Network/ike-issues#385): an inline prompt on a real terminal, an own-line prompt in a piped IDE runner. In batch mode it throws a clear error directing the user to pass the property explicitly.- Parameters:
currentValue- the value from the@Parameterfield (may be null)propertyName- the-Dproperty name (for the error message)promptLabel- human-readable label shown in the prompt; callers pass it without trailing punctuation (a": "separator is appended here)- Returns:
- the resolved value — either the original or user-supplied
- Throws:
org.apache.maven.api.plugin.MojoException- if no value can be obtained
-
validateFeatureName
Validate a feature-name string withFeatureName.of(String)and surface any rule violation as a cleanMojoException(the rawIllegalArgumentExceptionwould otherwise bubble up as a Maven internal error). Use this anywhere a feature name leaves the-Dfeature=command-line boundary (ike-issues#205).- Parameters:
feature- the candidate feature name (must already be resolved — i.e. non-null afterrequireParam(String, String, String)or auto-detection)- Returns:
- the validated
FeatureNamevalue - Throws:
org.apache.maven.api.plugin.MojoException- iffeaturefails theFeatureNamesyntax rules
-
validateSubprojectName
Validate a subproject-name string withSubprojectName.of(String)and surface any rule violation as aMojoException(ike-issues#295).- Parameters:
subproject- the candidate subproject name (already resolved — non-null afterrequireParam(String, String, String)or POM derivation)- Returns:
- the validated
SubprojectNamevalue - Throws:
org.apache.maven.api.plugin.MojoException- ifsubprojectfails theSubprojectNamesyntax rules
-
validateMavenVersion
Validate a Maven version string withMavenVersion.of(String)and surface any rule violation as aMojoException(ike-issues#295). Perfeedback_no_semver_assumptionthe validator accepts single-segment monotonic, semver-like, calendar-based, and branch-qualified versions; it does not enforce semver.- Parameters:
version- the candidate version string- Returns:
- the validated
MavenVersionvalue - Throws:
org.apache.maven.api.plugin.MojoException- ifversionfails theMavenVersionsyntax rules
-
confirm
Prompt the user with a yes/no question, accepting "y"/"yes"/"n"/"no" (case-insensitive). When invoked in a non-interactive context, the default is used.- Parameters:
label- the question to display (without trailing punctuation)defaultYes- whethertrue(yes) is the default- Returns:
truefor yes,falsefor no- Throws:
org.apache.maven.api.plugin.MojoException- if no answer can be obtained
-
selectFromList
Prompt the user to pick from a numbered list. Returns the chosen option, ornullwhen the list is empty.- Parameters:
label- prompt header (printed via the Prompter as a message)options- ordered list of choices- Returns:
- the chosen option, or
nullifoptionsis empty - Throws:
org.apache.maven.api.plugin.MojoException- if no valid choice can be obtained
-
workspaceName
Read the workspace name from the root POM's artifactId. Falls back to "Workspace" if the POM cannot be read.- Returns:
- the workspace name derived from the root POM artifactId
-
header
-
execute
public final void execute() throws org.apache.maven.api.plugin.MojoExceptionRun the goal and write its report.This method is
final: everyws:*goal follows the same template — do the work, then write exactly one report. Subclasses supply the work and the report content by implementingrunGoal(); they cannot overrideexecute()to skip the report. That is what makes report-writing structural — a goal that compiles necessarily writes a report, so the #407 bug class (a goal runs fine but silently writes none) becomes compiler-impossible (IKE-Network/ike-issues#413).The report lands in its per-goal file at the workspace root (
ws꞉goal-name.md);WorkspaceReportself-heals the nearest.gitignoreso reports never land in git. A-publishrun does not delete the matching-draftreport — both are timestamped history (ike-issues#413).- Specified by:
executein interfaceorg.apache.maven.api.plugin.Mojo- Throws:
org.apache.maven.api.plugin.MojoException- if the goal fails
-