PostMutationSync.java
package network.ike.plugin.ws;
import org.apache.maven.api.plugin.Log;
import java.io.File;
/**
* Refresh workspace state derived from POMs and on-disk siblings.
* Called at the end of any goal whose effect can change which siblings
* are present on disk or what their POMs declare.
*
* <p>Currently one derivation:
* <ul>
* <li>{@link YamlDepsSync} — re-derives each subproject's
* {@code depends-on} edges from POM contents and rewrites
* {@code workspace.yaml} when the graph has drifted.</li>
* </ul>
*
* <p>Earlier revisions also ran {@code IdeProfileSync} here to
* maintain a {@code -P?with-*} block in {@code .mvn/maven.config}
* (IKE-Network/ike-issues#276), so IntelliJ would activate the
* {@code with-*} profiles that scoped subprojects into the reactor.
* That whole mechanism was retired in
* {@code IKE-Network/ike-issues#460}: the
* {@code ike-workspace-extension} prunes non-existent
* {@code <subprojects>} from workspace POMs at model-read time, so
* IntelliJ sees the right reactor without any profile activation.
*
* <p>The step is idempotent — running this hook back-to-back produces
* no further changes. Failures are logged at WARN and do not abort
* the caller.
*
* <p><b>Self-committing.</b> {@code workspace.yaml} is machine-maintained,
* never hand-authored, so a re-derivation it produces must not be left as
* an uncommitted working-tree change — that strands the change for the
* next goal's clean-tree preflight to trip over (the reported failure mode
* in {@code IKE-Network/ike-issues#774}: {@code ws:commit} re-derived
* {@code depends-on} edges <em>after</em> its commit loop, and the next
* {@code ws:push}/{@code ws:sync} then refused to run). When the
* re-derivation is the <em>sole</em> pending change to {@code workspace.yaml}
* — i.e. the manifest was committed-clean when the hook ran — it is
* committed here in isolation. When the manifest already carried a
* caller's own edit (e.g. {@code ws:add} adding a subproject, which
* deliberately leaves the manifest staged for the user), the combined
* change is left for that caller's own commit policy and this hook does
* not commit.
*
* <p>Triggered from: {@code ws:add}, {@code ws:remove}, {@code ws:sync},
* {@code ws:pull}, {@code ws:commit-publish}, {@code ws:scaffold-init},
* {@code ws:feature-finish-merge-publish},
* {@code ws:feature-finish-squash-publish},
* {@code ws:align-publish}, {@code ws:scaffold-publish}
* (which subsumes the retired ws:set-parent).
*
* <p>See {@code IKE-Network/ike-issues#279} (origin) and
* {@code IKE-Network/ike-issues#460} (IdeProfileSync retirement).
*/
public final class PostMutationSync {
private PostMutationSync() {}
/** Path of the machine-maintained manifest, relative to the root. */
private static final String MANIFEST = "workspace.yaml";
/**
* Run all post-mutation derivations against the workspace at
* {@code workspaceRoot}, committing a re-derived {@code workspace.yaml}
* in isolation when it is the sole pending change (see the class
* note on self-committing).
*
* @param workspaceRoot the workspace root directory
* @param log plugin log for status messages
* @return {@code true} if a re-derived {@code workspace.yaml} was
* committed by this call; {@code false} if nothing changed or
* the change was left for the caller to commit
*/
public static boolean refresh(File workspaceRoot, Log log) {
// Snapshot the manifest's state BEFORE the re-derivation. If it was
// unmodified, any change the derivation makes is attributable solely
// to it and is committed in isolation; if it already carried an edit
// (or there is no git state — e.g. .git excluded from Syncthing on a
// not-yet-bootstrapped sibling) it is excluded from the snapshot, so
// the combined change is left for the caller / bootstrapper (#774).
GoalAuthoredChanges authored =
GoalAuthoredChanges.snapshot(workspaceRoot, log, MANIFEST);
if (!YamlDepsSync.run(workspaceRoot, log)) {
return false;
}
boolean committed = authored.commitAuthored(
"ws: re-derive depends-on edges\n\n"
+ "Re-derives workspace.yaml depends-on from POM "
+ "reality after a workspace mutation, so the "
+ "manifest is never left uncommitted.\n\n"
+ "Refs: IKE-Network/ike-issues#774\n"
+ "Refs: IKE-Network/ike-issues#279");
if (committed) {
log.info(" workspace.yaml: committed re-derived depends-on edges");
}
return committed;
}
}