NexusPhase.java
package network.ike.plugin.release.nexus;
import network.ike.plugin.ReleaseSupport;
import network.ike.plugin.release.ReleaseContext;
import network.ike.plugin.release.RetrySchedule;
import org.apache.maven.api.plugin.MojoException;
import java.io.File;
/**
* The Nexus deploy phase of the release pipeline.
*
* <p>Runs {@code mvn clean deploy -P release,signArtifacts} from the
* release-tagged worktree, with a bounded retry budget configured by
* {@code ike.deploy.nexus.{maxAttempts,backoffSeconds}}. Nexus is the
* mandatory deploy: a release with no Nexus artifact has no internal
* consumers unblocked, so exhausting the retry budget aborts the
* release before any tag or main push.
*
* <p>Carved out of {@code ReleaseDraftMojo.deployToNexusWithRetry()}
* during the Phase 4 Commit 2 (IKE-Network/ike-issues#489).
*/
public final class NexusPhase {
private final ReleaseContext ctx;
/**
* Creates a new Nexus phase bound to the given context.
*
* @param ctx the per-invocation release context
*/
public NexusPhase(ReleaseContext ctx) {
this.ctx = ctx;
}
/**
* Executes the Nexus deploy with retry.
*
* <p>Retry parameters are read from {@code ctx.request()}:
* {@link network.ike.plugin.release.ReleaseRequest#nexusDeployMaxAttempts()}
* and
* {@link network.ike.plugin.release.ReleaseRequest#nexusDeployBackoffSeconds()}.
* The caller is responsible for skipping this phase entirely
* when {@code skipNexusDeploy} is set; this method does not
* inspect that flag.
*
* @return a {@link NexusOutcome} marked {@code succeeded=true} on the first
* successful cycle, with {@code attempts} set to the cycle on which
* success occurred
* @throws MojoException after the final attempt fails — the release is
* aborted before any tag or main push
*/
public NexusOutcome execute() throws MojoException {
File gitRoot = ctx.gitRoot();
File mvnw = ctx.mvnw();
int maxAttempts = ctx.request().nexusDeployMaxAttempts();
int[] backoff = RetrySchedule.parseSeconds(
"ike.deploy.nexus.backoffSeconds",
ctx.request().nexusDeployBackoffSeconds());
NexusOutcome outcome = NexusOutcome.initial();
Throwable last = null;
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
outcome = outcome.withAttempts(attempt);
if (attempt > 1) {
int wait = backoff[Math.min(attempt - 2,
backoff.length - 1)];
RetrySchedule.sleepBefore(ctx.log(), wait, "Nexus deploy",
attempt, maxAttempts);
}
ctx.log().info("Deploying to Nexus (cycle "
+ attempt + "/" + maxAttempts + ")...");
try {
ReleaseSupport.exec(gitRoot, ctx.log(),
mvnw.getAbsolutePath(), "clean", "deploy",
"-B", "-T", "1", "-P", "release,signArtifacts");
return outcome.withSucceeded(true);
} catch (MojoException e) {
last = e;
ctx.log().warn("Nexus deploy cycle " + attempt
+ "/" + maxAttempts
+ " failed: " + e.getMessage());
}
}
throw new MojoException(
"Nexus deploy failed after " + maxAttempts
+ " cycles. The release is aborted before "
+ "any tag or main push. Last error: "
+ (last == null ? "(none captured)"
: last.getMessage()),
last);
}
}