diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6187535 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,28 @@ +name: ci + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +jobs: + java: + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, macos-15, windows-2025] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0 + with: + distribution: temurin + java-version: '21' + cache: maven + - run: mvn --batch-mode test diff --git a/README.md b/README.md index 4fba735..5270c73 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,17 @@ # zed-eclipse -Buildable Eclipse core candidate for Zed Package Manager state, diagnostics, and recommended actions. +Eclipse integration work for Zed Package Manager state, diagnostics, and confirmation-gated recommended actions. -The Java process adapter uses an argv vector, a bounded timeout, schema validation, failure redaction, and rejects command actions that do not require explicit confirmation. `mvn test` exercises the safety boundary. +The dedicated repository now contains: -A dedicated repository still needs the PDE/OSGi bundle, Zed Packages view, workspace markers, quick fixes, p2 update site, and clean Eclipse application tests. +- a Java 21 process adapter using argv execution, bounded timeout, schema validation, failure redaction, and unsafe-action rejection; +- a multi-root workspace model for the future **Zed Packages** view; +- Problems-marker projections with file-specific resources and `.zpkg.toml` fallback markers; +- confirmation-gated quick-fix command previews carrying exact executable, argv, and working directory; +- JUnit coverage and cross-platform Maven CI. + +```sh +mvn test +``` + +Remaining native work is the PDE/OSGi bundle, resource listeners, view and quick-fix UI wiring, clean Eclipse application tests, and p2 feature/update-site packaging. diff --git a/conformance/ide-integration.json b/conformance/ide-integration.json new file mode 100644 index 0000000..fc7a315 --- /dev/null +++ b/conformance/ide-integration.json @@ -0,0 +1,27 @@ +{ + "schema": "zed-pkg/ide-integration-conformance/v1", + "integration": "eclipse", + "repository": "zed-pkg/zed-eclipse", + "contractVersion": 1, + "level": {"core": "pass", "nativeShell": "workspace-marker-model", "distribution": "repository"}, + "capabilities": { + "multiRootDiscovery": "workspace-model", + "manifestDiagnostics": true, + "lockDiagnostics": true, + "materializationDiagnostics": true, + "stagingRecovery": true, + "argvExecution": true, + "boundedExecution": true, + "outputRedaction": true, + "explicitMutationConfirmation": true, + "problemsMarkerProjection": true, + "versionedInspectAdapter": "pending-zed-cli-191", + "nativeUnitTests": true, + "repoLocalCI": true, + "immutableWorkflowInputs": true, + "fixedRunnerImages": true, + "checkoutCredentialsPersisted": false + }, + "remaining": ["PDE/OSGi bundle", "Zed Packages view", "workspace resource listeners", "quick-fix UI wiring", "Eclipse application test", "p2 update site"], + "linearIssue": "DEN-2508" +} diff --git a/src/main/java/tech/zpkg/eclipse/ZedWorkspaceModel.java b/src/main/java/tech/zpkg/eclipse/ZedWorkspaceModel.java new file mode 100644 index 0000000..f08f2c3 --- /dev/null +++ b/src/main/java/tech/zpkg/eclipse/ZedWorkspaceModel.java @@ -0,0 +1,50 @@ +package tech.zpkg.eclipse; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public final class ZedWorkspaceModel { + public record PackageNode(String root, long errors, long warnings, List issues) {} + public record Marker(String workspaceRoot, String resource, String severity, String issueId, String message) {} + public record ActionPreview(String executable, List arguments, String workingDirectory, boolean requiresConfirmation) {} + public record Snapshot(List packages, List markers) {} + + private ZedWorkspaceModel() {} + + public static Snapshot project(List reports) { + var packages = new ArrayList(); + var markers = new ArrayList(); + for (var report : reports == null ? List.of() : reports) { + var root = Path.of(report.workspaceRoot()).toAbsolutePath().normalize(); + var issues = report.issues() == null ? List.of() : report.issues(); + long errors = issues.stream().filter(issue -> "error".equalsIgnoreCase(issue.severity())).count(); + long warnings = issues.stream().filter(issue -> "warning".equalsIgnoreCase(issue.severity())).count(); + packages.add(new PackageNode(root.toString(), errors, warnings, List.copyOf(issues))); + for (var issue : issues) { + var files = issue.files() == null || issue.files().isEmpty() ? List.of(".zpkg.toml") : issue.files(); + for (var file : files) { + var resourcePath = Path.of(file); + var resource = resourcePath.isAbsolute() ? resourcePath.normalize() : root.resolve(resourcePath).normalize(); + markers.add(new Marker(root.toString(), resource.toString(), issue.severity(), issue.id(), issue.title() + ": " + issue.detail())); + } + } + } + packages.sort(Comparator.comparing(PackageNode::root)); + markers.sort(Comparator.comparing(Marker::resource).thenComparing(Marker::issueId)); + return new Snapshot(List.copyOf(packages), List.copyOf(markers)); + } + + public static ActionPreview preview(ZedInspector.Action action, Path root) { + if (!"command".equals(action.kind())) throw new IllegalArgumentException("only command actions have an execution preview"); + if (!action.requiresConfirmation()) throw new IllegalArgumentException("command actions must require explicit confirmation"); + if (action.command() == null || action.command().isBlank()) throw new IllegalArgumentException("command executable must not be blank"); + return new ActionPreview( + action.command(), + List.copyOf(action.arguments() == null ? List.of() : action.arguments()), + root.toAbsolutePath().normalize().toString(), + true + ); + } +} diff --git a/src/test/java/tech/zpkg/eclipse/ZedWorkspaceModelTest.java b/src/test/java/tech/zpkg/eclipse/ZedWorkspaceModelTest.java new file mode 100644 index 0000000..f751ad3 --- /dev/null +++ b/src/test/java/tech/zpkg/eclipse/ZedWorkspaceModelTest.java @@ -0,0 +1,43 @@ +package tech.zpkg.eclipse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.file.Path; +import java.util.List; +import org.junit.jupiter.api.Test; + +class ZedWorkspaceModelTest { + @Test + void projectsPackagesAndProblemsMarkersAcrossWorkspaceRoots() { + var warning = new ZedInspector.Issue( + "lock.stale", "warning", "Stale lock", "lock is stale", List.of(".zpkg.lock"), + List.of(new ZedInspector.Action("install", "Install", "command", "zed", List.of("install"), true)) + ); + var error = new ZedInspector.Issue("manifest.invalid", "error", "Invalid manifest", "bad toml", List.of(), List.of()); + var snapshot = ZedWorkspaceModel.project(List.of( + new ZedInspector.Report(1, "zeta", null, List.of(warning)), + new ZedInspector.Report(1, "alpha", null, List.of(error)) + )); + assertEquals(2, snapshot.packages().size()); + assertTrue(snapshot.packages().get(0).root().endsWith("alpha")); + assertEquals(1, snapshot.packages().get(0).errors()); + assertEquals(1, snapshot.packages().get(1).warnings()); + assertEquals(2, snapshot.markers().size()); + assertTrue(snapshot.markers().stream().anyMatch(marker -> marker.resource().endsWith(".zpkg.toml"))); + assertTrue(snapshot.markers().stream().anyMatch(marker -> marker.resource().endsWith(".zpkg.lock"))); + } + + @Test + void previewsOnlyConfirmationGatedQuickFixCommands() { + var action = new ZedInspector.Action("install", "Install", "command", "zed", List.of("install"), true); + var preview = ZedWorkspaceModel.preview(action, Path.of("workspace")); + assertEquals("zed", preview.executable()); + assertEquals(List.of("install"), preview.arguments()); + assertTrue(preview.requiresConfirmation()); + assertThrows(IllegalArgumentException.class, () -> ZedWorkspaceModel.preview( + new ZedInspector.Action("bad", "Bad", "command", "zed", List.of("install"), false), Path.of("workspace") + )); + } +}