Class ReleaseSupport
All subprocess invocations use ProcessBuilder — no
library dependencies beyond the JDK and maven-plugin-api.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordA command paired with a display label for parallel execution. -
Field Summary
Fields -
Method Summary
Modifier and TypeMethodDescriptionbakeAliasIndirections(File gitRoot, org.apache.maven.api.plugin.Log log) Bake__ALIAS-driven indirections into all POM files under the git root.static StringbranchToSitePath(String branch) Convert a git branch name to a safe site path segment.static voidcleanRemoteSiteDir(File workDir, org.apache.maven.api.plugin.Log log, String remotePath) Remove a directory tree on the site server via SSH.static voidcleanRemoteSiteDir(File workDir, org.apache.maven.api.plugin.Log log, String remotePath, String... sshPrefix) Overload accepting an explicit SSH command prefix — package-private for testing against containers.static voidcopyDirectory(Path source, Path target) Recursively copy a directory tree.static voidcopyDirectoryExcludingTopLevelVersionDirs(Path source, Path target) Recursively copy a directory tree, excluding top-level subdirectories whose names look like release versions.static StringcurrentBranch(File workDir) Get the current git branch name.static voiddeleteDirectory(Path dir) Recursively delete a directory and all its contents.static StringderiveCheckpointVersion(String pomVersion, File gitRoot) Derive a checkpoint version from the current POM version.static StringderiveNextSnapshot(String releaseVersion) Derive the next SNAPSHOT version by incrementing the last numeric segment.static StringderiveReleaseVersion(String snapshotVersion) Derive the release version from a SNAPSHOT version.static PathdetectAggregatedStaging(Path source, String projectId) Detect aggregated-reactor staging where the project's own site lives alongside multiple sibling subdirs (ike-issues#351).static PathdetectHttpsUrlStaging(Path source, String projectId) Detect URL-as-path staging wheresite:stagehas mapped anhttps://-form<site><url>to a literal directory tree undertarget/staging/(ike-issues#359).static PathdetectParentArtifactNesting(Path source, String projectId) Detect parent-artifactId staging nesting (ike-issues#342).static voidRun a command, inherit IO so output streams to the Maven console.static StringexecCapture(File workDir, String... command) Run a command and capture stdout as a trimmed String.static StringexecCaptureAndLog(File workDir, org.apache.maven.api.plugin.Log log, String... command) Run a command, streaming output through Maven's logger AND capturing the full output as a String.static voidexecParallel(File workDir, org.apache.maven.api.plugin.Log log, ReleaseSupport.LabeledTask... tasks) Run multiple commands concurrently, prefixing each line of output with the task's label (e.g.,[nexus] ...).findPomFiles(File gitRoot) Find allpom.xmlfiles under the git root, excludingtarget/directories and the.mvn/directory.findSubmoduleSiteDirs(Path layer, Path exclude) Find one-level-deep subdirectories underlayerthat look like rendered Maven-site outputs (have anindex.htmlat the top), excludingexcludeitself (the workspace's own subtree, already published at the gh-pages root).static StringgetRemoteUrl(File workDir, String remoteName) Return the URL of a named git remote, or null if the remote does not exist.static voidgitAddFiles(File gitRoot, org.apache.maven.api.plugin.Log log, List<File> files) Stage a list of files withgit add.static FileGet the git repository root directory.static booleanCheck whether a named git remote exists.static booleanisEmptyDirectory(Path dir) Check whether a directory has no entries.static booleanisMacOS()Check if the current platform is macOS.static booleanCheck if the current platform is Windows.static voidpublishProjectSiteToGhPages(Path stagingDir, String repoUrl, org.apache.maven.api.plugin.Log log, String projectId, String version) Publish a project's rendered site to its repo'sgh-pagesbranch using the hybrid structure (ike-issues#312, #332).static StringreadPomArtifactId(File pomFile) Read the project's own<artifactId>from a POM file, skipping any<artifactId>inside the<parent>block.static StringreadPomDescription(File pomFile) Read the<description>element from a POM file.static StringreadPomGroupId(File pomFile) Read the project's own<groupId>from a POM file, skipping any<groupId>inside the<parent>block.static StringreadPomName(File pomFile) Read the<name>element from a POM file.static StringreadPomProperty(File pomFile, String propertyName) Read a<properties>value from a POM file by element name.static StringreadPomVersion(File pomFile) Read the project's own<version>from a POM file, skipping any<version>inside the<parent>block.replaceProjectVersionRefs(File gitRoot, String version, org.apache.maven.api.plugin.Log log) Replace all occurrences of${project.version}with a literal version string in every POM file under the git root.static voidrequireCleanWorktree(File workDir) Assert that the git working tree is clean (no staged or unstaged changes).static FileresolveMavenWrapper(File gitRoot, org.apache.maven.api.plugin.Log log) Resolve the Maven executable.restoreBackups(File gitRoot, org.apache.maven.api.plugin.Log log) Restore all POM files from their.ike-backupcopies and delete the backup files.static voidrouteSubprocessLine(org.apache.maven.api.plugin.Log log, String line) Route a subprocess output line through Maven's logger at the correct level.static voidrouteSubprocessLine(org.apache.maven.api.plugin.Log log, String line, String prefix) Route a subprocess output line through Maven's logger with a prefix.static voidsetPomVersion(File pomFile, String oldVersion, String newVersion) Replace the project's own<version>old</version>with<version>new</version>, skipping any version inside the<parent>block.static StringsiteDiskPath(String projectId, String siteType, String subPath) Resolve the on-disk site path for a given project, type, and optional subdirectory.static StringsiteStagingPath(String diskPath) Return the staging path for a site deploy (final path + ".staging").static StringsiteStagingUrl(String targetUrl) Return the scpexe URL for the staging directory.static voidstampOutputTimestamp(File pomFile, String newTimestamp, org.apache.maven.api.plugin.Log log) Stamp<project.build.outputTimestamp>in the root POM tonewTimestamp, enabling reproducible builds for the release.static voidswapRemoteSiteDir(File workDir, org.apache.maven.api.plugin.Log log, String remotePath) Atomically swap a newly deployed site into place on the server.static voidswapRemoteSiteDir(File workDir, org.apache.maven.api.plugin.Log log, String remotePath, String... sshPrefix) Overload accepting an explicit SSH command prefix — package-private for testing against containers.static booleanCheck whether a git tag exists (locally).unbakeAliasIndirections(File gitRoot, org.apache.maven.api.plugin.Log log) Unbake__ALIAS-driven indirections from all POM files under the git root — the inverse ofbakeAliasIndirections(File, Log).static voidupdateLatestSymlink(File workDir, org.apache.maven.api.plugin.Log log, String remotePath) Update thelatestsymlink alongside a version-prefixed site deploy so that<site-base>/latest/always points at the most recent release (ike-issues#303).static voidupdateLatestSymlink(File workDir, org.apache.maven.api.plugin.Log log, String remotePath, String... sshPrefix) Overload accepting an explicit SSH command prefix — package-private for testing against containers.static StringupdateVersionProperty(String pomContent, String propertyName, String newVersion) Update a named Maven property in POM content.static voidvalidateRemotePath(String remotePath) Validate that a remote path is safe for deletion operations.
-
Field Details
-
SITE_DISK_BASE
-
SITE_SSH_HOST
-
-
Method Details
-
isMacOS
public static boolean isMacOS()Check if the current platform is macOS.- Returns:
trueif running on macOS or Darwin
-
exec
public static void exec(File workDir, org.apache.maven.api.plugin.Log log, String... command) throws org.apache.maven.api.plugin.MojoException Run a command, inherit IO so output streams to the Maven console. Throws on non-zero exit code.- Parameters:
workDir- working directory for the subprocesslog- Maven logger for output routingcommand- the command and arguments to execute- Throws:
org.apache.maven.api.plugin.MojoException- if the command exits non-zero or cannot be started
-
routeSubprocessLine
Route a subprocess output line through Maven's logger at the correct level. Strips Maven log prefixes ([INFO], [WARNING], [ERROR]) from the line to avoid redundant nesting.- Parameters:
log- Maven loggerline- raw subprocess output line
-
routeSubprocessLine
public static void routeSubprocessLine(org.apache.maven.api.plugin.Log log, String line, String prefix) Route a subprocess output line through Maven's logger with a prefix.Recognized prefixes are stripped and routed to the matching Maven log level. Unrecognized lines are routed to
infoso that subprocess activity (especiallygitoperations during release) is visible in the build log.Specific git error patterns (
fatal:,error:,remote: error:,! [rejected],! [remote rejected]) are detected explicitly and routed toerrorso they cannot be missed in the log. Earlier behavior routed all unprefixed output todebug, which silently hid gh-pages push failures (seeIKE-Network/ike-issues#329).- Parameters:
log- Maven loggerline- raw subprocess output lineprefix- string prepended to each routed line
-
execParallel
public static void execParallel(File workDir, org.apache.maven.api.plugin.Log log, ReleaseSupport.LabeledTask... tasks) throws org.apache.maven.api.plugin.MojoException Run multiple commands concurrently, prefixing each line of output with the task's label (e.g.,[nexus] ...).Spawns virtual threads to read stdout/stderr from each process. All processes run to completion even if one fails — the exception reports which task(s) failed.
- Parameters:
workDir- working directory for each subprocesslog- Maven logger for output routingtasks- the labeled tasks to run concurrently- Throws:
org.apache.maven.api.plugin.MojoException- if any task fails or execution is interrupted
-
execCapture
public static String execCapture(File workDir, String... command) throws org.apache.maven.api.plugin.MojoException Run a command and capture stdout as a trimmed String. Throws on non-zero exit code.- Parameters:
workDir- working directory for the subprocesscommand- the command and arguments to execute- Returns:
- trimmed stdout output
- Throws:
org.apache.maven.api.plugin.MojoException- if the command exits non-zero or cannot be started
-
execCaptureAndLog
public static String execCaptureAndLog(File workDir, org.apache.maven.api.plugin.Log log, String... command) throws org.apache.maven.api.plugin.MojoException Run a command, streaming output through Maven's logger AND capturing the full output as a String. Throws on non-zero exit.- Parameters:
workDir- working directory for the subprocesslog- Maven logger for real-time outputcommand- the command and arguments to execute- Returns:
- the complete stdout+stderr output as a trimmed string
- Throws:
org.apache.maven.api.plugin.MojoException- if the command exits non-zero
-
readPomVersion
Read the project's own<version>from a POM file, skipping any<version>inside the<parent>block.- Parameters:
pomFile- the POM file to read- Returns:
- the version string
- Throws:
org.apache.maven.api.plugin.MojoException- if the file cannot be read or has no version
-
stampOutputTimestamp
public static void stampOutputTimestamp(File pomFile, String newTimestamp, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Stamp<project.build.outputTimestamp>in the root POM tonewTimestamp, enabling reproducible builds for the release.The property must already exist in the POM (inherited from ike-parent). If it is absent this method is a no-op with a warning.
- Parameters:
pomFile- the root POM to updatenewTimestamp- ISO-8601 UTC timestamp, e.g.2026-03-30T12:00:00Zlog- Maven log (used for warnings only)- Throws:
org.apache.maven.api.plugin.MojoException- if the file cannot be read or written
-
setPomVersion
public static void setPomVersion(File pomFile, String oldVersion, String newVersion) throws org.apache.maven.api.plugin.MojoException Replace the project's own<version>old</version>with<version>new</version>, skipping any version inside the<parent>block.- Parameters:
pomFile- the POM file to updateoldVersion- the current version string to replacenewVersion- the new version string- Throws:
org.apache.maven.api.plugin.MojoException- if the version is not found or the file cannot be updated
-
isWindows
public static boolean isWindows()Check if the current platform is Windows.- Returns:
trueifos.namecontains "win"
-
resolveMavenWrapper
public static File resolveMavenWrapper(File gitRoot, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Resolve the Maven executable. Prefers the Maven wrapper (mvnwon Unix,mvnw.cmdon Windows) at the git root; falls back to system Maven located viawhichon Unix orwhereon Windows.- Parameters:
gitRoot- the git repository root directorylog- Maven logger- Returns:
- the resolved Maven executable
- Throws:
org.apache.maven.api.plugin.MojoException- if neither wrapper nor system Maven is found
-
gitRoot
Get the git repository root directory.- Parameters:
startDir- any directory inside the repository- Returns:
- the repository root directory
- Throws:
org.apache.maven.api.plugin.MojoException- if git rev-parse fails
-
requireCleanWorktree
public static void requireCleanWorktree(File workDir) throws org.apache.maven.api.plugin.MojoException Assert that the git working tree is clean (no staged or unstaged changes).- Parameters:
workDir- any directory inside the repository- Throws:
org.apache.maven.api.plugin.MojoException- if the working tree has uncommitted changes
-
currentBranch
-
hasRemote
-
getRemoteUrl
Return the URL of a named git remote, or null if the remote does not exist.- Parameters:
workDir- any directory inside the repositoryremoteName- the remote name (typically"origin")- Returns:
- the remote URL, or null if the remote is absent
-
deriveReleaseVersion
-
deriveNextSnapshot
-
updateVersionProperty
public static String updateVersionProperty(String pomContent, String propertyName, String newVersion) Update a named Maven property in POM content. Replaces<propertyName>oldValue</propertyName>with<propertyName>newVersion</propertyName>.- Parameters:
pomContent- the POM file content as a stringpropertyName- the Maven property name (e.g., "ike-bom.version")newVersion- the new version value- Returns:
- the updated POM content (unchanged if property not found)
-
findPomFiles
public static List<File> findPomFiles(File gitRoot) throws org.apache.maven.api.plugin.MojoException Find allpom.xmlfiles under the git root, excludingtarget/directories and the.mvn/directory.- Parameters:
gitRoot- the git repository root directory- Returns:
- list of discovered POM files
- Throws:
org.apache.maven.api.plugin.MojoException- if the file tree cannot be walked
-
replaceProjectVersionRefs
public static List<File> replaceProjectVersionRefs(File gitRoot, String version, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Replace all occurrences of${project.version}with a literal version string in every POM file under the git root. Before replacing, each affected file is saved aspom.xml.ike-backupso it can be restored later.- Parameters:
gitRoot- the git repository root directoryversion- the literal version to substitutelog- Maven logger- Returns:
- the list of POM files that were modified
- Throws:
org.apache.maven.api.plugin.MojoException- if a file cannot be read or written
-
restoreBackups
public static List<File> restoreBackups(File gitRoot, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Restore all POM files from their.ike-backupcopies and delete the backup files. This reversesreplaceProjectVersionRefs(File, String, Log).- Parameters:
gitRoot- the git repository root directorylog- Maven logger- Returns:
- the list of POM files that were restored
- Throws:
org.apache.maven.api.plugin.MojoException- if a backup cannot be restored
-
bakeAliasIndirections
public static List<File> bakeAliasIndirections(File gitRoot, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Bake__ALIAS-driven indirections into all POM files under the git root. For each property whose name ends in__ALIAS(carrying a comma-separated list of legacy short-name aliases), generates and adds a corresponding indirection property<short>${G__GA__A__VERSION}</short>to the same POM — unless the property is already declared.This is the release-time materialization step: source poms declare the alias relationship via
__ALIASmetadata only; the actual indirection lines (required for Maven property resolution) are added by release-publish to the tagged source pom, then removed byunbakeAliasIndirections(File, Log)after the tag. See IKE-Network/ike-issues#527.- Parameters:
gitRoot- the git repository root directorylog- Maven logger- Returns:
- the list of POM files that were modified
- Throws:
org.apache.maven.api.plugin.MojoException- if a file cannot be read or written
-
unbakeAliasIndirections
public static List<File> unbakeAliasIndirections(File gitRoot, org.apache.maven.api.plugin.Log log) throws org.apache.maven.api.plugin.MojoException Unbake__ALIAS-driven indirections from all POM files under the git root — the inverse ofbakeAliasIndirections(File, Log). For each property whose name ends in__ALIAS, removes any corresponding indirection property whose value exactly matches the expected canonical reference${G__GA__A__VERSION}. Indirection lines with different values (e.g. project-local hand-written ones) are left alone.This is the post-tag removal step: after the release tag captures the baked state, the source pom is restored to its declarative-only form for the SNAPSHOT cycle. See IKE-Network/ike-issues#527.
- Parameters:
gitRoot- the git repository root directorylog- Maven logger- Returns:
- the list of POM files that were modified
- Throws:
org.apache.maven.api.plugin.MojoException- if a file cannot be read or written
-
gitAddFiles
public static void gitAddFiles(File gitRoot, org.apache.maven.api.plugin.Log log, List<File> files) throws org.apache.maven.api.plugin.MojoException Stage a list of files withgit add.- Parameters:
gitRoot- the git repository root directorylog- Maven loggerfiles- the files to stage- Throws:
org.apache.maven.api.plugin.MojoException- if the git add command fails
-
deriveCheckpointVersion
public static String deriveCheckpointVersion(String pomVersion, File gitRoot) throws org.apache.maven.api.plugin.MojoException Derive a checkpoint version from the current POM version.Format:
{base}-checkpoint.{yyyyMMdd}.{shortSha}wherebaseis the POM version minus-SNAPSHOT, andshortShais the abbreviated SHA of the current HEAD commit.This scheme is fully deterministic — the same commit on any machine always produces the same version string. No tag-sequence coordination across machines is required.
- Parameters:
pomVersion- current POM version (may include -SNAPSHOT)gitRoot- git repository root (for HEAD SHA lookup)- Returns:
- the checkpoint version string
- Throws:
org.apache.maven.api.plugin.MojoException- if the HEAD SHA cannot be resolved
-
tagExists
-
cleanRemoteSiteDir
public static void cleanRemoteSiteDir(File workDir, org.apache.maven.api.plugin.Log log, String remotePath) throws org.apache.maven.api.plugin.MojoException Remove a directory tree on the site server via SSH.Used to clean up snapshot sites after release or feature-finish.
Safety: validates the path starts with
SITE_DISK_BASEand contains at least two path components after the base to prevent accidental deletion of the entire site root.- Parameters:
workDir- local directory for process executionlog- Maven logremotePath- absolute path on the server (e.g.,/srv/ike-site/ike-pipeline/snapshot/main)- Throws:
org.apache.maven.api.plugin.MojoException- if the path is unsafe or SSH fails
-
cleanRemoteSiteDir
public static void cleanRemoteSiteDir(File workDir, org.apache.maven.api.plugin.Log log, String remotePath, String... sshPrefix) throws org.apache.maven.api.plugin.MojoException Overload accepting an explicit SSH command prefix — package-private for testing against containers.- Parameters:
workDir- local directory for process executionlog- Maven logremotePath- absolute path on the server to removesshPrefix- the SSH command tokens (e.g., "ssh", "-i", "key", "-p", "2222", "user@localhost")- Throws:
org.apache.maven.api.plugin.MojoException- if the path is unsafe or SSH fails
-
swapRemoteSiteDir
public static void swapRemoteSiteDir(File workDir, org.apache.maven.api.plugin.Log log, String remotePath) throws org.apache.maven.api.plugin.MojoException Atomically swap a newly deployed site into place on the server.The deployment flow is:
- SCP deploys to a staging path (
<target>.staging) - This method renames the old directory to
<target>.old - Renames the staging directory to the final target
- Removes the old directory
This avoids a window where the site is missing (rm + deploy) and ensures the site always serves either the old or new version.
- Parameters:
workDir- local directory for process executionlog- Maven logremotePath- final target path on the server- Throws:
org.apache.maven.api.plugin.MojoException- if SSH commands fail
- SCP deploys to a staging path (
-
swapRemoteSiteDir
public static void swapRemoteSiteDir(File workDir, org.apache.maven.api.plugin.Log log, String remotePath, String... sshPrefix) throws org.apache.maven.api.plugin.MojoException Overload accepting an explicit SSH command prefix — package-private for testing against containers.- Parameters:
workDir- local directory for process executionlog- Maven logremotePath- final target path on the serversshPrefix- the SSH command tokens (e.g., "ssh", "-i", "key", "-p", "2222", "user@localhost")- Throws:
org.apache.maven.api.plugin.MojoException- if the path is unsafe or SSH fails
-
siteStagingPath
-
updateLatestSymlink
public static void updateLatestSymlink(File workDir, org.apache.maven.api.plugin.Log log, String remotePath) throws org.apache.maven.api.plugin.MojoException Update thelatestsymlink alongside a version-prefixed site deploy so that<site-base>/latest/always points at the most recent release (ike-issues#303).For a release deployed to
/srv/ike-site/ike-platform/17/, this issuescd /srv/ike-site/ike-platform && ln -snf 17 lateston the site host. Idempotent — the-fflag replaces any prior symlink target.Uses the same SSH host as
swapRemoteSiteDir(File, Log, String). Best-effort: callers should catchMojoExceptionand surface as a warning rather than failing the release — the version-prefixed site is reachable at its own URL even if the alias update fails.- Parameters:
workDir- local directory for process executionlog- Maven logremotePath- the version-prefixed final site path (e.g./srv/ike-site/ike-platform/17)- Throws:
org.apache.maven.api.plugin.MojoException- if the path is unsafe or SSH fails
-
updateLatestSymlink
public static void updateLatestSymlink(File workDir, org.apache.maven.api.plugin.Log log, String remotePath, String... sshPrefix) throws org.apache.maven.api.plugin.MojoException Overload accepting an explicit SSH command prefix — package-private for testing against containers.- Parameters:
workDir- local directory for process executionlog- Maven logremotePath- the version-prefixed final site pathsshPrefix- the SSH command tokens- Throws:
org.apache.maven.api.plugin.MojoException- if the path is unsafe or SSH fails
-
siteStagingUrl
-
publishProjectSiteToGhPages
public static void publishProjectSiteToGhPages(Path stagingDir, String repoUrl, org.apache.maven.api.plugin.Log log, String projectId, String version) throws org.apache.maven.api.plugin.MojoException Publish a project's rendered site to its repo'sgh-pagesbranch using the hybrid structure (ike-issues#312, #332).Layout produced after each release:
/— the just-released version's site at the root (sohttps://ike.network/<repo>/serves the current release, the same as before)./<version>/— the just-released version preserved under a versioned subdirectory for citations and reproducibility./latest/— a copy of the just-released version under the canonical "latest" path. Not a git symlink: GitHub Pages doesn't follow them reliably.- Earlier
/<version>/subdirectories from prior releases are preserved unchanged.
Mechanics: the existing
gh-pagesbranch is cloned (preserving full history including all prior<version>/subdirs); root-level files and non-version root subdirs are wiped (stale assets from the previous release shouldn't linger); staging is copied to root, to<version>/, and tolatest/; an additive commit is pushed (no--force). On first-time publish (nogh-pagesbranch yet) the bootstrap path usesgit checkout --orphanand force-push.Adds a
.nojekyllmarker so GitHub Pages skips Jekyll processing — the content is already rendered HTML and we don't want underscore-prefixed directories to be stripped.Does NOT write a
CNAMEfile: the org-levelIKE-Network.github.io/CNAME(set toike.network) extends to all project pages under the org automatically. A per-project CNAME would either be ignored or conflict.Patterned on
OrgSiteSupport.publishToGhPages(in the ike-maven-plugin module) but generalized to any project's staging dir + remote. ike-workspace-model can't@linkdirectly to ike-maven-plugin classes — it sits below in the dependency stack, so they're not on its javadoc classpath.- Parameters:
stagingDir- directory containing the rendered site (typicallytarget/staging/)repoUrl- git URL of the project repo to push tolog- Maven loggerprojectId- project artifact ID, used in the commit messageversion- release version, used in the commit message- Throws:
org.apache.maven.api.plugin.MojoException- if any step fails
-
validateRemotePath
public static void validateRemotePath(String remotePath) throws org.apache.maven.api.plugin.MojoException Validate that a remote path is safe for deletion operations.Ensures the path starts with
SITE_DISK_BASEand has sufficient depth to prevent accidental deletion of the site root.- Parameters:
remotePath- absolute path on the server- Throws:
org.apache.maven.api.plugin.MojoException- if the path is unsafe
-
siteDiskPath
Resolve the on-disk site path for a given project, type, and optional subdirectory.- Parameters:
projectId- Maven artifact ID (e.g., "ike-pipeline")siteType- "release", "snapshot", or "checkpoint"subPath- optional subdirectory (branch name, version); null or blank to omit- Returns:
- absolute path on the server
-
branchToSitePath
-
readPomGroupId
Read the project's own<groupId>from a POM file, skipping any<groupId>inside the<parent>block.Used by cascade self-identification (IKE-Network/ike-issues#402): a reactor-root POM declares its own
<groupId>, which the release goals match againstrelease-cascade.yamlentries.- Parameters:
pomFile- the POM file to read- Returns:
- the group ID string
- Throws:
org.apache.maven.api.plugin.MojoException- if the file cannot be read or declares no own group ID
-
readPomArtifactId
public static String readPomArtifactId(File pomFile) throws org.apache.maven.api.plugin.MojoException Read the project's own<artifactId>from a POM file, skipping any<artifactId>inside the<parent>block.- Parameters:
pomFile- the POM file to read- Returns:
- the artifact ID string
- Throws:
org.apache.maven.api.plugin.MojoException- if the file cannot be read or has no artifact ID
-
readPomProperty
public static String readPomProperty(File pomFile, String propertyName) throws org.apache.maven.api.plugin.MojoException Read a<properties>value from a POM file by element name.Used by cascade resolution (IKE-Network/ike-issues#404) to read
ike-tooling.version— the version at which a foundation repo resolves theike-build-standardscascadeartifact.- Parameters:
pomFile- the POM file to readpropertyName- the property element name (e.g.ike-tooling.version)- Returns:
- the property value, or
nullif the POM declares no such property - Throws:
org.apache.maven.api.plugin.MojoException- if the file cannot be read
-
isEmptyDirectory
Check whether a directory has no entries. Returnstrueif the path is a directory with zero entries;falseif it has at least one entry. Throws if the path is not a readable directory.Used by the gh-pages publish flow (ike-issues#334) to distinguish "directory exists but is empty" (e.g.,
mvn site:stageproduced nothing for a single-module project) from "directory exists and has content" (the normal case).- Parameters:
dir- the directory to inspect- Returns:
trueif the directory contains no entries- Throws:
org.apache.maven.api.plugin.MojoException- if the directory cannot be listed
-
detectParentArtifactNesting
public static Path detectParentArtifactNesting(Path source, String projectId) throws org.apache.maven.api.plugin.MojoException Detect parent-artifactId staging nesting (ike-issues#342).When a Maven project has a
<parent>block,maven-site-plugin'ssite:stagewrites content totarget/staging/<parentArtifactId>/<projectId>/rather thantarget/staging/. The hybrid gh-pages publish path treatsstagingDiras the source of truth, so the published tree ends up double-nested at/<repo>/<version>/<parentArtifactId>/<projectId>/when it should be at/<repo>/<version>/.This helper detects that wrap by checking:
sourcecontains exactly one entry that is itself a directory, and- that single directory contains a non-empty subdirectory
whose name equals
projectId.
Returns the path to
<single-entry>/<projectId>when both conditions hold; otherwise returnsnullto indicate no unwrap is needed.The check is intentionally conservative — it requires a single top-level entry so it does not mis-fire on staging trees that legitimately have multiple top-level dirs (the normal aggregated-reactor case where each module's site lives under its own subdirectory).
- Parameters:
source- the post-#337-unwrap effective staging sourceprojectId- the project's own artifactId- Returns:
- the unwrapped path, or
nullif no nesting was detected - Throws:
org.apache.maven.api.plugin.MojoException- if the directory cannot be inspected
-
detectAggregatedStaging
public static Path detectAggregatedStaging(Path source, String projectId) throws org.apache.maven.api.plugin.MojoException Detect aggregated-reactor staging where the project's own site lives alongside multiple sibling subdirs (ike-issues#351).When a workspace pom inherits a
<site>URL with no common ancestor among its reactor subprojects' URLs (e.g. each module getshttps://ike.network/<artifactId>/),site:stageflattens each module's site at its own top-level path undertarget/staging/. The workspace's own content lives instaging/<projectId>/and the subprojects are siblings. Without unwrap, the gh-pages root gets the whole flattened tree — wrong index.html, missing the workspace's own pages.Detection criteria (all must hold):
sourcecontains at least one top-level entry, AND- a subdirectory named
projectIdexists at the top level, AND - that subdirectory is non-empty.
Single-top-level cases are handled by
detectParentArtifactNesting(Path, String); this method's value-add is the multi-top-level case whereprojectIdis one of several siblings.Returns the path to
source/<projectId>when the detection fires;nullotherwise.- Parameters:
source- the post-#337/#342-unwrap effective staging sourceprojectId- the project's own artifactId- Returns:
- the unwrapped path, or
null - Throws:
org.apache.maven.api.plugin.MojoException- if the directory cannot be inspected
-
detectHttpsUrlStaging
public static Path detectHttpsUrlStaging(Path source, String projectId) throws org.apache.maven.api.plugin.MojoException Detect URL-as-path staging wheresite:stagehas mapped anhttps://-form<site><url>to a literal directory tree undertarget/staging/(ike-issues#359).For a
<site><url>https://ike.network/<projectId>/inside a multi-module reactor, maven-site-plugin producestarget/staging/https:/ike.network/<projectId>/<content>— scheme (https:), host (ike.network), and each path segment each become a real subdirectory. The pre-#304 scpexe URLs also went through this transformation, but their path segments aligned with where site-deploy actually wrote, so the layout was sensible. Post-#304 thehttps://URLs survive only for relative-path computation and the URL-as-path staging is wasted — the actual publish target is gh-pages.Detection: descend into
source/https:/(orsource/http:/), then a single host directory, then the project's own<projectId>/subdirectory. When all three exist and<projectId>/is non-empty, return it as the effective staging source. The caller then applies the remaining unwraps (version-nested #337, parent-artifactId #342, aggregated #351) to the narrowed source — e.g. ike-platform'shttps://ike.network/ike-platform/${project.version}/resolves tosource/https:/ike.network/ike-platform/here, then #337 descends into40/.Returns
nullif any layer of the expected structure is missing or the project's own directory is empty — staying conservative so non-matching projects (single-module reactors, scpexe-URL projects, etc.) fall through to the existing unwrap chain unchanged.- Parameters:
source- the staging directory to inspectprojectId- the project's artifactId- Returns:
- the unwrap target, or
nullif no URL-as-path nesting was detected - Throws:
org.apache.maven.api.plugin.MojoException- if a directory cannot be listed
-
findSubmoduleSiteDirs
public static List<Path> findSubmoduleSiteDirs(Path layer, Path exclude) throws org.apache.maven.api.plugin.MojoException Find one-level-deep subdirectories underlayerthat look like rendered Maven-site outputs (have anindex.htmlat the top), excludingexcludeitself (the workspace's own subtree, already published at the gh-pages root).Used by
publishProjectSiteToGhPages(Path, String, Log, String, String)step (4) to surface sibling submodule subtrees in a multi-module reactor. The conservativeindex.html-presence check keeps the walk from mis-classifying resource dirs (css/,fonts/,images/) or version dirs as submodule sites.Returns an empty list if
layeris not a directory or has no matching entries. Order is unspecified.- Parameters:
layer- the directory to scanexclude- a path that, when matched against an entry, causes the entry to be skipped (typically the workspace's own narrowed staging source)- Returns:
- list of submodule site directories
- Throws:
org.apache.maven.api.plugin.MojoException- if the directory cannot be listed
-
deleteDirectory
Recursively delete a directory and all its contents. Best-effort — failures are silently ignored.- Parameters:
dir- the directory to delete
-
copyDirectory
Recursively copy a directory tree.- Parameters:
source- the source directory to copy fromtarget- the target directory to copy to- Throws:
IOException- if a file cannot be copied
-
copyDirectoryExcludingTopLevelVersionDirs
public static void copyDirectoryExcludingTopLevelVersionDirs(Path source, Path target) throws IOException Recursively copy a directory tree, excluding top-level subdirectories whose names look like release versions. Files at the top level and non-version subdirectories at the top level are copied normally. Inside any non-filtered subdirectory, all entries are copied without further filtering — the exclusion applies only at depth 0.Used by
publishProjectSiteToGhPages(Path, String, Log, String, String)to keep the gh-pages source clean of staging-pollution. Three causes of top-level version-prefixed entries in the source:- Stale local mirrors: when a project's
site.deploy.urlcontains the version segment andmaven-clean-pluginpreservestarget/staging/, prior releases'<version>/subdirs accumulate there. - Self-nesting: copying staging that
already has a
<currentVersion>/subdir into a version subdir would produce<currentVersion>/<currentVersion>/.... - Race with the wipe step: the wipe preserved version dirs in the gh-pages clone; if staging happens to also contain those names, the copy would clobber the preserved content with potentially stale mirror copies.
The filter applies the same digit-prefix heuristic as
isVersionDirName(String): top-level dirs whose names start with a digit are skipped.- Parameters:
source- the source directory to copy fromtarget- the target directory to copy to- Throws:
IOException- if a file cannot be copied- See Also:
- Stale local mirrors: when a project's
-
readPomName
Read the<name>element from a POM file.- Parameters:
pomFile- the POM file to read- Returns:
- the name, or null if not present
- Throws:
org.apache.maven.api.plugin.MojoException- if the file cannot be read
-
readPomDescription
public static String readPomDescription(File pomFile) throws org.apache.maven.api.plugin.MojoException Read the<description>element from a POM file.- Parameters:
pomFile- the POM file to read- Returns:
- the description, or null if not present
- Throws:
org.apache.maven.api.plugin.MojoException- if the file cannot be read
-