Implement isolated Build and Refresh with immutable leased snapshots
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# Build and Refresh with immutable snapshots
|
||||
|
||||
Work package 5 implements the shared build coordinator, effective Maven capture helper and persistent snapshot store. This is usable through host services; wiring the editor action, discovery restart and preview workers remains work package 7. No application class runs inside the IDE or shared UI.
|
||||
|
||||
## Workflow
|
||||
|
||||
`BuildRefresh.start` returns a cancellable run with a completion stage. It accepts a configuration/context, an optional explicitly selected fallback Maven executable, exact approved document revisions, and an output callback. Callers must not block an IDE/UI thread waiting for the result. Source editing remains available throughout.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Inputs[Known drafts / runtime choices] --> Gate{Unsaved build inputs?}
|
||||
Gate -->|Yes| Save[Review exact revisions: Save and build or Cancel]
|
||||
Save --> Inputs
|
||||
Gate -->|No| Copy[Isolated reactor copy]
|
||||
Copy --> Plan[Maven effective plan]
|
||||
Plan --> Build[Scoped clean / dependencies / package / capture]
|
||||
Build --> Verify[Check inputs and dependency identities]
|
||||
Verify --> Publish[Atomic snapshot publication]
|
||||
Publish --> Lease[Lease one build identity for discovery and preview]
|
||||
Build -->|Failure or cancellation| Retain[Retain old build as outdated]
|
||||
Retain --> Explicit[Explicit Preview with last successful build]
|
||||
```
|
||||
|
||||
The result is READY, NEEDS_SAVE, CANCELLED, FAILED or STALE. NEEDS_SAVE includes the document URI and revision list and launches no Maven process. A later call must approve exactly the current list; newer edits require another review. The host saves through its own Document services. An autosave that already persisted the approved IntelliJ revision is accepted. Hosts without unsaved-input inventory fail closed instead of assuming disk is current.
|
||||
|
||||
The initial input closure is deliberately conservative: known non-FXML drafts throughout the reactor, plus user Maven settings/toolchains. It can request saving an unrelated reactor draft, although Maven builds only selected modules and dependencies. This avoids guessing profile-dependent build inputs before Maven has evaluated the model. Another process's unsaved buffers are not observable. Unsaved FXML is reserved for the later draft-overlay mechanism and does not by itself require Java rebuilding; edits to saved resources do affect freshness.
|
||||
|
||||
## Maven integration and scope
|
||||
|
||||
`designer-maven-plugin` is a separate Java 21 Maven plugin, installed by the bootstrap command and excluded from IDE/application runtime dependencies. It runs in the selected build JDK. It uses [MavenProject's effective model and runtime/test classpaths](https://maven.apache.org/ref/3.9.11/maven-core/apidocs/org/apache/maven/project/MavenProject.html), rather than reconstructing dependency graphs from POM text or parsing console output.
|
||||
|
||||
The coordinator prefers the reactor wrapper. Without one, an absolute executable Maven path must be selected; there is no PATH fallback. Maven 3.9.11 is the qualified baseline. JAVA_HOME and the leading PATH entry are set from the named build runtime; the target runtime is resolved and recorded separately. Profiles are explicitly passed, and `-pl` selects the application and optional preview module with `-am` for required reactor dependencies. The command and output are observable.
|
||||
|
||||
The helper goals run in this order:
|
||||
|
||||
1. `plan`: inspect the selected effective projects, check profile activation, and reject configured source/resource/output paths outside each isolated module before executing application lifecycle phases.
|
||||
2. `clean`: remove copied stale outputs, including custom in-module output directories.
|
||||
3. `dependencies`: capture resolved dependency coordinates, content hashes and file attributes before compilation for each project.
|
||||
4. `package`, with tests skipped but test compilation enabled: supports both a dedicated preview module and application test-source previews.
|
||||
5. `capture`: verify dependency identities again and emit effective runtime/test classpaths, source/resource roots, profiles, reactor relationships and coordinates as machine-readable properties outside the build outputs.
|
||||
|
||||
The returned `ReactorView` contains the selected effective closure for context matching. Generated roots outside the original source tree are not offered as editable source roots. Module-path settings are retained in the snapshot's context specification; real module-path preview launch/access remains work package 6.
|
||||
|
||||
## Consistency and capture
|
||||
|
||||
Each run creates a private copy below `.neitzel-fx-designer/work`. Conventional Maven `target` outputs, Git/editor metadata and designer caches are excluded. Ordinary source packages named `target` remain inputs. The original reactor's output directories are not modified by this build.
|
||||
|
||||
User `settings.xml` and `toolchains.xml` are copied into the private run directory and selected explicitly. If absent, empty settings are used. Their fingerprints are included in the input identity, but their contents are not published into the snapshot. Standard Maven distribution/global configuration and plugin-controlled external state remain build-environment inputs; this implementation is not a sandbox or a promise of reproducible arbitrary custom plugins.
|
||||
|
||||
Input content hashes, file identity/size/mtime and filesystem change notifications are checked around isolation, compilation and publication. Watch overflow or detected changes invalidate the build. A private source copy prevents later original edits from changing the files being compiled. This combines checks across the operation; it does not rely on one timestamp taken after compilation. Filesystem observations cannot provide an adversarial, atomic multi-file transaction against external tools that deliberately restore content and all metadata; supported usage assumes ordinary editor/build activity.
|
||||
|
||||
After Maven succeeds, runtime class directories, packaged reactor dependencies, external JARs, compiled resources and test output (when selected) are copied into a separate snapshot staging directory. No runtime entry points at a mutable original `target` directory or dependency-cache file. File hashes and complete directory membership are rechecked after capture. Dependency signatures recorded before compilation must still match at publication. Cancellation or changed inputs prevent current activation; partial staging is never a selectable build.
|
||||
|
||||
A JSON manifest records:
|
||||
|
||||
- Snapshot UUID, context and creation time.
|
||||
- Source/settings input identities.
|
||||
- Ordered snapshot-relative runtime entries and per-file SHA-256 hashes.
|
||||
- Effective project/dependency coordinates and the context configuration.
|
||||
- Build/target runtime paths and feature versions, command, manifest and wire versions.
|
||||
|
||||
Publication moves the complete staging directory atomically and then atomically updates the context's last-successful pointer. If interrupted between those operations, an unreferenced complete snapshot can be evicted; a partial snapshot cannot become the current build. Snapshot data is immutable through the store interface and independent of later build outputs. The cache is not a security boundary against a process intentionally modifying its files; lease acquisition verifies its contents and file set.
|
||||
|
||||
## Freshness, explicit fallback and lifetime
|
||||
|
||||
Starting a build marks previous results outdated. A failed, cancelled or invalidated build preserves the last successful ID and reason. `previewCurrent` rechecks source identities, known unsaved inputs, configuration identity and resolved runtime mappings before returning a lease. It throws for outdated state. Only `previewLastSuccessful` explicitly opts into the retained build; no failed build silently activates old output.
|
||||
|
||||
A lease supplies ordered private runtime paths and one manifest/build identity for discovery and rendering. Each lease holds a filesystem lock in a unique ticket; eviction checks those locks across threads, store instances and processes. Published last-successful snapshots are retained even without a lease. Closing a lease permits eviction of superseded snapshots. Process death releases its OS lock; abandoned tickets and staging directories are recoverable. Normal run workspaces are removed when the run finishes; abandoned private workspaces after a host crash are ignored, not activated, and may be removed when no build process owns them. There is no automatic age/size quota yet.
|
||||
|
||||
Cancellation stops the owned Maven process and observed descendants through the shared process runner. Capture checks cancellation between files. Cancellation after the publication commit point can complete as READY; it does not retroactively delete a successfully published build. The caller must keep the lease alive for the full worker lifetime and release it after worker shutdown; worker integration is a later gate.
|
||||
|
||||
## Validation
|
||||
|
||||
The normal [bootstrap check](../README.md#validate-the-foundation) installs the helper, runs the tests and then executes a real Maven smoke fixture using Java 21. Its four-module reactor includes a deliberately uncompilable unrelated module. Checks prove that only the selected application/dependency/preview scope builds, profile-selected module previews and test-source previews work, resources are present, original outputs remain untouched, old snapshots keep running after a rebuild, and compiler failure preserves explicit last-successful preview.
|
||||
|
||||
Unit/integration regressions additionally cover exact save approval, FXML draft freshness, input changes during build, cancellation, dependency/output changes during capture, staging recovery, snapshot integrity, same-JVM and separate-process leases, owner death, native IntelliJ dirty-Java inventory and autosave. The earlier configuration, protocol, bytecode, runtime artifact identity and plugin packaging checks remain required.
|
||||
|
||||
Acceptance on 2026-11-12: **45 tests passed** (41 Maven, four IntelliJ), with no failures/errors/skips, plus **14 real Maven/Java 21 smoke checks**. The full clean bootstrap command also passed artifact identity, bytecode/dependency isolation, target linkage, plugin packaging and documentation checks. The previously documented intentional IntelliJ until-build warning remains.
|
||||
|
||||
The next bootstrap step validates the typed preview API and target adapter against real NeitzelLib classpath/module-path fixtures. Setup UI, worker transport, discovery/render restart, draft overlays and the connected two-host workflow remain later gates.
|
||||
Reference in New Issue
Block a user