StaleSiteCleanupReconciler.java
package network.ike.plugin.reconcile;
import network.ike.plugin.ReleaseSupport;
import org.apache.maven.api.plugin.MojoException;
import java.util.List;
/**
* Reconciler that removes deployed-site directories from the legacy
* scpexe deploy server ({@link ReleaseSupport#SITE_DISK_BASE} on
* {@link ReleaseSupport#SITE_SSH_HOST}).
*
* <p>Subsumes the retired {@code ike:clean-site} goal
* (IKE-Network/ike-issues#398). Most projects no longer use the scpexe
* mirror (#304 retired it as the canonical distribution channel in
* favor of GitHub Pages), but the directory may still exist from
* before that change and shows up in {@code ike:site-publish -Dsite=removed}
* runs as cleanup work.
*
* <p><b>Detect</b>: read-only — does not SSH out to enumerate stale
* dirs (that would slow {@code site-draft} for every project even when
* nothing is wrong). Always reports {@link SiteDriftReport#noDrift}
* during a forward-deploy draft; the uninstall path (triggered by
* {@code -Dsite=removed}) is the only time stale cleanup is in scope.
*
* <p><b>Apply</b>: forward-deploy direction does nothing (this
* reconciler is uninstall-only). The forward {@link DeployedSiteReconciler}
* already publishes a fresh site.
*
* <p><b>Uninstall</b>: SSH out and remove
* {@code /srv/ike-site/<projectId>/} (the release subtree). The
* {@code .staging} / {@code .old} suffixes are handled by the same
* {@code ssh rm -rf} since they're inside the same project tree.
*/
public class StaleSiteCleanupReconciler implements SiteReconciler {
/** Creates this reconciler instance. */
public StaleSiteCleanupReconciler() {}
@Override
public String dimension() {
return "Stale deployed-site directories";
}
@Override
public String optOutFlag() {
return "cleanSite";
}
@Override
public SiteDriftReport detect(SiteContext ctx) {
// No remote enumeration in detect — keep site-draft snappy.
// The uninstall path (-Dsite=removed) is the only place this
// reconciler runs work, and that path doesn't go through
// detect() at all.
return SiteDriftReport.noDrift(dimension());
}
@Override
public void apply(SiteContext ctx) {
// Forward deploy: nothing to do. DeployedSiteReconciler
// publishes fresh content; the prior scpexe mirror was retired
// in #304 and no longer needs per-deploy cleanup.
if (ctx.options().isOptedOut(optOutFlag())) {
return;
}
}
@Override
public void uninstall(SiteContext ctx) {
if (ctx.options().isOptedOut(optOutFlag())) {
ctx.log().info(" " + dimension() + ": skipped (opted out via -D"
+ optOutFlag() + "=false)");
return;
}
String projectId = ctx.projectId();
if (projectId == null || projectId.isBlank()) {
ctx.log().info(" " + dimension()
+ ": skipped (no project id)");
return;
}
String remotePath = ReleaseSupport.SITE_DISK_BASE + projectId;
try {
ReleaseSupport.cleanRemoteSiteDir(
ctx.gitRoot(), ctx.log(), remotePath);
ctx.log().info(" " + dimension()
+ ": removed " + remotePath);
} catch (MojoException e) {
// Likely "host not reachable" on machines without the
// wireguard / proxy SSH config. Non-fatal — most projects
// were never on the scpexe mirror.
ctx.log().info(" " + dimension()
+ ": scpexe mirror cleanup skipped ("
+ e.getMessage() + ")");
}
}
}