From 3802edb46974992ef2e874beeb43538a1e8e0c3e Mon Sep 17 00:00:00 2001 From: Stefano Cordio Date: Sat, 25 Jul 2026 13:04:53 +0200 Subject: [PATCH 1/7] Add Eclipse JDT embedded lockfiles, upgrade formatter version to 4.40 --- .../spotless/extra/EquoBasedStepBuilder.java | 58 ++++ .../extra/java/EclipseJdtFormatterStep.java | 7 +- .../eclipse_jdt_formatter/v4.11.lockfile | 2 + .../eclipse_jdt_formatter/v4.39.lockfile | 2 + .../eclipse_jdt_formatter/v4.40.lockfile | 2 + .../extra/eclipse_jdt_formatter/v4.9.lockfile | 2 + .../EquoBasedStepBuilderLockfileTest.java | 93 ++++++ .../java/EclipseJdtFormatterStepTest.java | 27 +- .../java/EclipseJdtLockfileMetadataTool.java | 278 ++++++++++++++++++ .../diffplug/spotless/extra/empty.lockfile | 1 + 10 files changed, 466 insertions(+), 6 deletions(-) create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile create mode 100644 lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java create mode 100644 lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java create mode 100644 lib-extra/src/test/resources/com/diffplug/spotless/extra/empty.lockfile diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java index f9748217be..4d0a1ef624 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java @@ -18,7 +18,10 @@ import static java.util.stream.Collectors.toMap; import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.io.Serializable; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -28,6 +31,7 @@ import javax.annotation.Nullable; +import com.diffplug.common.base.Errors; import com.diffplug.common.collect.ImmutableMap; import com.diffplug.spotless.FileSignature; import com.diffplug.spotless.FormatterFunc; @@ -122,6 +126,10 @@ protected void addPlatformRepo(P2Model model, String version) { /** Returns the FormatterStep (whose state will be calculated lazily). */ public FormatterStep build() { var roundtrippableState = new EquoStep(formatterVersion, settingProperties, settingXml, FileSignature.promise(settingsFiles), JarState.promise(() -> { + List lockfileDependencies = readEmbeddedLockfileDependencies(formatterVersion); + if (lockfileDependencies != null) { + return JarState.from(lockfileDependencies, mavenProvisioner); + } P2Model model = createModelWithMirrors(); P2ModelWrapper modelWrapper = P2ModelWrapper.wrap(model); List classpath = p2Provisioner.provisionP2Dependencies(modelWrapper, mavenProvisioner, cacheDirectory).stream() @@ -133,6 +141,56 @@ public FormatterStep build() { return FormatterStep.create(formatterName, roundtrippableState, EquoStep::state, stateToFormatter); } + @Nullable + private List readEmbeddedLockfileDependencies(String version) { + String lockfileResourcePath = lockfileResourcePath(version); + if (lockfileResourcePath == null) { + return null; + } + if (!lockfileResourcePath.startsWith("/")) { + throw new IllegalArgumentException("Lockfile resource path must start with '/': " + lockfileResourcePath); + } + InputStream lockfile = EquoBasedStepBuilder.class.getResourceAsStream(lockfileResourcePath); + if (lockfile == null) { + // No lockfile embedded for this version โ€” fall back to P2 provisioning + return null; + } + String allLines; + try (lockfile) { + allLines = new String(lockfile.readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + throw Errors.asRuntime(e); + } + var dependencies = new ArrayList(); + for (String line : allLines.split("\n")) { + String trimmed = line.trim(); + if (!trimmed.isEmpty() && !trimmed.startsWith("#")) { + dependencies.add(trimmed); + } + } + if (dependencies.isEmpty()) { + throw new IllegalArgumentException("No dependencies defined in lockfile " + lockfileResourcePath); + } + return dependencies; + } + + /** + * Returns the classpath resource path of an embedded lockfile for the given formatter version. + *

+ * The default implementation always returns {@code null}, which means dependency resolution + * falls back to P2 provisioning. + *

+ * Overriding implementations should return an absolute classpath resource path (starting with + * {@code /}) that is compatible with {@link Class#getResourceAsStream(String)}. + * + * @return absolute classpath resource path of the embedded lockfile, or {@code null} to use + * P2 provisioning + */ + @Nullable + protected String lockfileResourcePath(String version) { + return null; + } + private P2Model createModelWithMirrors() { P2Model model = model(formatterVersion); if (p2Mirrors.isEmpty()) { diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java index d140d03042..961d889334 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java @@ -35,7 +35,7 @@ public final class EclipseJdtFormatterStep { private EclipseJdtFormatterStep() {} private static final String NAME = "eclipse jdt formatter"; - private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(17, "4.39"); + private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(17, "4.40"); public static String defaultVersion() { return JVM_SUPPORT.getRecommendedFormatterVersion(); @@ -76,6 +76,11 @@ protected P2Model model(String version) { return model; } + @Override + protected String lockfileResourcePath(String version) { + return "/com/diffplug/spotless/extra/eclipse_jdt_formatter/v" + version + ".lockfile"; + } + @Override public void setVersion(String version) { if (version.endsWith(".0")) { diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile new file mode 100644 index 0000000000..a16f9b3d35 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile @@ -0,0 +1,2 @@ +# Spotless formatter based on Eclipse-JDT 4.11 +org.eclipse.jdt:org.eclipse.jdt.core:3.17.0 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile new file mode 100644 index 0000000000..1723597337 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile @@ -0,0 +1,2 @@ +# Spotless formatter based on Eclipse-JDT 4.39 +org.eclipse.jdt:org.eclipse.jdt.core:3.45.0 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile new file mode 100644 index 0000000000..20212967ba --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile @@ -0,0 +1,2 @@ +# Spotless formatter based on Eclipse-JDT 4.40 +org.eclipse.jdt:org.eclipse.jdt.core:3.46.0 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile new file mode 100644 index 0000000000..cbe0b690ea --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile @@ -0,0 +1,2 @@ +# Spotless formatter based on Eclipse-JDT 4.9 +org.eclipse.jdt:org.eclipse.jdt.core:3.15.0 diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java new file mode 100644 index 0000000000..32fc5ea1da --- /dev/null +++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java @@ -0,0 +1,93 @@ +/* + * Copyright 2016-2026 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.diffplug.spotless.extra; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import com.diffplug.common.collect.ImmutableMap; +import com.diffplug.spotless.StepHarness; +import com.diffplug.spotless.Provisioner; + +import dev.equo.solstice.p2.P2Model; + +class EquoBasedStepBuilderLockfileTest { + + @Test + void missingEmbeddedLockfileFallsBackToP2() throws Exception { + P2Provisioner p2Provisioner = mock(); + Provisioner mavenProvisioner = mock(); + when(p2Provisioner.provisionP2Dependencies(any(), any(), any())).thenReturn(List.of()); + EquoBasedStepBuilder builder = builderWithLockfilePath("/com/diffplug/spotless/extra/missing.lockfile", p2Provisioner, mavenProvisioner); + StepHarness.forStep(builder.build()).test("class T {}", "class T {}"); + verify(p2Provisioner).provisionP2Dependencies(any(), any(), any()); + verifyNoInteractions(mavenProvisioner); + } + + @Test + void lockfilePathMustBeAbsolute() { + P2Provisioner p2Provisioner = mock(); + Provisioner mavenProvisioner = mock(); + EquoBasedStepBuilder builder = builderWithLockfilePath("com/diffplug/spotless/extra/empty.lockfile", + p2Provisioner, mavenProvisioner); + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> StepHarness.forStep(builder.build()).test("class T {}", "class T {}")); + assertTrue(exception.getMessage().contains("must start with '/'")); + verifyNoInteractions(p2Provisioner, mavenProvisioner); + } + + @Test + void emptyEmbeddedLockfileThrows() { + P2Provisioner p2Provisioner = mock(); + Provisioner mavenProvisioner = mock(); + EquoBasedStepBuilder builder = builderWithLockfilePath("/com/diffplug/spotless/extra/empty.lockfile", + p2Provisioner, mavenProvisioner); + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> StepHarness.forStep(builder.build()).test("class T {}", "class T {}")); + assertTrue(exception.getMessage().contains("No dependencies defined in lockfile")); + verifyNoInteractions(p2Provisioner, mavenProvisioner); + } + + private static EquoBasedStepBuilder builderWithLockfilePath(String lockfilePath, P2Provisioner p2Provisioner, Provisioner mavenProvisioner) { + return new EquoBasedStepBuilder( + "lockfile test formatter", + mavenProvisioner, + p2Provisioner, + "4.40", + state -> input -> input, + ImmutableMap.builder()) { + @Override + protected P2Model model(String version) { + return new P2Model(); + } + + @Override + protected String lockfileResourcePath(String version) { + return lockfilePath; + } + }; + } + +} diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java index ff8eeaeae7..1b204fa129 100644 --- a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java +++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java @@ -15,19 +15,27 @@ */ package com.diffplug.spotless.extra.java; -import java.util.stream.Stream; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoInteractions; + +import java.util.List; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.FieldSource; +import com.diffplug.spotless.StepHarnessWithFile; import com.diffplug.spotless.TestP2Provisioner; import com.diffplug.spotless.TestProvisioner; import com.diffplug.spotless.extra.EquoBasedStepBuilder; +import com.diffplug.spotless.extra.P2Provisioner; import com.diffplug.spotless.extra.eclipse.EquoResourceHarness; class EclipseJdtFormatterStepTest extends EquoResourceHarness { + + private static final List EMBEDDED_LOCKFILE_VERSIONS = List.of("4.9", "4.11", "4.39", EclipseJdtFormatterStep.defaultVersion()); + private static EquoBasedStepBuilder createBuilder() { return EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral(), TestP2Provisioner.defaultProvisioner()); } @@ -37,15 +45,24 @@ public EclipseJdtFormatterStepTest() { } @ParameterizedTest - @MethodSource + @FieldSource("EMBEDDED_LOCKFILE_VERSIONS") void formatWithVersion(String version) throws Exception { harnessFor(version).test("test.java", "package p; class C{}", "package p;\nclass C {\n}"); } - private static Stream formatWithVersion() { - return Stream.of("4.9", EclipseJdtFormatterStep.defaultVersion()); + @ParameterizedTest + @FieldSource("EMBEDDED_LOCKFILE_VERSIONS") + void embeddedLockfileVersionsDoNotUseP2(String version) { + P2Provisioner p2Provisioner = mock(); + EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral(), p2Provisioner); + builder.setVersion(version); + StepHarnessWithFile.forStep(this, builder.build()).test( + "test.java", + "package p; class C{}", + "package p;\nclass C {\n}"); + verifyNoInteractions(p2Provisioner); } /** New format interface requires source file information to distinguish module-info from compilation unit */ diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java new file mode 100644 index 0000000000..602639eafc --- /dev/null +++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java @@ -0,0 +1,278 @@ +/* + * Copyright 2016-2026 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.diffplug.spotless.extra.java; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.StringReader; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Redirect; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.jar.JarInputStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +/** + * Executable class for validating or updating embedded JDT lockfiles from Eclipse P2 metadata. + *

+ * Intended for manual execution via the following entrypoints: + *

+ * - {@link EclipseJdtLockfileMetadataTool.Verify#main(String[])}
+ * - {@link EclipseJdtLockfileMetadataTool.Update#main(String[])}
+ * 
+ */ +public class EclipseJdtLockfileMetadataTool { + + private static final List TARGET_VERSIONS = List.of("4.9", "4.11", "4.39", "4.40"); + + /** + * Verifies the JDT lockfiles at {@link #TARGET_VERSIONS} against Eclipse P2 metadata. + */ + public static class Verify { + + public static void main(String[] args) { + run(false); + } + + } + + /** + * Updates the JDT lockfiles at {@link #TARGET_VERSIONS} from Eclipse P2 metadata. + *

+ * Missing lockfiles will be created. + */ + public static class Update { + + public static void main(String[] args) { + run(true); + } + + } + + private static final String JDT_ID = "org.eclipse.jdt.core"; + private static final String JDT_MAVEN_PREFIX = "org.eclipse.jdt:org.eclipse.jdt.core:"; + private static final Pattern MAJOR_MINOR_PATCH = Pattern.compile("^([0-9]+\\.[0-9]+\\.[0-9]+).*$"); + private static final int MAX_TRAVERSAL = 200; + + private static final HttpClient HTTP = HttpClient.newBuilder() + .followRedirects(Redirect.NORMAL) + .build(); + + private static void run(boolean update) { + Path lockfileDir = lockfileDir(); + Map lockfilesByVersion = targetLockfiles(lockfileDir, TARGET_VERSIONS); + + int checked = 0; + int failures = 0; + for (Map.Entry entry : lockfilesByVersion.entrySet()) { + checked++; + String eclipseVersion = entry.getKey(); + Path lockfilePath = entry.getValue(); + try { + String bundleVersion = resolveBundleVersion(eclipseVersion); + String expectedCoordinate = JDT_MAVEN_PREFIX + normalizeToMavenVersion(bundleVersion); + if (update) { + Files.writeString(lockfilePath, lockfileContent(eclipseVersion, expectedCoordinate), StandardCharsets.UTF_8); + System.out.println("WROTE v" + eclipseVersion + " -> " + expectedCoordinate); + } else { + if (!Files.exists(lockfilePath)) { + System.err.println("MISSING v" + eclipseVersion + " -> " + lockfilePath + " (expected: " + expectedCoordinate + ")"); + failures++; + continue; + } + String actualCoordinate = lockfileCoordinate(lockfilePath); + if (!expectedCoordinate.equals(actualCoordinate)) { + System.err.println("MISMATCH v" + eclipseVersion + " -> expected: " + expectedCoordinate + " actual: " + actualCoordinate); + failures++; + continue; + } + System.out.println("OK v" + eclipseVersion + " -> " + actualCoordinate); + } + } catch (Exception e) { + System.err.println("ERROR v" + eclipseVersion + " -> " + e.getMessage()); + failures++; + } + } + if (update) { + System.out.println("Updated " + checked + " lockfile(s)."); + } else { + System.out.println("Verified " + checked + " lockfile(s) with " + failures + " issue(s)."); + } + } + + private static Map targetLockfiles(Path lockfileDir, List versions) { + Map targets = new LinkedHashMap<>(); + for (String version : versions) { + targets.put(version, lockfileDir.resolve("v" + version + ".lockfile")); + } + return targets; + } + + private static String lockfileContent(String eclipseVersion, String coordinate) { + return "# Spotless formatter based on Eclipse-JDT " + eclipseVersion + "\n" + coordinate + "\n"; + } + + private static String lockfileCoordinate(Path lockfilePath) throws IOException { + try (var lines = Files.lines(lockfilePath, StandardCharsets.UTF_8)) { + return lines.map(String::trim) + .filter(line -> !line.isEmpty()) + .filter(line -> !line.startsWith("#")) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("No dependency coordinate found in " + lockfilePath)); + } + } + + private static Path lockfileDir() { + Path fromRepoRoot = Path.of("lib-extra", "src", "main", "resources", "com", "diffplug", "spotless", "extra", "eclipse_jdt_formatter"); + if (Files.isDirectory(fromRepoRoot)) { + return fromRepoRoot; + } + Path fromLibExtra = Path.of("src", "main", "resources", "com", "diffplug", "spotless", "extra", "eclipse_jdt_formatter"); + if (Files.isDirectory(fromLibExtra)) { + return fromLibExtra; + } + throw new IllegalStateException("Unable to locate eclipse_jdt_formatter resource directory"); + } + + private static String resolveBundleVersion(String eclipseVersion) throws Exception { + ArrayDeque queue = new ArrayDeque<>(); + Set visited = new LinkedHashSet<>(); + queue.add("https://download.eclipse.org/eclipse/updates/" + eclipseVersion + "/"); + int traversed = 0; + while (!queue.isEmpty()) { + String repoUrl = queue.removeFirst(); + if (!visited.add(repoUrl)) { + continue; + } + traversed++; + if (traversed > MAX_TRAVERSAL) { + throw new IllegalStateException("Traversal exceeded " + MAX_TRAVERSAL + " repositories while resolving Eclipse " + eclipseVersion); + } + Optional contentXml = readJarEntry(repoUrl, "content.jar", "content.xml"); + if (contentXml.isPresent()) { + String bundleVersion = extractBundleVersion(contentXml.get()); + if (bundleVersion != null) { + return bundleVersion; + } + } + Optional compositeXml = readJarEntry(repoUrl, "compositeContent.jar", "compositeContent.xml"); + if (compositeXml.isPresent()) { + queue.addAll(compositeChildren(repoUrl, compositeXml.get(), visited)); + } + } + throw new IllegalStateException("Unable to resolve " + JDT_ID + " from Eclipse " + eclipseVersion + " update site"); + } + + private static Optional readJarEntry(String repoUrl, String jarName, String entryName) throws Exception { + Optional bytes = download(repoUrl + jarName); + if (bytes.isEmpty()) { + return Optional.empty(); + } + try (JarInputStream jarInputStream = new JarInputStream(new ByteArrayInputStream(bytes.get()))) { + var entry = jarInputStream.getNextJarEntry(); + while (entry != null) { + if (!entry.isDirectory() && entryName.equals(entry.getName())) { + return Optional.of(new String(jarInputStream.readAllBytes(), StandardCharsets.UTF_8)); + } + entry = jarInputStream.getNextJarEntry(); + } + } + throw new IllegalStateException("Entry " + entryName + " not found in " + repoUrl + jarName); + } + + private static Optional download(String url) throws Exception { + HttpRequest request = HttpRequest.newBuilder(URI.create(url)).GET().build(); + HttpResponse response = HTTP.send(request, HttpResponse.BodyHandlers.ofByteArray()); + if (response.statusCode() == 200) { + return Optional.of(response.body()); + } + if (response.statusCode() == 404) { + return Optional.empty(); + } + throw new IOException("Unexpected HTTP status " + response.statusCode() + " from " + url); + } + + private static String extractBundleVersion(String contentXml) throws Exception { + Document document = parseXml(contentXml); + var xpath = XPathFactory.newInstance().newXPath(); + String value = (String) xpath.evaluate("//unit[@id='" + JDT_ID + "']/@version", document, XPathConstants.STRING); + return value.isBlank() ? null : value; + } + + private static List compositeChildren(String parentRepoUrl, String compositeXml, Set visited) throws Exception { + Document document = parseXml(compositeXml); + var xpath = XPathFactory.newInstance().newXPath(); + var nodes = (NodeList) xpath.evaluate("//child/@location", document, XPathConstants.NODESET); + List children = new ArrayList<>(nodes.getLength()); + for (int i = 0; i < nodes.getLength(); i++) { + String location = nodes.item(i).getNodeValue(); + String childUrl = parentRepoUrl + trimTrailingSlash(location) + "/"; + if (!visited.contains(childUrl)) { + children.add(childUrl); + } + } + return children; + } + + private static String trimTrailingSlash(String value) { + return value.endsWith("/") ? value.substring(0, value.length() - 1) : value; + } + + private static Document parseXml(String xml) throws Exception { + var factory = DocumentBuilderFactory.newInstance(); + // Disable DTDs/external entities and enable secure processing to prevent XXE + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + factory.setExpandEntityReferences(false); + factory.setNamespaceAware(false); + var builder = factory.newDocumentBuilder(); + return builder.parse(new InputSource(new StringReader(xml))); + } + + private static String normalizeToMavenVersion(String bundleVersion) { + Matcher matcher = MAJOR_MINOR_PATCH.matcher(bundleVersion); + if (!matcher.matches()) { + throw new IllegalArgumentException("Unexpected org.eclipse.jdt.core bundle version: " + bundleVersion); + } + return matcher.group(1); + } + +} diff --git a/lib-extra/src/test/resources/com/diffplug/spotless/extra/empty.lockfile b/lib-extra/src/test/resources/com/diffplug/spotless/extra/empty.lockfile new file mode 100644 index 0000000000..d25c36fa9a --- /dev/null +++ b/lib-extra/src/test/resources/com/diffplug/spotless/extra/empty.lockfile @@ -0,0 +1 @@ +# intentionally empty lockfile used by EquoBasedStepBuilderLockfileTest From 9c204fbf13ba9d9cd0d819ea6f4ff6d16e1c84e8 Mon Sep 17 00:00:00 2001 From: Stefano Cordio Date: Sat, 25 Jul 2026 13:52:25 +0200 Subject: [PATCH 2/7] Add summary to `CHANGES.md` files --- CHANGES.md | 1 + plugin-gradle/CHANGES.md | 1 + plugin-maven/CHANGES.md | 1 + 3 files changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 67dc8e1940..905c30e5b9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Changes - Bump default `greclipse` version to latest `4.39` -> `4.40`. ([#2989](https://github.com/diffplug/spotless/pull/2989)) +- Add embedded lockfiles to Eclipse JDT (4.9, 4.11, 4.39, 4.40), bump version to latest `4.39` -> `4.40`. ([#1996](https://github.com/diffplug/spotless/issues/1996)) ## [4.8.0] - 2026-06-29 ### Added diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 17f8167d02..e789acc3d6 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -7,6 +7,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( - Prevent parallel Gradle input fingerprinting from failing when `toggleOffOn()` wraps a slow lazy formatter step with no matching target files. ([#2994](https://github.com/diffplug/spotless/pull/2994)) ### Changes - Bump default `greclipse` version to latest `4.39` -> `4.40`. ([#2989](https://github.com/diffplug/spotless/pull/2989)) +- Add embedded lockfiles to Eclipse JDT (4.9, 4.11, 4.39, 4.40), bump version to latest `4.39` -> `4.40`. ([#1996](https://github.com/diffplug/spotless/issues/1996)) ## [8.8.0] - 2026-06-29 ### Added diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index a7e517860c..d62b6cf49f 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -5,6 +5,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Changes - Bump default `greclipse` version to latest `4.39` -> `4.40`. ([#2989](https://github.com/diffplug/spotless/pull/2989)) +- Add embedded lockfiles to Eclipse JDT (4.9, 4.11, 4.39, 4.40), bump version to latest `4.39` -> `4.40`. ([#1996](https://github.com/diffplug/spotless/issues/1996)) ## [3.8.0] - 2026-06-29 ### Added From e70065e3b23611d6269ced13f0a05de383984cdc Mon Sep 17 00:00:00 2001 From: Stefano Cordio Date: Tue, 28 Jul 2026 11:18:17 +0200 Subject: [PATCH 3/7] Fix format violations --- .../com/diffplug/spotless/extra/EquoBasedStepBuilder.java | 6 ++---- .../spotless/extra/EquoBasedStepBuilderLockfileTest.java | 2 +- .../spotless/extra/java/EclipseJdtLockfileMetadataTool.java | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java index 4d0a1ef624..b4e6698f8c 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java @@ -141,8 +141,7 @@ public FormatterStep build() { return FormatterStep.create(formatterName, roundtrippableState, EquoStep::state, stateToFormatter); } - @Nullable - private List readEmbeddedLockfileDependencies(String version) { + private @Nullable List readEmbeddedLockfileDependencies(String version) { String lockfileResourcePath = lockfileResourcePath(version); if (lockfileResourcePath == null) { return null; @@ -186,8 +185,7 @@ private List readEmbeddedLockfileDependencies(String version) { * @return absolute classpath resource path of the embedded lockfile, or {@code null} to use * P2 provisioning */ - @Nullable - protected String lockfileResourcePath(String version) { + protected @Nullable String lockfileResourcePath(String version) { return null; } diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java index 32fc5ea1da..df7b37de8a 100644 --- a/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java +++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java @@ -28,8 +28,8 @@ import org.junit.jupiter.api.Test; import com.diffplug.common.collect.ImmutableMap; -import com.diffplug.spotless.StepHarness; import com.diffplug.spotless.Provisioner; +import com.diffplug.spotless.StepHarness; import dev.equo.solstice.p2.P2Model; diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java index 602639eafc..930b533e50 100644 --- a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java +++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java @@ -90,8 +90,8 @@ public static void main(String[] args) { private static final int MAX_TRAVERSAL = 200; private static final HttpClient HTTP = HttpClient.newBuilder() - .followRedirects(Redirect.NORMAL) - .build(); + .followRedirects(Redirect.NORMAL) + .build(); private static void run(boolean update) { Path lockfileDir = lockfileDir(); From 8bb51946c0bccdb368f9398b4286ce929162e6f0 Mon Sep 17 00:00:00 2001 From: Stefano Cordio Date: Tue, 28 Jul 2026 13:50:51 +0200 Subject: [PATCH 4/7] Add missing transitive coordinates, remove lockfiles with underlying version ranges --- .../spotless/extra/EquoBasedStepBuilder.java | 2 +- .../eclipse_jdt_formatter/v4.11.lockfile | 2 - .../eclipse_jdt_formatter/v4.39.lockfile | 18 +++ .../eclipse_jdt_formatter/v4.40.lockfile | 18 +++ .../extra/eclipse_jdt_formatter/v4.9.lockfile | 2 - .../java/EclipseJdtFormatterStepTest.java | 2 +- .../java/EclipseJdtLockfileMetadataTool.java | 149 ++++++++++++++++-- 7 files changed, 175 insertions(+), 18 deletions(-) delete mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile delete mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java index b4e6698f8c..462c9832c6 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java @@ -128,7 +128,7 @@ public FormatterStep build() { var roundtrippableState = new EquoStep(formatterVersion, settingProperties, settingXml, FileSignature.promise(settingsFiles), JarState.promise(() -> { List lockfileDependencies = readEmbeddedLockfileDependencies(formatterVersion); if (lockfileDependencies != null) { - return JarState.from(lockfileDependencies, mavenProvisioner); + return JarState.withoutTransitives(lockfileDependencies, mavenProvisioner); } P2Model model = createModelWithMirrors(); P2ModelWrapper modelWrapper = P2ModelWrapper.wrap(model); diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile deleted file mode 100644 index a16f9b3d35..0000000000 --- a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile +++ /dev/null @@ -1,2 +0,0 @@ -# Spotless formatter based on Eclipse-JDT 4.11 -org.eclipse.jdt:org.eclipse.jdt.core:3.17.0 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile index 1723597337..9b82d35258 100644 --- a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile @@ -1,2 +1,20 @@ # Spotless formatter based on Eclipse-JDT 4.39 org.eclipse.jdt:org.eclipse.jdt.core:3.45.0 +net.java.dev.jna:jna-platform:5.18.1 +net.java.dev.jna:jna:5.18.1 +org.eclipse.jdt:ecj:3.45.0 +org.eclipse.platform:org.eclipse.core.commands:3.12.500 +org.eclipse.platform:org.eclipse.core.contenttype:3.9.800 +org.eclipse.platform:org.eclipse.core.expressions:3.9.500 +org.eclipse.platform:org.eclipse.core.filesystem:1.11.400 +org.eclipse.platform:org.eclipse.core.jobs:3.15.700 +org.eclipse.platform:org.eclipse.core.resources:3.23.200 +org.eclipse.platform:org.eclipse.core.runtime:3.34.200 +org.eclipse.platform:org.eclipse.equinox.app:1.7.600 +org.eclipse.platform:org.eclipse.equinox.common:3.20.300 +org.eclipse.platform:org.eclipse.equinox.preferences:3.12.100 +org.eclipse.platform:org.eclipse.equinox.registry:3.12.600 +org.eclipse.platform:org.eclipse.osgi:3.24.100 +org.eclipse.platform:org.eclipse.text:3.14.600 +org.osgi:org.osgi.service.prefs:1.1.2 +org.osgi:osgi.annotation:8.0.1 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile index 20212967ba..1b8309d0ba 100644 --- a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile @@ -1,2 +1,20 @@ # Spotless formatter based on Eclipse-JDT 4.40 org.eclipse.jdt:org.eclipse.jdt.core:3.46.0 +net.java.dev.jna:jna-platform:5.18.1 +net.java.dev.jna:jna:5.18.1 +org.eclipse.jdt:ecj:3.46.0 +org.eclipse.platform:org.eclipse.core.commands:3.12.500 +org.eclipse.platform:org.eclipse.core.contenttype:3.9.800 +org.eclipse.platform:org.eclipse.core.expressions:3.9.600 +org.eclipse.platform:org.eclipse.core.filesystem:1.11.400 +org.eclipse.platform:org.eclipse.core.jobs:3.15.700 +org.eclipse.platform:org.eclipse.core.resources:3.24.0 +org.eclipse.platform:org.eclipse.core.runtime:3.34.200 +org.eclipse.platform:org.eclipse.equinox.app:1.7.600 +org.eclipse.platform:org.eclipse.equinox.common:3.20.400 +org.eclipse.platform:org.eclipse.equinox.preferences:3.12.100 +org.eclipse.platform:org.eclipse.equinox.registry:3.12.600 +org.eclipse.platform:org.eclipse.osgi:3.24.200 +org.eclipse.platform:org.eclipse.text:3.14.700 +org.osgi:org.osgi.service.prefs:1.1.2 +org.osgi:osgi.annotation:8.0.1 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile deleted file mode 100644 index cbe0b690ea..0000000000 --- a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile +++ /dev/null @@ -1,2 +0,0 @@ -# Spotless formatter based on Eclipse-JDT 4.9 -org.eclipse.jdt:org.eclipse.jdt.core:3.15.0 diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java index 1b204fa129..73d50baa33 100644 --- a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java +++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java @@ -34,7 +34,7 @@ class EclipseJdtFormatterStepTest extends EquoResourceHarness { - private static final List EMBEDDED_LOCKFILE_VERSIONS = List.of("4.9", "4.11", "4.39", EclipseJdtFormatterStep.defaultVersion()); + private static final List EMBEDDED_LOCKFILE_VERSIONS = List.of("4.39", EclipseJdtFormatterStep.defaultVersion()); private static EquoBasedStepBuilder createBuilder() { return EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral(), TestP2Provisioner.defaultProvisioner()); diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java index 930b533e50..ffeb2a50cc 100644 --- a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java +++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java @@ -34,6 +34,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.StringJoiner; import java.util.jar.JarInputStream; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -44,6 +45,7 @@ import javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; @@ -58,7 +60,13 @@ */ public class EclipseJdtLockfileMetadataTool { - private static final List TARGET_VERSIONS = List.of("4.9", "4.11", "4.39", "4.40"); + // Lockfiles are only provided for Eclipse versions whose org.eclipse.jdt.core POM on Maven Central + // declares exact transitive dependency versions rather than version ranges (e.g. "[3.12.0,4.0.0)"). + // org.eclipse.jdt.core:3.32.0 is the first version on Maven Central with exact version dependencies; + // the likely cause is eclipse-platform/eclipse.platform.releng#135 (issue #128), which updated the + // CBI aggregator configuration to map bundle requirements to their resolved versions rather than the + // OSGi version ranges from MANIFEST.MF. + private static final List TARGET_VERSIONS = List.of("4.39", "4.40"); /** * Verifies the JDT lockfiles at {@link #TARGET_VERSIONS} against Eclipse P2 metadata. @@ -105,23 +113,24 @@ private static void run(boolean update) { Path lockfilePath = entry.getValue(); try { String bundleVersion = resolveBundleVersion(eclipseVersion); - String expectedCoordinate = JDT_MAVEN_PREFIX + normalizeToMavenVersion(bundleVersion); + String rootCoordinate = JDT_MAVEN_PREFIX + normalizeToMavenVersion(bundleVersion); if (update) { - Files.writeString(lockfilePath, lockfileContent(eclipseVersion, expectedCoordinate), StandardCharsets.UTF_8); - System.out.println("WROTE v" + eclipseVersion + " -> " + expectedCoordinate); + List allCoordinates = resolveTransitiveClosure(rootCoordinate); + Files.writeString(lockfilePath, lockfileContent(eclipseVersion, allCoordinates), StandardCharsets.UTF_8); + System.out.println("WROTE v" + eclipseVersion + " -> " + allCoordinates.size() + " artifact(s), root: " + rootCoordinate); } else { if (!Files.exists(lockfilePath)) { - System.err.println("MISSING v" + eclipseVersion + " -> " + lockfilePath + " (expected: " + expectedCoordinate + ")"); + System.err.println("MISSING v" + eclipseVersion + " -> " + lockfilePath + " (expected root: " + rootCoordinate + ")"); failures++; continue; } - String actualCoordinate = lockfileCoordinate(lockfilePath); - if (!expectedCoordinate.equals(actualCoordinate)) { - System.err.println("MISMATCH v" + eclipseVersion + " -> expected: " + expectedCoordinate + " actual: " + actualCoordinate); + String actualRootCoordinate = lockfileRootCoordinate(lockfilePath); + if (!rootCoordinate.equals(actualRootCoordinate)) { + System.err.println("MISMATCH v" + eclipseVersion + " -> expected root: " + rootCoordinate + " actual: " + actualRootCoordinate); failures++; continue; } - System.out.println("OK v" + eclipseVersion + " -> " + actualCoordinate); + System.out.println("OK v" + eclipseVersion + " -> " + actualRootCoordinate); } } catch (Exception e) { System.err.println("ERROR v" + eclipseVersion + " -> " + e.getMessage()); @@ -143,11 +152,14 @@ private static Map targetLockfiles(Path lockfileDir, List return targets; } - private static String lockfileContent(String eclipseVersion, String coordinate) { - return "# Spotless formatter based on Eclipse-JDT " + eclipseVersion + "\n" + coordinate + "\n"; + private static String lockfileContent(String eclipseVersion, List coordinates) { + String prefix = "# Spotless formatter based on Eclipse-JDT " + eclipseVersion + "\n"; + StringJoiner joiner = new StringJoiner("\n", prefix, "\n"); + coordinates.forEach(joiner::add); + return joiner.toString(); } - private static String lockfileCoordinate(Path lockfilePath) throws IOException { + private static String lockfileRootCoordinate(Path lockfilePath) throws IOException { try (var lines = Files.lines(lockfilePath, StandardCharsets.UTF_8)) { return lines.map(String::trim) .filter(line -> !line.isEmpty()) @@ -169,6 +181,119 @@ private static Path lockfileDir() { throw new IllegalStateException("Unable to locate eclipse_jdt_formatter resource directory"); } + /** + * Resolves the full Maven transitive closure of the given root coordinate using + * Maven Central POM traversal, with highest-version-wins conflict resolution. + *

+ * Optional dependencies and test/provided/system-scoped dependencies are excluded. + * The root coordinate appears first in the result; remaining entries are sorted. + */ + private static List resolveTransitiveClosure(String rootCoordinate) throws Exception { + // "g:a" -> selected version + Map selected = new LinkedHashMap<>(); + ArrayDeque queue = new ArrayDeque<>(); + queue.add(rootCoordinate.split(":")); + + while (!queue.isEmpty()) { + String[] parts = queue.removeFirst(); + String groupId = parts[0], artifactId = parts[1], version = parts[2]; + String key = groupId + ":" + artifactId; + + String existing = selected.get(key); + if (existing != null && compareVersions(existing, version) >= 0) { + continue; + } + selected.put(key, version); + + List deps = fetchNonOptionalDepsFromPom(groupId, artifactId, version); + for (String[] dep : deps) { + String depKey = dep[0] + ":" + dep[1]; + String existingDepVersion = selected.get(depKey); + if (existingDepVersion == null || compareVersions(existingDepVersion, dep[2]) < 0) { + queue.add(dep); + } + } + } + + String[] rootParts = rootCoordinate.split(":"); + String rootKey = rootParts[0] + ":" + rootParts[1]; + List result = new ArrayList<>(); + result.add(rootCoordinate); + selected.entrySet().stream() + .filter(e -> !e.getKey().equals(rootKey)) + .map(e -> e.getKey() + ":" + e.getValue()) + .sorted() + .forEach(result::add); + return result; + } + + private static List fetchNonOptionalDepsFromPom(String groupId, String artifactId, String version) throws Exception { + String path = groupId.replace('.', '/') + "/" + artifactId + "/" + version + "/" + artifactId + "-" + version + ".pom"; + Optional bytes = download("https://repo1.maven.org/maven2/" + path); + if (bytes.isEmpty()) { + System.err.println("WARNING: POM not found on Maven Central: " + groupId + ":" + artifactId + ":" + version); + return List.of(); + } + return parseNonOptionalDepsFromPom(new String(bytes.get(), StandardCharsets.UTF_8)); + } + + private static List parseNonOptionalDepsFromPom(String pomXml) throws Exception { + Document doc = parseXml(pomXml); + var xpath = XPathFactory.newInstance().newXPath(); + NodeList deps = (NodeList) xpath.evaluate("/project/dependencies/dependency", doc, XPathConstants.NODESET); + List result = new ArrayList<>(); + for (int i = 0; i < deps.getLength(); i++) { + Element dep = (Element) deps.item(i); + String g = textContent(dep, "groupId"); + String a = textContent(dep, "artifactId"); + String v = textContent(dep, "version"); + String scope = textContent(dep, "scope"); + String optional = textContent(dep, "optional"); + String type = textContent(dep, "type"); + + if ("true".equals(optional)) + continue; + if ("test".equals(scope) || "provided".equals(scope) || "system".equals(scope)) + continue; + if ("pom".equals(type)) + continue; + if (v.isEmpty() || v.startsWith("[") || v.startsWith("(") || v.startsWith("$")) + continue; + if (g.isEmpty() || a.isEmpty()) + continue; + + result.add(new String[]{g, a, v}); + } + return result; + } + + private static String textContent(Element parent, String tag) { + var nodes = parent.getElementsByTagName(tag); + return nodes.getLength() > 0 ? nodes.item(0).getTextContent().trim() : ""; + } + + /** Compares two version strings numerically, segment by segment. Returns positive if v1 > v2. */ + private static int compareVersions(String v1, String v2) { + String[] p1 = v1.split("\\."); + String[] p2 = v2.split("\\."); + int len = Math.max(p1.length, p2.length); + for (int i = 0; i < len; i++) { + int n1 = i < p1.length ? parseVersionSegment(p1[i]) : 0; + int n2 = i < p2.length ? parseVersionSegment(p2[i]) : 0; + if (n1 != n2) + return Integer.compare(n1, n2); + } + return 0; + } + + private static int parseVersionSegment(String segment) { + try { + return Integer.parseInt(segment); + } catch (NumberFormatException e) { + return 0; + } + } + private static String resolveBundleVersion(String eclipseVersion) throws Exception { ArrayDeque queue = new ArrayDeque<>(); Set visited = new LinkedHashSet<>(); From 71c40eb575ce341785a21e69a668794b5e32631d Mon Sep 17 00:00:00 2001 From: Stefano Cordio Date: Tue, 28 Jul 2026 15:44:40 +0200 Subject: [PATCH 5/7] Add support for version range based lockfiles, improve lockfile-first coverage --- .../eclipse_jdt_formatter/v4.11.lockfile | 16 + .../eclipse_jdt_formatter/v4.25.lockfile | 17 + .../eclipse_jdt_formatter/v4.26.lockfile | 17 + .../eclipse_jdt_formatter/v4.39.lockfile | 3 +- .../eclipse_jdt_formatter/v4.40.lockfile | 5 +- .../extra/eclipse_jdt_formatter/v4.9.lockfile | 16 + .../EquoBasedStepBuilderLockfileTest.java | 28 ++ .../java/EclipseJdtFormatterStepTest.java | 12 +- .../java/EclipseJdtLockfileMetadataTool.java | 290 +++++------------- .../diffplug/spotless/extra/two-deps.lockfile | 3 + 10 files changed, 180 insertions(+), 227 deletions(-) create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.25.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.26.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile create mode 100644 lib-extra/src/test/resources/com/diffplug/spotless/extra/two-deps.lockfile diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile new file mode 100644 index 0000000000..47a5454c25 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.11 +org.eclipse.jdt:org.eclipse.jdt.core:3.17.0 +org.eclipse.platform:org.eclipse.core.commands:3.9.300 +org.eclipse.platform:org.eclipse.core.contenttype:3.7.300 +org.eclipse.platform:org.eclipse.core.expressions:3.6.300 +org.eclipse.platform:org.eclipse.core.filesystem:1.7.300 +org.eclipse.platform:org.eclipse.core.jobs:3.10.300 +org.eclipse.platform:org.eclipse.core.resources:3.13.300 +org.eclipse.platform:org.eclipse.core.runtime:3.15.200 +org.eclipse.platform:org.eclipse.equinox.app:1.4.100 +org.eclipse.platform:org.eclipse.equinox.common:3.10.300 +org.eclipse.platform:org.eclipse.equinox.preferences:3.7.300 +org.eclipse.platform:org.eclipse.equinox.registry:3.8.300 +org.eclipse.platform:org.eclipse.equinox.supplement:1.8.200 +org.eclipse.platform:org.eclipse.osgi:3.13.300 +org.eclipse.platform:org.eclipse.text:3.8.100 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.25.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.25.lockfile new file mode 100644 index 0000000000..c51599e651 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.25.lockfile @@ -0,0 +1,17 @@ +# Spotless formatter based on Eclipse-JDT 4.25 +org.eclipse.jdt:org.eclipse.jdt.core:3.31.0 +org.eclipse.platform:org.eclipse.core.commands:3.10.200 +org.eclipse.platform:org.eclipse.core.contenttype:3.8.200 +org.eclipse.platform:org.eclipse.core.expressions:3.8.200 +org.eclipse.platform:org.eclipse.core.filesystem:1.9.500 +org.eclipse.platform:org.eclipse.core.jobs:3.13.100 +org.eclipse.platform:org.eclipse.core.resources:3.18.0 +org.eclipse.platform:org.eclipse.core.runtime:3.26.0 +org.eclipse.platform:org.eclipse.equinox.app:1.6.200 +org.eclipse.platform:org.eclipse.equinox.common:3.16.200 +org.eclipse.platform:org.eclipse.equinox.preferences:3.10.100 +org.eclipse.platform:org.eclipse.equinox.registry:3.11.200 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.600 +org.eclipse.platform:org.eclipse.osgi:3.18.100 +org.eclipse.platform:org.eclipse.text:3.12.200 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.26.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.26.lockfile new file mode 100644 index 0000000000..b4c053796e --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.26.lockfile @@ -0,0 +1,17 @@ +# Spotless formatter based on Eclipse-JDT 4.26 +org.eclipse.jdt:org.eclipse.jdt.core:3.32.0 +org.eclipse.platform:org.eclipse.core.commands:3.10.300 +org.eclipse.platform:org.eclipse.core.contenttype:3.8.200 +org.eclipse.platform:org.eclipse.core.expressions:3.8.200 +org.eclipse.platform:org.eclipse.core.filesystem:1.9.500 +org.eclipse.platform:org.eclipse.core.jobs:3.13.200 +org.eclipse.platform:org.eclipse.core.resources:3.18.100 +org.eclipse.platform:org.eclipse.core.runtime:3.26.100 +org.eclipse.platform:org.eclipse.equinox.app:1.6.200 +org.eclipse.platform:org.eclipse.equinox.common:3.17.0 +org.eclipse.platform:org.eclipse.equinox.preferences:3.10.100 +org.eclipse.platform:org.eclipse.equinox.registry:3.11.200 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.600 +org.eclipse.platform:org.eclipse.osgi:3.18.200 +org.eclipse.platform:org.eclipse.text:3.12.300 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile index 9b82d35258..e86e95e3d3 100644 --- a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile @@ -1,7 +1,6 @@ # Spotless formatter based on Eclipse-JDT 4.39 org.eclipse.jdt:org.eclipse.jdt.core:3.45.0 net.java.dev.jna:jna-platform:5.18.1 -net.java.dev.jna:jna:5.18.1 org.eclipse.jdt:ecj:3.45.0 org.eclipse.platform:org.eclipse.core.commands:3.12.500 org.eclipse.platform:org.eclipse.core.contenttype:3.9.800 @@ -14,7 +13,7 @@ org.eclipse.platform:org.eclipse.equinox.app:1.7.600 org.eclipse.platform:org.eclipse.equinox.common:3.20.300 org.eclipse.platform:org.eclipse.equinox.preferences:3.12.100 org.eclipse.platform:org.eclipse.equinox.registry:3.12.600 +org.eclipse.platform:org.eclipse.equinox.supplement:1.12.200 org.eclipse.platform:org.eclipse.osgi:3.24.100 org.eclipse.platform:org.eclipse.text:3.14.600 org.osgi:org.osgi.service.prefs:1.1.2 -org.osgi:osgi.annotation:8.0.1 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile index 1b8309d0ba..f30d63dc8c 100644 --- a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile @@ -1,20 +1,19 @@ # Spotless formatter based on Eclipse-JDT 4.40 org.eclipse.jdt:org.eclipse.jdt.core:3.46.0 net.java.dev.jna:jna-platform:5.18.1 -net.java.dev.jna:jna:5.18.1 org.eclipse.jdt:ecj:3.46.0 org.eclipse.platform:org.eclipse.core.commands:3.12.500 org.eclipse.platform:org.eclipse.core.contenttype:3.9.800 org.eclipse.platform:org.eclipse.core.expressions:3.9.600 org.eclipse.platform:org.eclipse.core.filesystem:1.11.400 -org.eclipse.platform:org.eclipse.core.jobs:3.15.700 +org.eclipse.platform:org.eclipse.core.jobs:3.15.800 org.eclipse.platform:org.eclipse.core.resources:3.24.0 org.eclipse.platform:org.eclipse.core.runtime:3.34.200 org.eclipse.platform:org.eclipse.equinox.app:1.7.600 org.eclipse.platform:org.eclipse.equinox.common:3.20.400 org.eclipse.platform:org.eclipse.equinox.preferences:3.12.100 org.eclipse.platform:org.eclipse.equinox.registry:3.12.600 +org.eclipse.platform:org.eclipse.equinox.supplement:1.12.300 org.eclipse.platform:org.eclipse.osgi:3.24.200 org.eclipse.platform:org.eclipse.text:3.14.700 org.osgi:org.osgi.service.prefs:1.1.2 -org.osgi:osgi.annotation:8.0.1 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile new file mode 100644 index 0000000000..19ddfb2389 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.9 +org.eclipse.jdt:org.eclipse.jdt.core:3.15.0 +org.eclipse.platform:org.eclipse.core.commands:3.9.200 +org.eclipse.platform:org.eclipse.core.contenttype:3.7.100 +org.eclipse.platform:org.eclipse.core.expressions:3.6.200 +org.eclipse.platform:org.eclipse.core.filesystem:1.7.200 +org.eclipse.platform:org.eclipse.core.jobs:3.10.100 +org.eclipse.platform:org.eclipse.core.resources:3.13.100 +org.eclipse.platform:org.eclipse.core.runtime:3.15.0 +org.eclipse.platform:org.eclipse.equinox.app:1.3.600 +org.eclipse.platform:org.eclipse.equinox.common:3.10.100 +org.eclipse.platform:org.eclipse.equinox.preferences:3.7.200 +org.eclipse.platform:org.eclipse.equinox.registry:3.8.100 +org.eclipse.platform:org.eclipse.equinox.supplement:1.8.100 +org.eclipse.platform:org.eclipse.osgi:3.13.100 +org.eclipse.platform:org.eclipse.text:3.7.0 diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java index df7b37de8a..b6d859e559 100644 --- a/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java +++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java @@ -15,17 +15,26 @@ */ package com.diffplug.spotless.extra; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyCollection; +import static org.mockito.ArgumentMatchers.assertArg; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; +import java.io.File; +import java.nio.file.Path; +import java.util.Collection; import java.util.List; +import java.util.Set; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import com.diffplug.common.collect.ImmutableMap; import com.diffplug.spotless.Provisioner; @@ -70,6 +79,25 @@ void emptyEmbeddedLockfileThrows() { verifyNoInteractions(p2Provisioner, mavenProvisioner); } + @Test + void embeddedLockfileUsesWithoutTransitivesAndAllCoordinates(@TempDir Path tempDir) throws Exception { + P2Provisioner p2Provisioner = mock(); + Provisioner mavenProvisioner = mock(); + File dummyJar = tempDir.resolve("spotless-lockfile-test.jar").toFile(); + assertTrue(dummyJar.createNewFile()); + when(mavenProvisioner.provisionWithTransitives(eq(false), anyCollection())).thenReturn(Set.of(dummyJar)); + EquoBasedStepBuilder builder = builderWithLockfilePath("/com/diffplug/spotless/extra/two-deps.lockfile", + p2Provisioner, mavenProvisioner); + + StepHarness.forStep(builder.build()).test("class T {}", "class T {}"); + + verify(mavenProvisioner).provisionWithTransitives(eq(false), assertArg((Collection coords) -> assertThat(coords) + .containsExactlyInAnyOrder( + "org.eclipse.jdt:org.eclipse.jdt.core:3.45.0", + "org.eclipse.jdt:ecj:3.45.0"))); + verifyNoInteractions(p2Provisioner); + } + private static EquoBasedStepBuilder builderWithLockfilePath(String lockfilePath, P2Provisioner p2Provisioner, Provisioner mavenProvisioner) { return new EquoBasedStepBuilder( "lockfile test formatter", diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java index 73d50baa33..e69549da6f 100644 --- a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java +++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java @@ -34,7 +34,17 @@ class EclipseJdtFormatterStepTest extends EquoResourceHarness { - private static final List EMBEDDED_LOCKFILE_VERSIONS = List.of("4.39", EclipseJdtFormatterStep.defaultVersion()); + /** + * Embedded lockfile coverage includes both dependency styles: + *

    + *
  • Range-based Maven POM dependencies: 4.9, 4.11, and 4.25
  • + *
  • Exact Maven POM dependencies: 4.26, 4.39, and the default version
  • + *
+ * The cutoff aligns with + * eclipse-platform/eclipse.platform.releng#135, + * which switched Maven dependency mapping from OSGi ranges to resolved concrete versions. + */ + private static final List EMBEDDED_LOCKFILE_VERSIONS = List.of("4.9", "4.11", "4.25", "4.26", "4.39", EclipseJdtFormatterStep.defaultVersion()); private static EquoBasedStepBuilder createBuilder() { return EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral(), TestP2Provisioner.defaultProvisioner()); diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java index ffeb2a50cc..f370291eca 100644 --- a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java +++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java @@ -15,39 +15,20 @@ */ package com.diffplug.spotless.extra.java; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.StringReader; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpClient.Redirect; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.Set; import java.util.StringJoiner; -import java.util.jar.JarInputStream; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.xml.XMLConstants; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathFactory; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; +import dev.equo.solstice.p2.P2ClientCache; +import dev.equo.solstice.p2.P2Model; +import dev.equo.solstice.p2.P2QueryCache; +import dev.equo.solstice.p2.P2QueryResult; /** * Executable class for validating or updating embedded JDT lockfiles from Eclipse P2 metadata. @@ -60,13 +41,9 @@ */ public class EclipseJdtLockfileMetadataTool { - // Lockfiles are only provided for Eclipse versions whose org.eclipse.jdt.core POM on Maven Central - // declares exact transitive dependency versions rather than version ranges (e.g. "[3.12.0,4.0.0)"). - // org.eclipse.jdt.core:3.32.0 is the first version on Maven Central with exact version dependencies; - // the likely cause is eclipse-platform/eclipse.platform.releng#135 (issue #128), which updated the - // CBI aggregator configuration to map bundle requirements to their resolved versions rather than the - // OSGi version ranges from MANIFEST.MF. - private static final List TARGET_VERSIONS = List.of("4.39", "4.40"); + // Full explicit lockfiles can be produced for any target by taking Solstice's P2-resolved Maven + // coordinates directly (already fully version-resolved), then writing them as a lockfile. + private static final List TARGET_VERSIONS = List.of("4.9", "4.11", "4.25", "4.26", "4.39", "4.40"); /** * Verifies the JDT lockfiles at {@link #TARGET_VERSIONS} against Eclipse P2 metadata. @@ -93,13 +70,10 @@ public static void main(String[] args) { } private static final String JDT_ID = "org.eclipse.jdt.core"; - private static final String JDT_MAVEN_PREFIX = "org.eclipse.jdt:org.eclipse.jdt.core:"; - private static final Pattern MAJOR_MINOR_PATCH = Pattern.compile("^([0-9]+\\.[0-9]+\\.[0-9]+).*$"); - private static final int MAX_TRAVERSAL = 200; - - private static final HttpClient HTTP = HttpClient.newBuilder() - .followRedirects(Redirect.NORMAL) - .build(); + private static final String JDT_MAVEN_KEY = "org.eclipse.jdt:org.eclipse.jdt.core"; + private static final List ECLIPSE_UPDATE_BASE_URLS = List.of( + "https://download.eclipse.org/eclipse/updates/", + "https://archive.eclipse.org/eclipse/updates/"); private static void run(boolean update) { Path lockfileDir = lockfileDir(); @@ -112,10 +86,9 @@ private static void run(boolean update) { String eclipseVersion = entry.getKey(); Path lockfilePath = entry.getValue(); try { - String bundleVersion = resolveBundleVersion(eclipseVersion); - String rootCoordinate = JDT_MAVEN_PREFIX + normalizeToMavenVersion(bundleVersion); + List allCoordinates = resolveTransitiveClosure(eclipseVersion); + String rootCoordinate = allCoordinates.get(0); if (update) { - List allCoordinates = resolveTransitiveClosure(rootCoordinate); Files.writeString(lockfilePath, lockfileContent(eclipseVersion, allCoordinates), StandardCharsets.UTF_8); System.out.println("WROTE v" + eclipseVersion + " -> " + allCoordinates.size() + " artifact(s), root: " + rootCoordinate); } else { @@ -124,13 +97,14 @@ private static void run(boolean update) { failures++; continue; } - String actualRootCoordinate = lockfileRootCoordinate(lockfilePath); - if (!rootCoordinate.equals(actualRootCoordinate)) { - System.err.println("MISMATCH v" + eclipseVersion + " -> expected root: " + rootCoordinate + " actual: " + actualRootCoordinate); + List actualCoordinates = lockfileCoordinates(lockfilePath); + String mismatch = mismatchMessage(eclipseVersion, allCoordinates, actualCoordinates); + if (mismatch != null) { + System.err.println(mismatch); failures++; continue; } - System.out.println("OK v" + eclipseVersion + " -> " + actualRootCoordinate); + System.out.println("OK v" + eclipseVersion + " -> " + rootCoordinate + " (" + allCoordinates.size() + " coordinates)"); } } catch (Exception e) { System.err.println("ERROR v" + eclipseVersion + " -> " + e.getMessage()); @@ -159,16 +133,29 @@ private static String lockfileContent(String eclipseVersion, List coordi return joiner.toString(); } - private static String lockfileRootCoordinate(Path lockfilePath) throws IOException { + private static List lockfileCoordinates(Path lockfilePath) throws IOException { try (var lines = Files.lines(lockfilePath, StandardCharsets.UTF_8)) { - return lines.map(String::trim) + List coordinates = lines.map(String::trim) .filter(line -> !line.isEmpty()) .filter(line -> !line.startsWith("#")) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("No dependency coordinate found in " + lockfilePath)); + .toList(); + if (coordinates.isEmpty()) { + throw new IllegalArgumentException("No dependency coordinate found in " + lockfilePath); + } + return coordinates; } } + static String mismatchMessage(String eclipseVersion, List expectedCoordinates, List actualCoordinates) { + if (expectedCoordinates.equals(actualCoordinates)) { + return null; + } + String expectedRoot = expectedCoordinates.isEmpty() ? "" : expectedCoordinates.get(0); + String actualRoot = actualCoordinates.isEmpty() ? "" : actualCoordinates.get(0); + return "MISMATCH v" + eclipseVersion + " -> expected root: " + expectedRoot + " (" + expectedCoordinates.size() + + " coordinates), actual: " + actualRoot + " (" + actualCoordinates.size() + " coordinates)"; + } + private static Path lockfileDir() { Path fromRepoRoot = Path.of("lib-extra", "src", "main", "resources", "com", "diffplug", "spotless", "extra", "eclipse_jdt_formatter"); if (Files.isDirectory(fromRepoRoot)) { @@ -182,94 +169,61 @@ private static Path lockfileDir() { } /** - * Resolves the full Maven transitive closure of the given root coordinate using - * Maven Central POM traversal, with highest-version-wins conflict resolution. + * Resolves full, explicit Maven coordinates from the P2 query result for the given Eclipse version. *

- * Optional dependencies and test/provided/system-scoped dependencies are excluded. - * The root coordinate appears first in the result; remaining entries are sorted. + * This supports both exact and range-based historical metadata because the P2 solver has already + * chosen concrete versions before exposing Maven coordinates. */ - private static List resolveTransitiveClosure(String rootCoordinate) throws Exception { + private static List resolveTransitiveClosure(String eclipseVersion) throws Exception { + P2QueryResult query = queryJdtFromP2(eclipseVersion); + // "g:a" -> selected version Map selected = new LinkedHashMap<>(); - ArrayDeque queue = new ArrayDeque<>(); - queue.add(rootCoordinate.split(":")); - - while (!queue.isEmpty()) { - String[] parts = queue.removeFirst(); + for (String coordinate : query.getJarsOnMavenCentral()) { + String[] parts = coordinate.split(":"); + if (parts.length != 3) { + throw new IllegalStateException("Expected Maven coordinate g:a:v but got: " + coordinate); + } String groupId = parts[0], artifactId = parts[1], version = parts[2]; String key = groupId + ":" + artifactId; - String existing = selected.get(key); - if (existing != null && compareVersions(existing, version) >= 0) { - continue; - } - selected.put(key, version); - - List deps = fetchNonOptionalDepsFromPom(groupId, artifactId, version); - for (String[] dep : deps) { - String depKey = dep[0] + ":" + dep[1]; - String existingDepVersion = selected.get(depKey); - if (existingDepVersion == null || compareVersions(existingDepVersion, dep[2]) < 0) { - queue.add(dep); - } + if (existing == null || compareVersions(existing, version) < 0) { + selected.put(key, version); } } - - String[] rootParts = rootCoordinate.split(":"); - String rootKey = rootParts[0] + ":" + rootParts[1]; + String rootVersion = selected.remove(JDT_MAVEN_KEY); + if (rootVersion == null) { + throw new IllegalStateException("P2 result for Eclipse " + eclipseVersion + " did not contain " + JDT_MAVEN_KEY); + } List result = new ArrayList<>(); - result.add(rootCoordinate); + result.add(JDT_MAVEN_KEY + ":" + rootVersion); selected.entrySet().stream() - .filter(e -> !e.getKey().equals(rootKey)) .map(e -> e.getKey() + ":" + e.getValue()) .sorted() .forEach(result::add); return result; } - private static List fetchNonOptionalDepsFromPom(String groupId, String artifactId, String version) throws Exception { - String path = groupId.replace('.', '/') + "/" + artifactId + "/" + version + "/" + artifactId + "-" + version + ".pom"; - Optional bytes = download("https://repo1.maven.org/maven2/" + path); - if (bytes.isEmpty()) { - System.err.println("WARNING: POM not found on Maven Central: " + groupId + ":" + artifactId + ":" + version); - return List.of(); + private static P2QueryResult queryJdtFromP2(String eclipseVersion) throws Exception { + Exception lastError = null; + for (String baseUrl : ECLIPSE_UPDATE_BASE_URLS) { + try { + P2Model model = new P2Model(); + addPlatformRepo(model, eclipseVersion, baseUrl); + model.getInstall().add(JDT_ID); + return model.query(P2ClientCache.PREFER_OFFLINE, P2QueryCache.ALLOW); + } catch (Exception e) { + lastError = e; + } } - return parseNonOptionalDepsFromPom(new String(bytes.get(), StandardCharsets.UTF_8)); + throw new IllegalStateException("Failed to query Eclipse " + eclipseVersion + " from known update sites", lastError); } - private static List parseNonOptionalDepsFromPom(String pomXml) throws Exception { - Document doc = parseXml(pomXml); - var xpath = XPathFactory.newInstance().newXPath(); - NodeList deps = (NodeList) xpath.evaluate("/project/dependencies/dependency", doc, XPathConstants.NODESET); - List result = new ArrayList<>(); - for (int i = 0; i < deps.getLength(); i++) { - Element dep = (Element) deps.item(i); - String g = textContent(dep, "groupId"); - String a = textContent(dep, "artifactId"); - String v = textContent(dep, "version"); - String scope = textContent(dep, "scope"); - String optional = textContent(dep, "optional"); - String type = textContent(dep, "type"); - - if ("true".equals(optional)) - continue; - if ("test".equals(scope) || "provided".equals(scope) || "system".equals(scope)) - continue; - if ("pom".equals(type)) - continue; - if (v.isEmpty() || v.startsWith("[") || v.startsWith("(") || v.startsWith("$")) - continue; - if (g.isEmpty() || a.isEmpty()) - continue; - - result.add(new String[]{g, a, v}); + private static void addPlatformRepo(P2Model model, String version, String baseUrl) { + if (!version.startsWith("4.")) { + throw new IllegalArgumentException("Expected 4.x"); } - return result; - } - - private static String textContent(Element parent, String tag) { - var nodes = parent.getElementsByTagName(tag); - return nodes.getLength() > 0 ? nodes.item(0).getTextContent().trim() : ""; + model.addP2Repo(baseUrl + version + "/"); } /** Compares two version strings numerically, segment by segment. Returns positive if v1 > v2. */ @@ -294,110 +248,4 @@ private static int parseVersionSegment(String segment) { } } - private static String resolveBundleVersion(String eclipseVersion) throws Exception { - ArrayDeque queue = new ArrayDeque<>(); - Set visited = new LinkedHashSet<>(); - queue.add("https://download.eclipse.org/eclipse/updates/" + eclipseVersion + "/"); - int traversed = 0; - while (!queue.isEmpty()) { - String repoUrl = queue.removeFirst(); - if (!visited.add(repoUrl)) { - continue; - } - traversed++; - if (traversed > MAX_TRAVERSAL) { - throw new IllegalStateException("Traversal exceeded " + MAX_TRAVERSAL + " repositories while resolving Eclipse " + eclipseVersion); - } - Optional contentXml = readJarEntry(repoUrl, "content.jar", "content.xml"); - if (contentXml.isPresent()) { - String bundleVersion = extractBundleVersion(contentXml.get()); - if (bundleVersion != null) { - return bundleVersion; - } - } - Optional compositeXml = readJarEntry(repoUrl, "compositeContent.jar", "compositeContent.xml"); - if (compositeXml.isPresent()) { - queue.addAll(compositeChildren(repoUrl, compositeXml.get(), visited)); - } - } - throw new IllegalStateException("Unable to resolve " + JDT_ID + " from Eclipse " + eclipseVersion + " update site"); - } - - private static Optional readJarEntry(String repoUrl, String jarName, String entryName) throws Exception { - Optional bytes = download(repoUrl + jarName); - if (bytes.isEmpty()) { - return Optional.empty(); - } - try (JarInputStream jarInputStream = new JarInputStream(new ByteArrayInputStream(bytes.get()))) { - var entry = jarInputStream.getNextJarEntry(); - while (entry != null) { - if (!entry.isDirectory() && entryName.equals(entry.getName())) { - return Optional.of(new String(jarInputStream.readAllBytes(), StandardCharsets.UTF_8)); - } - entry = jarInputStream.getNextJarEntry(); - } - } - throw new IllegalStateException("Entry " + entryName + " not found in " + repoUrl + jarName); - } - - private static Optional download(String url) throws Exception { - HttpRequest request = HttpRequest.newBuilder(URI.create(url)).GET().build(); - HttpResponse response = HTTP.send(request, HttpResponse.BodyHandlers.ofByteArray()); - if (response.statusCode() == 200) { - return Optional.of(response.body()); - } - if (response.statusCode() == 404) { - return Optional.empty(); - } - throw new IOException("Unexpected HTTP status " + response.statusCode() + " from " + url); - } - - private static String extractBundleVersion(String contentXml) throws Exception { - Document document = parseXml(contentXml); - var xpath = XPathFactory.newInstance().newXPath(); - String value = (String) xpath.evaluate("//unit[@id='" + JDT_ID + "']/@version", document, XPathConstants.STRING); - return value.isBlank() ? null : value; - } - - private static List compositeChildren(String parentRepoUrl, String compositeXml, Set visited) throws Exception { - Document document = parseXml(compositeXml); - var xpath = XPathFactory.newInstance().newXPath(); - var nodes = (NodeList) xpath.evaluate("//child/@location", document, XPathConstants.NODESET); - List children = new ArrayList<>(nodes.getLength()); - for (int i = 0; i < nodes.getLength(); i++) { - String location = nodes.item(i).getNodeValue(); - String childUrl = parentRepoUrl + trimTrailingSlash(location) + "/"; - if (!visited.contains(childUrl)) { - children.add(childUrl); - } - } - return children; - } - - private static String trimTrailingSlash(String value) { - return value.endsWith("/") ? value.substring(0, value.length() - 1) : value; - } - - private static Document parseXml(String xml) throws Exception { - var factory = DocumentBuilderFactory.newInstance(); - // Disable DTDs/external entities and enable secure processing to prevent XXE - factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - factory.setFeature("http://xml.org/sax/features/external-general-entities", false); - factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); - factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); - factory.setExpandEntityReferences(false); - factory.setNamespaceAware(false); - var builder = factory.newDocumentBuilder(); - return builder.parse(new InputSource(new StringReader(xml))); - } - - private static String normalizeToMavenVersion(String bundleVersion) { - Matcher matcher = MAJOR_MINOR_PATCH.matcher(bundleVersion); - if (!matcher.matches()) { - throw new IllegalArgumentException("Unexpected org.eclipse.jdt.core bundle version: " + bundleVersion); - } - return matcher.group(1); - } - } diff --git a/lib-extra/src/test/resources/com/diffplug/spotless/extra/two-deps.lockfile b/lib-extra/src/test/resources/com/diffplug/spotless/extra/two-deps.lockfile new file mode 100644 index 0000000000..757276a5bb --- /dev/null +++ b/lib-extra/src/test/resources/com/diffplug/spotless/extra/two-deps.lockfile @@ -0,0 +1,3 @@ +# test lockfile +org.eclipse.jdt:org.eclipse.jdt.core:3.45.0 +org.eclipse.jdt:ecj:3.45.0 From 246e0025e600ea4923a5d8e3e5b9c006a894f08e Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sat, 15 Aug 2026 16:35:21 -0700 Subject: [PATCH 6/7] test: update P2 assertions for the JDT lockfile path Eclipse JDT now resolves from an embedded lockfile, so it never runs a P2 query. Two tests still asserted the P2 path: - FormatterStepFactoryTest's eclipse cache-directory cases asserted the P2 provisioner received the cache directory. Replaced with a test that the lockfile path is taken (Maven coordinates resolved, P2 never queried). grEclipse and eclipseCdt have no lockfile and still cover the P2 cache directory wiring. - predeclareFailsWhenEclipseFormatterNotPredeclared expected the P2 'not predeclared' message; an un-predeclared subproject now fails on the Maven coordinates instead. greclipse still covers the P2 message. Co-Authored-By: Claude Opus 5 (1M context) --- .../SpotlessPredeclareIntegrationTest.java | 8 ++- .../maven/FormatterStepFactoryTest.java | 50 +++++++++++++++---- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java index 796c8896ec..5f54de218d 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java @@ -209,9 +209,13 @@ target file('test.java') setFile("sub/test.java").toResource("java/eclipse/JavaCodeUnformatted.test"); BuildResult result = gradleRunner().withArguments("spotlessApply").buildAndFail(); + // Eclipse JDT resolves from an embedded lockfile rather than a P2 query, so an + // un-predeclared subproject fails on the Maven coordinates instead of on P2. + // greclipse still has no lockfile, so it keeps the P2 message - see + // GroovyDependencies.predeclareFailsWhenGroovyGrEclipseNotPredeclared. assertThat(result.getOutput()) - .contains("P2 dependencies not predeclared") - .contains("Add Eclipse formatter configuration to the `spotlessPredeclare` block in the root project"); + .contains("into the `spotlessPredeclare` block in the root project") + .contains("org.eclipse.jdt:org.eclipse.jdt.core"); } @Test diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/FormatterStepFactoryTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/FormatterStepFactoryTest.java index ff98996431..f2b5a2f31c 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/FormatterStepFactoryTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/FormatterStepFactoryTest.java @@ -24,9 +24,11 @@ import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.aether.RepositorySystemSession; @@ -51,11 +53,6 @@ void defaultP2CacheDirectoryUsesMavenLocalRepository() { .isEqualTo(new File(localRepository, "dev/equo/p2-data")); } - @Test - void eclipseUsesConfiguredCacheDirectory() throws Exception { - assertUsesConfiguredCacheDirectory(new Eclipse()); - } - @Test void grEclipseUsesConfiguredCacheDirectory() throws Exception { assertUsesConfiguredCacheDirectory(new GrEclipse()); @@ -66,11 +63,6 @@ void eclipseCdtUsesConfiguredCacheDirectory() throws Exception { assertUsesConfiguredCacheDirectory(new EclipseCdt()); } - @Test - void eclipseUsesDefaultCacheDirectory() throws Exception { - assertUsesDefaultCacheDirectory(new Eclipse()); - } - @Test void grEclipseUsesDefaultCacheDirectory() throws Exception { assertUsesDefaultCacheDirectory(new GrEclipse()); @@ -81,6 +73,44 @@ void eclipseCdtUsesDefaultCacheDirectory() throws Exception { assertUsesDefaultCacheDirectory(new EclipseCdt()); } + /** + * Eclipse JDT ships an embedded lockfile for its default version, so it resolves straight from + * Maven and never runs a P2 query -- which also means its P2 cache directory goes unused. The + * P2 cache directory behavior is covered by the grEclipse and eclipseCdt cases above, neither + * of which has an embedded lockfile. + */ + @Test + void eclipseResolvesFromEmbeddedLockfileInsteadOfP2() throws Exception { + Eclipse factory = new Eclipse(); + factory.init(repositorySystemSession(tempDir.resolve("local-repo").toFile())); + + AtomicReference> actualCoordinates = new AtomicReference<>(); + AtomicBoolean p2WasQueried = new AtomicBoolean(); + File fakeJar = tempDir.resolve("fake.jar").toFile(); + Files.write(fakeJar.toPath(), new byte[]{0}); + + FormatterStep step = factory.newFormatterStep(new FormatterStepConfig( + UTF_8, + "", + Optional.empty(), + (withTransitives, mavenCoordinates) -> { + actualCoordinates.set(mavenCoordinates); + return Set.of(fakeJar); + }, + (modelWrapper, mavenProvisioner, cacheDirectory) -> { + p2WasQueried.set(true); + return List.of(fakeJar); + }, + null, + Optional.empty(), + Optional.empty())); + + int unused = step.hashCode(); + + assertThat(p2WasQueried).isFalse(); + assertThat(actualCoordinates.get()).anyMatch(coordinate -> coordinate.startsWith("org.eclipse.jdt:org.eclipse.jdt.core:")); + } + private void assertUsesConfiguredCacheDirectory(FormatterStepFactory factory) throws Exception { File configuredCacheDirectory = tempDir.resolve("configured-p2-cache").toFile(); Field cacheDirectory = cacheDirectoryField(factory); From 381d8c92b6cfb823f42f23dc70faad859e6e3d43 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sat, 15 Aug 2026 16:57:43 -0700 Subject: [PATCH 7/7] Ship a JDT lockfile for every supported version The lockfile set was six sampled versions, chosen to span Eclipse's switch from OSGi ranges to concrete versions in its Maven metadata (releng#135, effective 4.26). That made the P2 fallback depend on which release a user happened to pin: 4.30 silently resolved differently from 4.39, with a different predeclare error and cacheDirectory going unused on one path but not the other. Ship 4.9 through the default version contiguously instead. The range/exact distinction does not need to factor in -- Solstice resolves concrete coordinates under either style, so a lockfile can be produced for any target. Regenerating the six existing lockfiles reproduces them byte for byte. - EclipseJdtLockfileMetadataTool derives its targets from the default version rather than a hand-maintained list, and Verify now exits non-zero so it can gate. - Gradle entrypoints :lib-extra:{verify,update}EclipseJdtLockfiles. Verify queries the Eclipse update sites, so it runs in scheduled-cleanup rather than per-PR; gaps and strays are caught offline by a new test. - EquoBasedStepBuilder now derives the lockfile path from the formatter name, the same layout EclipseBasedStepBuilder already uses, so cdt and groovy pick up lockfiles whenever theirs are checked in. Both continue to fall back to P2 today. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/scheduled-cleanup.yml | 6 ++ CHANGES.md | 3 +- lib-extra/build.gradle | 21 +++++++ .../extra/EclipseBasedStepBuilder.java | 6 +- .../spotless/extra/EquoBasedStepBuilder.java | 12 ++-- .../extra/java/EclipseJdtFormatterStep.java | 5 -- .../eclipse_jdt_formatter/v4.10.lockfile | 16 +++++ .../eclipse_jdt_formatter/v4.12.lockfile | 16 +++++ .../eclipse_jdt_formatter/v4.13.lockfile | 16 +++++ .../eclipse_jdt_formatter/v4.14.lockfile | 16 +++++ .../eclipse_jdt_formatter/v4.15.lockfile | 16 +++++ .../eclipse_jdt_formatter/v4.16.lockfile | 16 +++++ .../eclipse_jdt_formatter/v4.17.lockfile | 16 +++++ .../eclipse_jdt_formatter/v4.18.lockfile | 16 +++++ .../eclipse_jdt_formatter/v4.19.lockfile | 16 +++++ .../eclipse_jdt_formatter/v4.20.lockfile | 16 +++++ .../eclipse_jdt_formatter/v4.21.lockfile | 16 +++++ .../eclipse_jdt_formatter/v4.22.lockfile | 16 +++++ .../eclipse_jdt_formatter/v4.23.lockfile | 16 +++++ .../eclipse_jdt_formatter/v4.24.lockfile | 17 +++++ .../eclipse_jdt_formatter/v4.27.lockfile | 18 ++++++ .../eclipse_jdt_formatter/v4.28.lockfile | 18 ++++++ .../eclipse_jdt_formatter/v4.29.lockfile | 18 ++++++ .../eclipse_jdt_formatter/v4.30.lockfile | 18 ++++++ .../eclipse_jdt_formatter/v4.31.lockfile | 18 ++++++ .../eclipse_jdt_formatter/v4.32.lockfile | 18 ++++++ .../eclipse_jdt_formatter/v4.33.lockfile | 19 ++++++ .../eclipse_jdt_formatter/v4.34.lockfile | 19 ++++++ .../eclipse_jdt_formatter/v4.35.lockfile | 19 ++++++ .../eclipse_jdt_formatter/v4.36.lockfile | 19 ++++++ .../eclipse_jdt_formatter/v4.37.lockfile | 19 ++++++ .../eclipse_jdt_formatter/v4.38.lockfile | 19 ++++++ .../java/EclipseJdtFormatterStepTest.java | 41 +++++++++--- .../java/EclipseJdtLockfileMetadataTool.java | 62 +++++++++++++++---- plugin-gradle/CHANGES.md | 3 +- plugin-maven/CHANGES.md | 3 +- 36 files changed, 576 insertions(+), 33 deletions(-) create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.10.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.12.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.13.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.14.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.15.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.16.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.17.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.18.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.19.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.20.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.21.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.22.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.23.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.24.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.27.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.28.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.29.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.30.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.31.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.32.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.33.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.34.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.35.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.36.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.37.lockfile create mode 100644 lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.38.lockfile diff --git a/.github/workflows/scheduled-cleanup.yml b/.github/workflows/scheduled-cleanup.yml index 54b3cb6c55..03c8f57ada 100644 --- a/.github/workflows/scheduled-cleanup.yml +++ b/.github/workflows/scheduled-cleanup.yml @@ -2,6 +2,7 @@ name: Scheduled Cleanup ๐Ÿงน on: schedule: - cron: '0 16 * * 3' + workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -27,3 +28,8 @@ jobs: run: ./gradlew assemble -Derror-prone=true - name: OpenRewrite โ˜‘๏ธ # 4m 55s (2 min compile, 3 min rewrite) run: ./gradlew rewriteDryRun + # Catches upstream drift between the embedded lockfiles and Eclipse's P2 metadata. It lives + # here rather than in CI because it queries the Eclipse update sites -- per-PR gaps and strays + # are already caught offline by EclipseJdtFormatterStepTest. + - name: Eclipse JDT Lockfiles ๐Ÿ”’ # ~1 min + run: ./gradlew :lib-extra:verifyEclipseJdtLockfiles diff --git a/CHANGES.md b/CHANGES.md index ad13642ab5..088d9ec572 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,7 +13,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ### Fixed - Concurrent P2 provisioning (parallel multi-project Gradle fingerprinting of `eclipse()` / `greclipse()` steps) no longer races Solstice's on-disk cache; also `ConfigurationCacheHackList.toString()` no longer evaluates step state (which could re-trigger provisioning while Gradle reports "cannot be serialized"). ([#3004](https://github.com/diffplug/spotless/issues/3004)) ### Changes -- Add embedded lockfiles to Eclipse JDT (4.9, 4.11, 4.39, 4.40), bump version to latest `4.39` -> `4.40`. ([#1996](https://github.com/diffplug/spotless/issues/1996)) +- Add embedded lockfiles to Eclipse JDT for every supported version (`4.9` through `4.40`), so `eclipse()` resolves from Maven Central instead of querying a P2 update site. Versions without an embedded lockfile still fall back to P2 provisioning. ([#1996](https://github.com/diffplug/spotless/issues/1996)) +- Bump default `eclipse` version to latest `4.39` -> `4.40`. ([#1996](https://github.com/diffplug/spotless/issues/1996)) - Bump default `adocfmt` version `0.2.0` -> `0.3.1`, which adds table formatting support (`formatTables`, `tableLayout`, `tableMaxLineWidth`, `tableBlankLines`). ## [4.9.0] - 2026-07-27 diff --git a/lib-extra/build.gradle b/lib-extra/build.gradle index 99e8b233b2..9545924358 100644 --- a/lib-extra/build.gradle +++ b/lib-extra/build.gradle @@ -96,6 +96,27 @@ p2deps { } } +// Embedded Eclipse JDT lockfiles, see EclipseJdtLockfileMetadataTool. `verify` runs in CI so the +// lockfiles cannot silently drift from Eclipse's P2 metadata; `update` is run by hand whenever the +// default version moves. Both hit the Eclipse update sites, so neither is wired into `build`. +def eclipseJdtLockfileTool = { String entrypoint -> + return { + group = 'verification' + classpath = sourceSets.test.runtimeClasspath + mainClass = "com.diffplug.spotless.extra.java.EclipseJdtLockfileMetadataTool\$${entrypoint}" + workingDir = rootProject.projectDir + outputs.upToDateWhen { false } + } +} +tasks.register('verifyEclipseJdtLockfiles', JavaExec) { + configure eclipseJdtLockfileTool('Verify') + description = 'Checks the embedded Eclipse JDT lockfiles against Eclipse P2 metadata.' +} +tasks.register('updateEclipseJdtLockfiles', JavaExec) { + configure eclipseJdtLockfileTool('Update') + description = 'Regenerates the embedded Eclipse JDT lockfiles from Eclipse P2 metadata.' +} + // we'll hold the core lib to a high standard spotbugs { // LOW|MEDIUM|DEFAULT|HIGH (low = sensitive to even minor mistakes). diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/EclipseBasedStepBuilder.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/EclipseBasedStepBuilder.java index 2c3d1a5a6c..6307e5395f 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/EclipseBasedStepBuilder.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/EclipseBasedStepBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2025 DiffPlug + * Copyright 2016-2026 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,9 +55,11 @@ public class EclipseBasedStepBuilder { * the fixed versions for the formatter and its transitive dependencies. * Each line is either a comment starting with {@code #} or corresponds to the format * {@code :[:packaging][:classifier]:} + *

+ * Shared with {@link EquoBasedStepBuilder}, which follows the same lockfile layout. *

*/ - private static final String ECLIPSE_FORMATTER_RESOURCES = EclipseBasedStepBuilder.class.getPackage().getName().replace('.', '/'); + static final String ECLIPSE_FORMATTER_RESOURCES = EclipseBasedStepBuilder.class.getPackage().getName().replace('.', '/'); private List dependencies = new ArrayList<>(); private Iterable settingsFiles = new ArrayList<>(); diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java index 462c9832c6..b6a4a1be08 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java @@ -176,17 +176,19 @@ public FormatterStep build() { /** * Returns the classpath resource path of an embedded lockfile for the given formatter version. *

- * The default implementation always returns {@code null}, which means dependency resolution - * falls back to P2 provisioning. + * Defaults to the same layout {@link EclipseBasedStepBuilder} uses -- {@code v.lockfile} + * inside a directory named after the formatter -- so a step picks up lockfile support simply by + * having its lockfiles checked in. Versions with no embedded lockfile fall back to P2 + * provisioning, which is what keeps Eclipse releases newer than Spotless usable. *

* Overriding implementations should return an absolute classpath resource path (starting with * {@code /}) that is compatible with {@link Class#getResourceAsStream(String)}. * - * @return absolute classpath resource path of the embedded lockfile, or {@code null} to use - * P2 provisioning + * @return absolute classpath resource path of the embedded lockfile, or {@code null} to always + * use P2 provisioning */ protected @Nullable String lockfileResourcePath(String version) { - return null; + return "/" + EclipseBasedStepBuilder.ECLIPSE_FORMATTER_RESOURCES + "/" + formatterName.replace(' ', '_') + "/v" + version + ".lockfile"; } private P2Model createModelWithMirrors() { diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java index 961d889334..83a3ea8d55 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java @@ -76,11 +76,6 @@ protected P2Model model(String version) { return model; } - @Override - protected String lockfileResourcePath(String version) { - return "/com/diffplug/spotless/extra/eclipse_jdt_formatter/v" + version + ".lockfile"; - } - @Override public void setVersion(String version) { if (version.endsWith(".0")) { diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.10.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.10.lockfile new file mode 100644 index 0000000000..2cbfe35e0f --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.10.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.10 +org.eclipse.jdt:org.eclipse.jdt.core:3.16.0 +org.eclipse.platform:org.eclipse.core.commands:3.9.200 +org.eclipse.platform:org.eclipse.core.contenttype:3.7.200 +org.eclipse.platform:org.eclipse.core.expressions:3.6.200 +org.eclipse.platform:org.eclipse.core.filesystem:1.7.200 +org.eclipse.platform:org.eclipse.core.jobs:3.10.200 +org.eclipse.platform:org.eclipse.core.resources:3.13.200 +org.eclipse.platform:org.eclipse.core.runtime:3.15.100 +org.eclipse.platform:org.eclipse.equinox.app:1.4.0 +org.eclipse.platform:org.eclipse.equinox.common:3.10.200 +org.eclipse.platform:org.eclipse.equinox.preferences:3.7.200 +org.eclipse.platform:org.eclipse.equinox.registry:3.8.200 +org.eclipse.platform:org.eclipse.equinox.supplement:1.8.100 +org.eclipse.platform:org.eclipse.osgi:3.13.200 +org.eclipse.platform:org.eclipse.text:3.8.0 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.12.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.12.lockfile new file mode 100644 index 0000000000..fae8e59670 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.12.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.12 +org.eclipse.jdt:org.eclipse.jdt.core:3.18.0 +org.eclipse.platform:org.eclipse.core.commands:3.9.400 +org.eclipse.platform:org.eclipse.core.contenttype:3.7.300 +org.eclipse.platform:org.eclipse.core.expressions:3.6.400 +org.eclipse.platform:org.eclipse.core.filesystem:1.7.400 +org.eclipse.platform:org.eclipse.core.jobs:3.10.400 +org.eclipse.platform:org.eclipse.core.resources:3.13.400 +org.eclipse.platform:org.eclipse.core.runtime:3.15.300 +org.eclipse.platform:org.eclipse.equinox.app:1.4.200 +org.eclipse.platform:org.eclipse.equinox.common:3.10.400 +org.eclipse.platform:org.eclipse.equinox.preferences:3.7.400 +org.eclipse.platform:org.eclipse.equinox.registry:3.8.400 +org.eclipse.platform:org.eclipse.equinox.supplement:1.8.300 +org.eclipse.platform:org.eclipse.osgi:3.14.0 +org.eclipse.platform:org.eclipse.text:3.8.200 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.13.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.13.lockfile new file mode 100644 index 0000000000..1c6f9d9b62 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.13.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.13 +org.eclipse.jdt:org.eclipse.jdt.core:3.19.0 +org.eclipse.platform:org.eclipse.core.commands:3.9.500 +org.eclipse.platform:org.eclipse.core.contenttype:3.7.400 +org.eclipse.platform:org.eclipse.core.expressions:3.6.500 +org.eclipse.platform:org.eclipse.core.filesystem:1.7.500 +org.eclipse.platform:org.eclipse.core.jobs:3.10.500 +org.eclipse.platform:org.eclipse.core.resources:3.13.500 +org.eclipse.platform:org.eclipse.core.runtime:3.16.0 +org.eclipse.platform:org.eclipse.equinox.app:1.4.300 +org.eclipse.platform:org.eclipse.equinox.common:3.10.500 +org.eclipse.platform:org.eclipse.equinox.preferences:3.7.500 +org.eclipse.platform:org.eclipse.equinox.registry:3.8.500 +org.eclipse.platform:org.eclipse.equinox.supplement:1.9.0 +org.eclipse.platform:org.eclipse.osgi:3.15.0 +org.eclipse.platform:org.eclipse.text:3.9.0 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.14.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.14.lockfile new file mode 100644 index 0000000000..7c237413de --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.14.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.14 +org.eclipse.jdt:org.eclipse.jdt.core:3.20.0 +org.eclipse.platform:org.eclipse.core.commands:3.9.600 +org.eclipse.platform:org.eclipse.core.contenttype:3.7.500 +org.eclipse.platform:org.eclipse.core.expressions:3.6.600 +org.eclipse.platform:org.eclipse.core.filesystem:1.7.600 +org.eclipse.platform:org.eclipse.core.jobs:3.10.600 +org.eclipse.platform:org.eclipse.core.resources:3.13.600 +org.eclipse.platform:org.eclipse.core.runtime:3.17.0 +org.eclipse.platform:org.eclipse.equinox.app:1.4.300 +org.eclipse.platform:org.eclipse.equinox.common:3.10.600 +org.eclipse.platform:org.eclipse.equinox.preferences:3.7.600 +org.eclipse.platform:org.eclipse.equinox.registry:3.8.600 +org.eclipse.platform:org.eclipse.equinox.supplement:1.9.100 +org.eclipse.platform:org.eclipse.osgi:3.15.100 +org.eclipse.platform:org.eclipse.text:3.10.0 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.15.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.15.lockfile new file mode 100644 index 0000000000..4ce8634ae9 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.15.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.15 +org.eclipse.jdt:org.eclipse.jdt.core:3.21.0 +org.eclipse.platform:org.eclipse.core.commands:3.9.700 +org.eclipse.platform:org.eclipse.core.contenttype:3.7.600 +org.eclipse.platform:org.eclipse.core.expressions:3.6.700 +org.eclipse.platform:org.eclipse.core.filesystem:1.7.700 +org.eclipse.platform:org.eclipse.core.jobs:3.10.700 +org.eclipse.platform:org.eclipse.core.resources:3.13.700 +org.eclipse.platform:org.eclipse.core.runtime:3.17.100 +org.eclipse.platform:org.eclipse.equinox.app:1.4.400 +org.eclipse.platform:org.eclipse.equinox.common:3.11.0 +org.eclipse.platform:org.eclipse.equinox.preferences:3.7.700 +org.eclipse.platform:org.eclipse.equinox.registry:3.8.700 +org.eclipse.platform:org.eclipse.equinox.supplement:1.9.200 +org.eclipse.platform:org.eclipse.osgi:3.15.200 +org.eclipse.platform:org.eclipse.text:3.10.100 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.16.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.16.lockfile new file mode 100644 index 0000000000..c2673bda4a --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.16.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.16 +org.eclipse.jdt:org.eclipse.jdt.core:3.22.0 +org.eclipse.platform:org.eclipse.core.commands:3.9.700 +org.eclipse.platform:org.eclipse.core.contenttype:3.7.700 +org.eclipse.platform:org.eclipse.core.expressions:3.6.800 +org.eclipse.platform:org.eclipse.core.filesystem:1.7.700 +org.eclipse.platform:org.eclipse.core.jobs:3.10.800 +org.eclipse.platform:org.eclipse.core.resources:3.13.700 +org.eclipse.platform:org.eclipse.core.runtime:3.18.0 +org.eclipse.platform:org.eclipse.equinox.app:1.4.500 +org.eclipse.platform:org.eclipse.equinox.common:3.12.0 +org.eclipse.platform:org.eclipse.equinox.preferences:3.8.0 +org.eclipse.platform:org.eclipse.equinox.registry:3.8.800 +org.eclipse.platform:org.eclipse.equinox.supplement:1.9.300 +org.eclipse.platform:org.eclipse.osgi:3.15.300 +org.eclipse.platform:org.eclipse.text:3.10.200 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.17.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.17.lockfile new file mode 100644 index 0000000000..5de970283c --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.17.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.17 +org.eclipse.jdt:org.eclipse.jdt.core:3.23.0 +org.eclipse.platform:org.eclipse.core.commands:3.9.700 +org.eclipse.platform:org.eclipse.core.contenttype:3.7.800 +org.eclipse.platform:org.eclipse.core.expressions:3.7.0 +org.eclipse.platform:org.eclipse.core.filesystem:1.7.700 +org.eclipse.platform:org.eclipse.core.jobs:3.10.800 +org.eclipse.platform:org.eclipse.core.resources:3.13.800 +org.eclipse.platform:org.eclipse.core.runtime:3.19.0 +org.eclipse.platform:org.eclipse.equinox.app:1.5.0 +org.eclipse.platform:org.eclipse.equinox.common:3.13.0 +org.eclipse.platform:org.eclipse.equinox.preferences:3.8.0 +org.eclipse.platform:org.eclipse.equinox.registry:3.9.0 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.0 +org.eclipse.platform:org.eclipse.osgi:3.16.0 +org.eclipse.platform:org.eclipse.text:3.10.300 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.18.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.18.lockfile new file mode 100644 index 0000000000..af354c5d5e --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.18.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.18 +org.eclipse.jdt:org.eclipse.jdt.core:3.24.0 +org.eclipse.platform:org.eclipse.core.commands:3.9.800 +org.eclipse.platform:org.eclipse.core.contenttype:3.7.800 +org.eclipse.platform:org.eclipse.core.expressions:3.7.0 +org.eclipse.platform:org.eclipse.core.filesystem:1.7.700 +org.eclipse.platform:org.eclipse.core.jobs:3.10.1000 +org.eclipse.platform:org.eclipse.core.resources:3.13.900 +org.eclipse.platform:org.eclipse.core.runtime:3.20.0 +org.eclipse.platform:org.eclipse.equinox.app:1.5.0 +org.eclipse.platform:org.eclipse.equinox.common:3.14.0 +org.eclipse.platform:org.eclipse.equinox.preferences:3.8.100 +org.eclipse.platform:org.eclipse.equinox.registry:3.10.0 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.0 +org.eclipse.platform:org.eclipse.osgi:3.16.100 +org.eclipse.platform:org.eclipse.text:3.10.400 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.19.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.19.lockfile new file mode 100644 index 0000000000..d5bbcfa27f --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.19.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.19 +org.eclipse.jdt:org.eclipse.jdt.core:3.25.0 +org.eclipse.platform:org.eclipse.core.commands:3.9.800 +org.eclipse.platform:org.eclipse.core.contenttype:3.7.900 +org.eclipse.platform:org.eclipse.core.expressions:3.7.100 +org.eclipse.platform:org.eclipse.core.filesystem:1.7.700 +org.eclipse.platform:org.eclipse.core.jobs:3.10.1100 +org.eclipse.platform:org.eclipse.core.resources:3.14.0 +org.eclipse.platform:org.eclipse.core.runtime:3.20.100 +org.eclipse.platform:org.eclipse.equinox.app:1.5.100 +org.eclipse.platform:org.eclipse.equinox.common:3.14.100 +org.eclipse.platform:org.eclipse.equinox.preferences:3.8.200 +org.eclipse.platform:org.eclipse.equinox.registry:3.10.100 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.100 +org.eclipse.platform:org.eclipse.osgi:3.16.200 +org.eclipse.platform:org.eclipse.text:3.11.0 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.20.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.20.lockfile new file mode 100644 index 0000000000..445c9f007f --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.20.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.20 +org.eclipse.jdt:org.eclipse.jdt.core:3.26.0 +org.eclipse.platform:org.eclipse.core.commands:3.10.0 +org.eclipse.platform:org.eclipse.core.contenttype:3.7.1000 +org.eclipse.platform:org.eclipse.core.expressions:3.7.100 +org.eclipse.platform:org.eclipse.core.filesystem:1.9.0 +org.eclipse.platform:org.eclipse.core.jobs:3.11.0 +org.eclipse.platform:org.eclipse.core.resources:3.15.0 +org.eclipse.platform:org.eclipse.core.runtime:3.22.0 +org.eclipse.platform:org.eclipse.equinox.app:1.5.100 +org.eclipse.platform:org.eclipse.equinox.common:3.15.0 +org.eclipse.platform:org.eclipse.equinox.preferences:3.8.200 +org.eclipse.platform:org.eclipse.equinox.registry:3.10.200 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.100 +org.eclipse.platform:org.eclipse.osgi:3.16.300 +org.eclipse.platform:org.eclipse.text:3.12.0 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.21.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.21.lockfile new file mode 100644 index 0000000000..2a889feb18 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.21.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.21 +org.eclipse.jdt:org.eclipse.jdt.core:3.27.0 +org.eclipse.platform:org.eclipse.core.commands:3.10.100 +org.eclipse.platform:org.eclipse.core.contenttype:3.8.0 +org.eclipse.platform:org.eclipse.core.expressions:3.8.0 +org.eclipse.platform:org.eclipse.core.filesystem:1.9.100 +org.eclipse.platform:org.eclipse.core.jobs:3.12.0 +org.eclipse.platform:org.eclipse.core.resources:3.15.100 +org.eclipse.platform:org.eclipse.core.runtime:3.23.0 +org.eclipse.platform:org.eclipse.equinox.app:1.6.0 +org.eclipse.platform:org.eclipse.equinox.common:3.15.0 +org.eclipse.platform:org.eclipse.equinox.preferences:3.9.0 +org.eclipse.platform:org.eclipse.equinox.registry:3.11.0 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.200 +org.eclipse.platform:org.eclipse.osgi:3.17.0 +org.eclipse.platform:org.eclipse.text:3.12.0 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.22.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.22.lockfile new file mode 100644 index 0000000000..70fe49568f --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.22.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.22 +org.eclipse.jdt:org.eclipse.jdt.core:3.28.0 +org.eclipse.platform:org.eclipse.core.commands:3.10.100 +org.eclipse.platform:org.eclipse.core.contenttype:3.8.100 +org.eclipse.platform:org.eclipse.core.expressions:3.8.100 +org.eclipse.platform:org.eclipse.core.filesystem:1.9.200 +org.eclipse.platform:org.eclipse.core.jobs:3.12.0 +org.eclipse.platform:org.eclipse.core.resources:3.16.0 +org.eclipse.platform:org.eclipse.core.runtime:3.24.0 +org.eclipse.platform:org.eclipse.equinox.app:1.6.100 +org.eclipse.platform:org.eclipse.equinox.common:3.15.100 +org.eclipse.platform:org.eclipse.equinox.preferences:3.9.100 +org.eclipse.platform:org.eclipse.equinox.registry:3.11.100 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.300 +org.eclipse.platform:org.eclipse.osgi:3.17.100 +org.eclipse.platform:org.eclipse.text:3.12.0 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.23.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.23.lockfile new file mode 100644 index 0000000000..ea17095f44 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.23.lockfile @@ -0,0 +1,16 @@ +# Spotless formatter based on Eclipse-JDT 4.23 +org.eclipse.jdt:org.eclipse.jdt.core:3.29.0 +org.eclipse.platform:org.eclipse.core.commands:3.10.100 +org.eclipse.platform:org.eclipse.core.contenttype:3.8.100 +org.eclipse.platform:org.eclipse.core.expressions:3.8.100 +org.eclipse.platform:org.eclipse.core.filesystem:1.9.300 +org.eclipse.platform:org.eclipse.core.jobs:3.12.100 +org.eclipse.platform:org.eclipse.core.resources:3.16.100 +org.eclipse.platform:org.eclipse.core.runtime:3.24.100 +org.eclipse.platform:org.eclipse.equinox.app:1.6.100 +org.eclipse.platform:org.eclipse.equinox.common:3.16.0 +org.eclipse.platform:org.eclipse.equinox.preferences:3.9.100 +org.eclipse.platform:org.eclipse.equinox.registry:3.11.100 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.400 +org.eclipse.platform:org.eclipse.osgi:3.17.200 +org.eclipse.platform:org.eclipse.text:3.12.0 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.24.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.24.lockfile new file mode 100644 index 0000000000..c8f527718d --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.24.lockfile @@ -0,0 +1,17 @@ +# Spotless formatter based on Eclipse-JDT 4.24 +org.eclipse.jdt:org.eclipse.jdt.core:3.30.0 +org.eclipse.platform:org.eclipse.core.commands:3.10.200 +org.eclipse.platform:org.eclipse.core.contenttype:3.8.100 +org.eclipse.platform:org.eclipse.core.expressions:3.8.100 +org.eclipse.platform:org.eclipse.core.filesystem:1.9.400 +org.eclipse.platform:org.eclipse.core.jobs:3.13.0 +org.eclipse.platform:org.eclipse.core.resources:3.17.0 +org.eclipse.platform:org.eclipse.core.runtime:3.25.0 +org.eclipse.platform:org.eclipse.equinox.app:1.6.100 +org.eclipse.platform:org.eclipse.equinox.common:3.16.100 +org.eclipse.platform:org.eclipse.equinox.preferences:3.10.1 +org.eclipse.platform:org.eclipse.equinox.registry:3.11.100 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.500 +org.eclipse.platform:org.eclipse.osgi:3.18.0 +org.eclipse.platform:org.eclipse.text:3.12.100 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.27.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.27.lockfile new file mode 100644 index 0000000000..a50ad530ef --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.27.lockfile @@ -0,0 +1,18 @@ +# Spotless formatter based on Eclipse-JDT 4.27 +org.eclipse.jdt:org.eclipse.jdt.core:3.33.0 +org.eclipse.jdt:ecj:3.33.0 +org.eclipse.platform:org.eclipse.core.commands:3.10.400 +org.eclipse.platform:org.eclipse.core.contenttype:3.8.200 +org.eclipse.platform:org.eclipse.core.expressions:3.8.200 +org.eclipse.platform:org.eclipse.core.filesystem:1.9.500 +org.eclipse.platform:org.eclipse.core.jobs:3.13.300 +org.eclipse.platform:org.eclipse.core.resources:3.18.200 +org.eclipse.platform:org.eclipse.core.runtime:3.26.100 +org.eclipse.platform:org.eclipse.equinox.app:1.6.200 +org.eclipse.platform:org.eclipse.equinox.common:3.17.100 +org.eclipse.platform:org.eclipse.equinox.preferences:3.10.200 +org.eclipse.platform:org.eclipse.equinox.registry:3.11.200 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.600 +org.eclipse.platform:org.eclipse.osgi:3.18.300 +org.eclipse.platform:org.eclipse.text:3.12.300 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.28.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.28.lockfile new file mode 100644 index 0000000000..19746c78b2 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.28.lockfile @@ -0,0 +1,18 @@ +# Spotless formatter based on Eclipse-JDT 4.28 +org.eclipse.jdt:org.eclipse.jdt.core:3.34.0 +org.eclipse.jdt:ecj:3.34.0 +org.eclipse.platform:org.eclipse.core.commands:3.11.0 +org.eclipse.platform:org.eclipse.core.contenttype:3.9.0 +org.eclipse.platform:org.eclipse.core.expressions:3.9.0 +org.eclipse.platform:org.eclipse.core.filesystem:1.10.0 +org.eclipse.platform:org.eclipse.core.jobs:3.14.0 +org.eclipse.platform:org.eclipse.core.resources:3.19.0 +org.eclipse.platform:org.eclipse.core.runtime:3.27.0 +org.eclipse.platform:org.eclipse.equinox.app:1.6.200 +org.eclipse.platform:org.eclipse.equinox.common:3.18.0 +org.eclipse.platform:org.eclipse.equinox.preferences:3.10.200 +org.eclipse.platform:org.eclipse.equinox.registry:3.11.200 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.700 +org.eclipse.platform:org.eclipse.osgi:3.18.400 +org.eclipse.platform:org.eclipse.text:3.13.0 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.29.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.29.lockfile new file mode 100644 index 0000000000..66b277c6c9 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.29.lockfile @@ -0,0 +1,18 @@ +# Spotless formatter based on Eclipse-JDT 4.29 +org.eclipse.jdt:org.eclipse.jdt.core:3.35.0 +org.eclipse.jdt:ecj:3.35.0 +org.eclipse.platform:org.eclipse.core.commands:3.11.100 +org.eclipse.platform:org.eclipse.core.contenttype:3.9.100 +org.eclipse.platform:org.eclipse.core.expressions:3.9.100 +org.eclipse.platform:org.eclipse.core.filesystem:1.10.100 +org.eclipse.platform:org.eclipse.core.jobs:3.15.0 +org.eclipse.platform:org.eclipse.core.resources:3.19.100 +org.eclipse.platform:org.eclipse.core.runtime:3.29.0 +org.eclipse.platform:org.eclipse.equinox.app:1.6.300 +org.eclipse.platform:org.eclipse.equinox.common:3.18.100 +org.eclipse.platform:org.eclipse.equinox.preferences:3.10.300 +org.eclipse.platform:org.eclipse.equinox.registry:3.11.300 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.700 +org.eclipse.platform:org.eclipse.osgi:3.18.500 +org.eclipse.platform:org.eclipse.text:3.13.100 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.30.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.30.lockfile new file mode 100644 index 0000000000..21a07f7fb9 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.30.lockfile @@ -0,0 +1,18 @@ +# Spotless formatter based on Eclipse-JDT 4.30 +org.eclipse.jdt:org.eclipse.jdt.core:3.36.0 +org.eclipse.jdt:ecj:3.36.0 +org.eclipse.platform:org.eclipse.core.commands:3.11.200 +org.eclipse.platform:org.eclipse.core.contenttype:3.9.200 +org.eclipse.platform:org.eclipse.core.expressions:3.9.200 +org.eclipse.platform:org.eclipse.core.filesystem:1.10.200 +org.eclipse.platform:org.eclipse.core.jobs:3.15.100 +org.eclipse.platform:org.eclipse.core.resources:3.20.0 +org.eclipse.platform:org.eclipse.core.runtime:3.30.0 +org.eclipse.platform:org.eclipse.equinox.app:1.6.400 +org.eclipse.platform:org.eclipse.equinox.common:3.18.200 +org.eclipse.platform:org.eclipse.equinox.preferences:3.10.400 +org.eclipse.platform:org.eclipse.equinox.registry:3.11.400 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.700 +org.eclipse.platform:org.eclipse.osgi:3.18.600 +org.eclipse.platform:org.eclipse.text:3.13.100 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.31.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.31.lockfile new file mode 100644 index 0000000000..df8e7feb5f --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.31.lockfile @@ -0,0 +1,18 @@ +# Spotless formatter based on Eclipse-JDT 4.31 +org.eclipse.jdt:org.eclipse.jdt.core:3.37.0 +org.eclipse.jdt:ecj:3.37.0 +org.eclipse.platform:org.eclipse.core.commands:3.12.0 +org.eclipse.platform:org.eclipse.core.contenttype:3.9.300 +org.eclipse.platform:org.eclipse.core.expressions:3.9.300 +org.eclipse.platform:org.eclipse.core.filesystem:1.10.300 +org.eclipse.platform:org.eclipse.core.jobs:3.15.200 +org.eclipse.platform:org.eclipse.core.resources:3.20.100 +org.eclipse.platform:org.eclipse.core.runtime:3.31.0 +org.eclipse.platform:org.eclipse.equinox.app:1.7.0 +org.eclipse.platform:org.eclipse.equinox.common:3.19.0 +org.eclipse.platform:org.eclipse.equinox.preferences:3.11.0 +org.eclipse.platform:org.eclipse.equinox.registry:3.12.0 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.800 +org.eclipse.platform:org.eclipse.osgi:3.19.0 +org.eclipse.platform:org.eclipse.text:3.14.0 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.32.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.32.lockfile new file mode 100644 index 0000000000..155347ba8e --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.32.lockfile @@ -0,0 +1,18 @@ +# Spotless formatter based on Eclipse-JDT 4.32 +org.eclipse.jdt:org.eclipse.jdt.core:3.38.0 +org.eclipse.jdt:ecj:3.38.0 +org.eclipse.platform:org.eclipse.core.commands:3.12.100 +org.eclipse.platform:org.eclipse.core.contenttype:3.9.400 +org.eclipse.platform:org.eclipse.core.expressions:3.9.400 +org.eclipse.platform:org.eclipse.core.filesystem:1.10.400 +org.eclipse.platform:org.eclipse.core.jobs:3.15.300 +org.eclipse.platform:org.eclipse.core.resources:3.20.200 +org.eclipse.platform:org.eclipse.core.runtime:3.31.100 +org.eclipse.platform:org.eclipse.equinox.app:1.7.100 +org.eclipse.platform:org.eclipse.equinox.common:3.19.100 +org.eclipse.platform:org.eclipse.equinox.preferences:3.11.100 +org.eclipse.platform:org.eclipse.equinox.registry:3.12.100 +org.eclipse.platform:org.eclipse.equinox.supplement:1.10.900 +org.eclipse.platform:org.eclipse.osgi:3.20.0 +org.eclipse.platform:org.eclipse.text:3.14.100 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.33.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.33.lockfile new file mode 100644 index 0000000000..b62e8b4478 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.33.lockfile @@ -0,0 +1,19 @@ +# Spotless formatter based on Eclipse-JDT 4.33 +org.eclipse.jdt:org.eclipse.jdt.core:3.39.0 +net.java.dev.jna:jna-platform:5.14.0 +org.eclipse.jdt:ecj:3.39.0 +org.eclipse.platform:org.eclipse.core.commands:3.12.200 +org.eclipse.platform:org.eclipse.core.contenttype:3.9.500 +org.eclipse.platform:org.eclipse.core.expressions:3.9.400 +org.eclipse.platform:org.eclipse.core.filesystem:1.11.0 +org.eclipse.platform:org.eclipse.core.jobs:3.15.400 +org.eclipse.platform:org.eclipse.core.resources:3.21.0 +org.eclipse.platform:org.eclipse.core.runtime:3.31.100 +org.eclipse.platform:org.eclipse.equinox.app:1.7.200 +org.eclipse.platform:org.eclipse.equinox.common:3.19.100 +org.eclipse.platform:org.eclipse.equinox.preferences:3.11.100 +org.eclipse.platform:org.eclipse.equinox.registry:3.12.100 +org.eclipse.platform:org.eclipse.equinox.supplement:1.11.0 +org.eclipse.platform:org.eclipse.osgi:3.21.0 +org.eclipse.platform:org.eclipse.text:3.14.100 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.34.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.34.lockfile new file mode 100644 index 0000000000..9e7d990a4a --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.34.lockfile @@ -0,0 +1,19 @@ +# Spotless formatter based on Eclipse-JDT 4.34 +org.eclipse.jdt:org.eclipse.jdt.core:3.40.0 +net.java.dev.jna:jna-platform:5.15.0 +org.eclipse.jdt:ecj:3.40.0 +org.eclipse.platform:org.eclipse.core.commands:3.12.200 +org.eclipse.platform:org.eclipse.core.contenttype:3.9.600 +org.eclipse.platform:org.eclipse.core.expressions:3.9.400 +org.eclipse.platform:org.eclipse.core.filesystem:1.11.100 +org.eclipse.platform:org.eclipse.core.jobs:3.15.400 +org.eclipse.platform:org.eclipse.core.resources:3.22.0 +org.eclipse.platform:org.eclipse.core.runtime:3.32.0 +org.eclipse.platform:org.eclipse.equinox.app:1.7.200 +org.eclipse.platform:org.eclipse.equinox.common:3.19.200 +org.eclipse.platform:org.eclipse.equinox.preferences:3.11.200 +org.eclipse.platform:org.eclipse.equinox.registry:3.12.200 +org.eclipse.platform:org.eclipse.equinox.supplement:1.11.100 +org.eclipse.platform:org.eclipse.osgi:3.22.0 +org.eclipse.platform:org.eclipse.text:3.14.200 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.35.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.35.lockfile new file mode 100644 index 0000000000..16690074b4 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.35.lockfile @@ -0,0 +1,19 @@ +# Spotless formatter based on Eclipse-JDT 4.35 +org.eclipse.jdt:org.eclipse.jdt.core:3.41.0 +net.java.dev.jna:jna-platform:5.16.0 +org.eclipse.jdt:ecj:3.41.0 +org.eclipse.platform:org.eclipse.core.commands:3.12.300 +org.eclipse.platform:org.eclipse.core.contenttype:3.9.600 +org.eclipse.platform:org.eclipse.core.expressions:3.9.400 +org.eclipse.platform:org.eclipse.core.filesystem:1.11.100 +org.eclipse.platform:org.eclipse.core.jobs:3.15.500 +org.eclipse.platform:org.eclipse.core.resources:3.22.100 +org.eclipse.platform:org.eclipse.core.runtime:3.33.0 +org.eclipse.platform:org.eclipse.equinox.app:1.7.300 +org.eclipse.platform:org.eclipse.equinox.common:3.20.0 +org.eclipse.platform:org.eclipse.equinox.preferences:3.11.300 +org.eclipse.platform:org.eclipse.equinox.registry:3.12.300 +org.eclipse.platform:org.eclipse.equinox.supplement:1.12.0 +org.eclipse.platform:org.eclipse.osgi:3.23.0 +org.eclipse.platform:org.eclipse.text:3.14.300 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.36.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.36.lockfile new file mode 100644 index 0000000000..3b55b19835 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.36.lockfile @@ -0,0 +1,19 @@ +# Spotless formatter based on Eclipse-JDT 4.36 +org.eclipse.jdt:org.eclipse.jdt.core:3.42.0 +net.java.dev.jna:jna-platform:5.17.0 +org.eclipse.jdt:ecj:3.42.0 +org.eclipse.platform:org.eclipse.core.commands:3.12.400 +org.eclipse.platform:org.eclipse.core.contenttype:3.9.600 +org.eclipse.platform:org.eclipse.core.expressions:3.9.400 +org.eclipse.platform:org.eclipse.core.filesystem:1.11.200 +org.eclipse.platform:org.eclipse.core.jobs:3.15.600 +org.eclipse.platform:org.eclipse.core.resources:3.22.200 +org.eclipse.platform:org.eclipse.core.runtime:3.33.100 +org.eclipse.platform:org.eclipse.equinox.app:1.7.400 +org.eclipse.platform:org.eclipse.equinox.common:3.20.100 +org.eclipse.platform:org.eclipse.equinox.preferences:3.11.400 +org.eclipse.platform:org.eclipse.equinox.registry:3.12.400 +org.eclipse.platform:org.eclipse.equinox.supplement:1.12.100 +org.eclipse.platform:org.eclipse.osgi:3.23.100 +org.eclipse.platform:org.eclipse.text:3.14.300 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.37.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.37.lockfile new file mode 100644 index 0000000000..b91e896130 --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.37.lockfile @@ -0,0 +1,19 @@ +# Spotless formatter based on Eclipse-JDT 4.37 +org.eclipse.jdt:org.eclipse.jdt.core:3.43.0 +net.java.dev.jna:jna-platform:5.17.0 +org.eclipse.jdt:ecj:3.43.0 +org.eclipse.platform:org.eclipse.core.commands:3.12.400 +org.eclipse.platform:org.eclipse.core.contenttype:3.9.700 +org.eclipse.platform:org.eclipse.core.expressions:3.9.500 +org.eclipse.platform:org.eclipse.core.filesystem:1.11.300 +org.eclipse.platform:org.eclipse.core.jobs:3.15.700 +org.eclipse.platform:org.eclipse.core.resources:3.23.0 +org.eclipse.platform:org.eclipse.core.runtime:3.34.0 +org.eclipse.platform:org.eclipse.equinox.app:1.7.500 +org.eclipse.platform:org.eclipse.equinox.common:3.20.200 +org.eclipse.platform:org.eclipse.equinox.preferences:3.12.0 +org.eclipse.platform:org.eclipse.equinox.registry:3.12.500 +org.eclipse.platform:org.eclipse.equinox.supplement:1.12.100 +org.eclipse.platform:org.eclipse.osgi:3.23.200 +org.eclipse.platform:org.eclipse.text:3.14.400 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.38.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.38.lockfile new file mode 100644 index 0000000000..f02f3494bf --- /dev/null +++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.38.lockfile @@ -0,0 +1,19 @@ +# Spotless formatter based on Eclipse-JDT 4.38 +org.eclipse.jdt:org.eclipse.jdt.core:3.44.0 +net.java.dev.jna:jna-platform:5.18.1 +org.eclipse.jdt:ecj:3.44.0 +org.eclipse.platform:org.eclipse.core.commands:3.12.500 +org.eclipse.platform:org.eclipse.core.contenttype:3.9.800 +org.eclipse.platform:org.eclipse.core.expressions:3.9.500 +org.eclipse.platform:org.eclipse.core.filesystem:1.11.400 +org.eclipse.platform:org.eclipse.core.jobs:3.15.700 +org.eclipse.platform:org.eclipse.core.resources:3.23.100 +org.eclipse.platform:org.eclipse.core.runtime:3.34.100 +org.eclipse.platform:org.eclipse.equinox.app:1.7.500 +org.eclipse.platform:org.eclipse.equinox.common:3.20.300 +org.eclipse.platform:org.eclipse.equinox.preferences:3.12.100 +org.eclipse.platform:org.eclipse.equinox.registry:3.12.600 +org.eclipse.platform:org.eclipse.equinox.supplement:1.12.100 +org.eclipse.platform:org.eclipse.osgi:3.24.0 +org.eclipse.platform:org.eclipse.text:3.14.500 +org.osgi:org.osgi.service.prefs:1.1.2 diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java index e69549da6f..1ad8066f98 100644 --- a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java +++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java @@ -15,10 +15,15 @@ */ package com.diffplug.spotless.extra.java; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoInteractions; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; +import java.util.stream.Stream; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -34,17 +39,39 @@ class EclipseJdtFormatterStepTest extends EquoResourceHarness { + private static final String LOCKFILE_SUFFIX = ".lockfile"; + /** - * Embedded lockfile coverage includes both dependency styles: + * Every supported version ships a lockfile, but actually formatting with one downloads that + * release's jars, so the round-trip tests below run against a representative sample rather than + * the whole range. The sample spans both of Eclipse's Maven dependency styles: *

    *
  • Range-based Maven POM dependencies: 4.9, 4.11, and 4.25
  • *
  • Exact Maven POM dependencies: 4.26, 4.39, and the default version
  • *
- * The cutoff aligns with + * The styles changed in * eclipse-platform/eclipse.platform.releng#135, - * which switched Maven dependency mapping from OSGi ranges to resolved concrete versions. + * effective 4.26, which switched Maven dependency mapping from OSGi ranges to resolved concrete + * versions. It is a sampling boundary only -- it does not affect which versions ship a lockfile. + */ + private static final List SAMPLED_LOCKFILE_VERSIONS = List.of("4.9", "4.11", "4.25", "4.26", "4.39", EclipseJdtFormatterStep.defaultVersion()); + + /** + * The shipped lockfiles are exactly the supported range -- no gaps, no strays. Contiguity is + * what makes the P2 fallback predictable: which resolution path you get should not depend on + * which release you happened to pin. See {@link EclipseJdtLockfileMetadataTool}, and run + * {@code ./gradlew :lib-extra:updateEclipseJdtLockfiles} when the default version moves. */ - private static final List EMBEDDED_LOCKFILE_VERSIONS = List.of("4.9", "4.11", "4.25", "4.26", "4.39", EclipseJdtFormatterStep.defaultVersion()); + @Test + void shippedLockfilesAreExactlyTheSupportedRange() throws IOException { + try (Stream files = Files.list(EclipseJdtLockfileMetadataTool.lockfileDir())) { + List shipped = files.map(path -> path.getFileName().toString()) + .filter(name -> name.endsWith(LOCKFILE_SUFFIX)) + .map(name -> name.substring(1, name.length() - LOCKFILE_SUFFIX.length())) + .toList(); + assertThat(shipped).containsExactlyInAnyOrderElementsOf(EclipseJdtLockfileMetadataTool.targetVersions()); + } + } private static EquoBasedStepBuilder createBuilder() { return EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral(), TestP2Provisioner.defaultProvisioner()); @@ -55,7 +82,7 @@ public EclipseJdtFormatterStepTest() { } @ParameterizedTest - @FieldSource("EMBEDDED_LOCKFILE_VERSIONS") + @FieldSource("SAMPLED_LOCKFILE_VERSIONS") void formatWithVersion(String version) throws Exception { harnessFor(version).test("test.java", "package p; class C{}", @@ -63,8 +90,8 @@ void formatWithVersion(String version) throws Exception { } @ParameterizedTest - @FieldSource("EMBEDDED_LOCKFILE_VERSIONS") - void embeddedLockfileVersionsDoNotUseP2(String version) { + @FieldSource("SAMPLED_LOCKFILE_VERSIONS") + void sampledLockfileVersionsDoNotUseP2(String version) { P2Provisioner p2Provisioner = mock(); EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral(), p2Provisioner); builder.setVersion(version); diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java index f370291eca..bc4b4334bc 100644 --- a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java +++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java @@ -41,30 +41,68 @@ */ public class EclipseJdtLockfileMetadataTool { - // Full explicit lockfiles can be produced for any target by taking Solstice's P2-resolved Maven - // coordinates directly (already fully version-resolved), then writing them as a lockfile. - private static final List TARGET_VERSIONS = List.of("4.9", "4.11", "4.25", "4.26", "4.39", "4.40"); + /** + * Every Eclipse release from {@value #OLDEST_MINOR}, inclusive, through the version Spotless + * defaults to gets an embedded lockfile, with no gaps. + *

+ * Contiguity is the point: a user on any supported version should get the same resolution path + * as a user on any other. Sampling versions instead would make the P2 fallback -- and the + * behavior differences that come with it -- depend on which release you happen to pin. + *

+ * Eclipse's switch from OSGi ranges to resolved concrete versions in its Maven metadata + * (eclipse-platform/eclipse.platform.releng#135, + * effective 4.26) deliberately does not factor in. Full explicit lockfiles can be + * produced for any target by taking Solstice's P2-resolved Maven coordinates directly, which + * are already fully version-resolved under either style. + */ + private static final int OLDEST_MINOR = 9; + + static List targetVersions() { + String defaultVersion = EclipseJdtFormatterStep.defaultVersion(); + int newestMinor = minorOf(defaultVersion); + if (newestMinor < OLDEST_MINOR) { + throw new IllegalStateException("Default version " + defaultVersion + " is older than 4." + OLDEST_MINOR); + } + List versions = new ArrayList<>(); + for (int minor = OLDEST_MINOR; minor <= newestMinor; minor++) { + versions.add("4." + minor); + } + return versions; + } + + private static int minorOf(String version) { + if (!version.startsWith("4.")) { + throw new IllegalArgumentException("Expected 4.x but got " + version); + } + return Integer.parseInt(version.substring(2).split("\\.")[0]); + } /** - * Verifies the JDT lockfiles at {@link #TARGET_VERSIONS} against Eclipse P2 metadata. + * Verifies the JDT lockfiles at {@link #targetVersions()} against Eclipse P2 metadata. + *

+ * Exits non-zero if any lockfile is missing or out of date, so this can gate CI. */ public static class Verify { public static void main(String[] args) { - run(false); + if (run(false) > 0) { + System.exit(1); + } } } /** - * Updates the JDT lockfiles at {@link #TARGET_VERSIONS} from Eclipse P2 metadata. + * Updates the JDT lockfiles at {@link #targetVersions()} from Eclipse P2 metadata. *

* Missing lockfiles will be created. */ public static class Update { public static void main(String[] args) { - run(true); + if (run(true) > 0) { + System.exit(1); + } } } @@ -75,9 +113,10 @@ public static void main(String[] args) { "https://download.eclipse.org/eclipse/updates/", "https://archive.eclipse.org/eclipse/updates/"); - private static void run(boolean update) { + /** Returns the number of failures. */ + private static int run(boolean update) { Path lockfileDir = lockfileDir(); - Map lockfilesByVersion = targetLockfiles(lockfileDir, TARGET_VERSIONS); + Map lockfilesByVersion = targetLockfiles(lockfileDir, targetVersions()); int checked = 0; int failures = 0; @@ -112,10 +151,11 @@ private static void run(boolean update) { } } if (update) { - System.out.println("Updated " + checked + " lockfile(s)."); + System.out.println("Updated " + checked + " lockfile(s) with " + failures + " issue(s)."); } else { System.out.println("Verified " + checked + " lockfile(s) with " + failures + " issue(s)."); } + return failures; } private static Map targetLockfiles(Path lockfileDir, List versions) { @@ -156,7 +196,7 @@ static String mismatchMessage(String eclipseVersion, List expectedCoordi + " coordinates), actual: " + actualRoot + " (" + actualCoordinates.size() + " coordinates)"; } - private static Path lockfileDir() { + static Path lockfileDir() { Path fromRepoRoot = Path.of("lib-extra", "src", "main", "resources", "com", "diffplug", "spotless", "extra", "eclipse_jdt_formatter"); if (Files.isDirectory(fromRepoRoot)) { return fromRepoRoot; diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index d91b01f140..e500f61fa3 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -7,7 +7,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( - `spotlessCheck` violation message now suggests the correct composite/included-build task path (e.g. `./gradlew :my-utils:spotlessApply`) instead of a bare `spotlessApply` / `:spotlessApply` that does not select included-build tasks. ([#2421](https://github.com/diffplug/spotless/issues/2421)) - Parallel multi-project builds no longer intermittently fail with "Cannot fingerprint input property 'stepsInternalEquality': ConfigurationCacheHackList cannot be serialized" / "Failed to provision P2 dependencies" when using `eclipse()` (or other P2-backed steps). Subprojects now share one deduping P2 provisioner and P2 queries are serialized process-wide. ([#3004](https://github.com/diffplug/spotless/issues/3004)) ### Changes -- Add embedded lockfiles to Eclipse JDT (4.9, 4.11, 4.39, 4.40), bump version to latest `4.39` -> `4.40`. ([#1996](https://github.com/diffplug/spotless/issues/1996)) +- Add embedded lockfiles to Eclipse JDT for every supported version (`4.9` through `4.40`), so `eclipse()` resolves from Maven Central instead of querying a P2 update site. Versions without an embedded lockfile still fall back to P2 provisioning. ([#1996](https://github.com/diffplug/spotless/issues/1996)) +- Bump default `eclipse` version to latest `4.39` -> `4.40`. ([#1996](https://github.com/diffplug/spotless/issues/1996)) - Bump default `adocfmt` version `0.2.0` -> `0.3.1`, which adds table formatting support (`formatTables`, `tableLayout`, `tableMaxLineWidth`, `tableBlankLines`). ## [8.9.0] - 2026-07-27 diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index fc4e79d9ae..e5a29e7867 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -6,7 +6,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ### Fixed - Concurrent P2 provisioning no longer races Solstice's on-disk cache (affects Eclipse-based formatters under parallel builds). ([#3004](https://github.com/diffplug/spotless/issues/3004)) ### Changes -- Add embedded lockfiles to Eclipse JDT (4.9, 4.11, 4.39, 4.40), bump version to latest `4.39` -> `4.40`. ([#1996](https://github.com/diffplug/spotless/issues/1996)) +- Add embedded lockfiles to Eclipse JDT for every supported version (`4.9` through `4.40`), so `eclipse()` resolves from Maven Central instead of querying a P2 update site. Versions without an embedded lockfile still fall back to P2 provisioning. ([#1996](https://github.com/diffplug/spotless/issues/1996)) +- Bump default `eclipse` version to latest `4.39` -> `4.40`. ([#1996](https://github.com/diffplug/spotless/issues/1996)) - Document Maven skip properties `spotless.skip`, `spotless.check.skip`, and `spotless.apply.skip`. Goal-specific skips now live on their own mojos so they no longer leak across goals. ([#3009](https://github.com/diffplug/spotless/pull/3009)) - Bump default `adocfmt` version `0.2.0` -> `0.3.1`, which adds table formatting support (``, ``, ``, ``). - Add support to apply alternate license header within same format ([#872](https://github.com/diffplug/spotless/issues/872))