From 398896935c3e05c2d97cfb4c6a926410e0a38213 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 27 Mar 2026 20:22:29 +1300 Subject: [PATCH 01/13] feat: merge dependencies block in gradle --- .../rewrite/AddConfigurationRecipe.java | 14 ++ .../rewrite/DependencyMergeUtil.java | 142 ++++++++++++++++++ .../rewrite/AddConfigurationRecipeTests.java | 55 +++++++ 3 files changed, 211 insertions(+) create mode 100644 src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java diff --git a/src/main/java/com/canonical/devpackspring/rewrite/AddConfigurationRecipe.java b/src/main/java/com/canonical/devpackspring/rewrite/AddConfigurationRecipe.java index a3f82268..28882428 100644 --- a/src/main/java/com/canonical/devpackspring/rewrite/AddConfigurationRecipe.java +++ b/src/main/java/com/canonical/devpackspring/rewrite/AddConfigurationRecipe.java @@ -127,6 +127,16 @@ private boolean addOrReplace(List targetStatements, Statement configS for (int i = 0; i < targetStatements.size(); i++) { Statement targetStmt = targetStatements.get(i); if (matches(targetStmt, configStmt)) { + // Special case: merge dependencies{} blocks instead of replacing + if (isDependenciesBlock(targetStmt)) { + Statement merged = DependencyMergeUtil.mergeDependenciesBlock(targetStmt, configStmt, targetCu, + configCu); + if (merged != null) { + targetStatements.set(i, merged); + return true; + } + return false; + } // avoid modifying if the trimmed outputs are strictly identical org.openrewrite.Cursor targetCursor = new org.openrewrite.Cursor( new org.openrewrite.Cursor(null, targetCu), targetStmt); @@ -142,6 +152,10 @@ private boolean addOrReplace(List targetStatements, Statement configS return true; } + private boolean isDependenciesBlock(Statement stmt) { + return stmt instanceof J.MethodInvocation m && "dependencies".equals(m.getSimpleName()); + } + private boolean matches(Statement targetStmt, Statement configStmt) { // handle project.ext.set -> we want to override only properties with the same // name diff --git a/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java b/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java new file mode 100644 index 00000000..b90d2434 --- /dev/null +++ b/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java @@ -0,0 +1,142 @@ +/* + * Copyright 2025 the original author or authors. + * + * 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 + * + * https://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.canonical.devpackspring.rewrite; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import org.openrewrite.Cursor; +import org.openrewrite.SourceFile; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.Statement; + +public abstract class DependencyMergeUtil { + + public static Statement mergeDependenciesBlock(Statement targetStmt, Statement configStmt, SourceFile targetCu, + SourceFile configCu) { + J.MethodInvocation targetMethod = (J.MethodInvocation) targetStmt; + J.MethodInvocation configMethod = (J.MethodInvocation) configStmt; + + List targetDeps = getDependenciesStatements(targetMethod); + List configDeps = getDependenciesStatements(configMethod); + if (targetDeps == null) { + return configStmt; + } + if (configDeps == null) { + return null; // nothing to merge, skip it + } + + HashSet deps = new HashSet(); + HashMap target = new HashMap(); + HashMap source = new HashMap(); + targetDeps.forEach(x -> updateDependencies(x, deps, target)); + configDeps.forEach(x -> updateDependencies(x, deps, source)); + + List mergedDeps = new ArrayList(); + var sortedList = new ArrayList(deps); + Collections.sort(sortedList); + for (var key : sortedList) { + Statement stm = source.get(key); + if (stm != null) { + if (!mergedDeps.isEmpty()) { + stm = stm.withPrefix(mergedDeps.getLast().getPrefix()); + } + mergedDeps.add(stm); + continue; + } + stm = target.get(key); + if (stm != null) { + if (!mergedDeps.isEmpty()) { + stm = stm.withPrefix(mergedDeps.getLast().getPrefix()); + } + mergedDeps.add(stm); + } + } + Statement rebuilt = rebuildDependenciesStatements(targetMethod, mergedDeps); + if (rebuilt == null) { + return null; + } + Cursor rc = new Cursor(new Cursor(null, targetCu), rebuilt); + String newResult = rebuilt.printTrimmed(rc).trim(); + Cursor cc = new Cursor(new Cursor(null, configCu), targetStmt); + String oldResult = targetStmt.printTrimmed(cc).trim(); + if (oldResult.equals(newResult)) { + return null; + } + return rebuilt; + } + + private static void updateDependencies(Statement stm, HashSet deps, HashMap depMap) { + var key = getDependencyKey(stm); + if (key == null) { + throw new RuntimeException("Unexpected element in depends block " + stm.toString()); + } + deps.add(key); + depMap.put(key, stm); + } + + private static List getDependenciesStatements(J.MethodInvocation m) { + if (m.getArguments().size() == 1) { + org.openrewrite.java.tree.Expression arg = m.getArguments().get(0); + if (arg instanceof J.Lambda lambda && lambda.getBody() instanceof J.Block block) { + return block.getStatements().stream().map(stmt -> { + if (stmt instanceof J.Return ret) { + return (Statement) ret.getExpression(); + } + return stmt; + }).toList(); + } + } + return null; + } + + private static Statement rebuildDependenciesStatements(J.MethodInvocation m, List newStatements) { + if (m.getArguments().size() == 1) { + org.openrewrite.java.tree.Expression arg = m.getArguments().getFirst(); + if (arg instanceof J.Lambda lambda && lambda.getBody() instanceof J.Block block) { + J.Block newBlock = block.withStatements(newStatements); + J.Lambda newLambda = lambda.withBody(newBlock); + return m.withArguments(List.of(newLambda)); + } + } + return null; + } + + private static String getDependencyKey(Statement stmt) { + if (!(stmt instanceof J.MethodInvocation m)) { + return null; + } + String scope = m.getSimpleName(); + if (m.getArguments().isEmpty()) { + return null; + } + String coord = m.getArguments().getFirst().toString(); + // Strip the version segment: "group:artifact:version" -> "group:artifact" + String[] parts = coord.split(":"); + if (parts.length >= 2) { + return scope + ":" + parts[0] + ":" + parts[1]; + } + if (parts.length == 1) { + return scope + ":" + coord; + } + throw new RuntimeException("Unexpected statement " + stmt); + } + +} diff --git a/src/test/java/com/canonical/devpackspring/rewrite/AddConfigurationRecipeTests.java b/src/test/java/com/canonical/devpackspring/rewrite/AddConfigurationRecipeTests.java index 38620b0c..3f13b30e 100644 --- a/src/test/java/com/canonical/devpackspring/rewrite/AddConfigurationRecipeTests.java +++ b/src/test/java/com/canonical/devpackspring/rewrite/AddConfigurationRecipeTests.java @@ -330,4 +330,59 @@ void testKotlinConfigurationAppendProperty() { """)); } + @Test + void testGroovyDependenciesMerge() { + String config = """ + dependencies { + implementation 'org.springframework.boot:spring-boot-starter:3.5.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' + } + """; + G.CompilationUnit cu = parseGroovyConfig(config); + rewriteRun(spec -> spec.recipe(new AddConfigurationRecipe(cu, false)), Assertions.buildGradle(""" + dependencies { + implementation 'org.springframework.boot:spring-boot-starter:3.3.0' + runtimeOnly 'org.postgresql:postgresql:42.7.0' + } + """, """ + dependencies { + implementation 'org.springframework.boot:spring-boot-starter:3.5.0' + runtimeOnly 'org.postgresql:postgresql:42.7.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' + } + """)); + rewriteRun(spec -> spec.recipe(new AddConfigurationRecipe(cu, false)), Assertions.buildGradle(""" + dependencies { + } + """, """ + dependencies { + implementation 'org.springframework.boot:spring-boot-starter:3.5.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' + } + """)); + } + + @Test + void testKotlinDependenciesMerge() { + String config = """ + dependencies { + implementation("org.springframework.boot:spring-boot-starter:3.5.0") + testImplementation("org.junit.jupiter:junit-jupiter:5.11.0") + } + """; + K.CompilationUnit cu = parseKotlinConfig(config); + rewriteRun(spec -> spec.recipe(new AddConfigurationRecipe(cu, true)), Assertions.buildGradleKts(""" + dependencies { + implementation("org.springframework.boot:spring-boot-starter:3.3.0") + runtimeOnly("org.postgresql:postgresql:42.7.0") + } + """, """ + dependencies { + implementation("org.springframework.boot:spring-boot-starter:3.5.0") + runtimeOnly("org.postgresql:postgresql:42.7.0") + testImplementation("org.junit.jupiter:junit-jupiter:5.11.0") + } + """)); + } + } From 2cada5451f3a0a70a4b91ede2bec91dd5015aa77 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Sat, 28 Mar 2026 17:32:34 +1300 Subject: [PATCH 02/13] fix: handle empty dependencies blocks --- .../rewrite/DependencyMergeUtil.java | 8 ++++- .../rewrite/AddConfigurationRecipeTests.java | 33 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java b/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java index b90d2434..0a44c489 100644 --- a/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java +++ b/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java @@ -111,7 +111,13 @@ private static Statement rebuildDependenciesStatements(J.MethodInvocation m, Lis if (m.getArguments().size() == 1) { org.openrewrite.java.tree.Expression arg = m.getArguments().getFirst(); if (arg instanceof J.Lambda lambda && lambda.getBody() instanceof J.Block block) { - J.Block newBlock = block.withStatements(newStatements); + J.Block newBlock = block.withStatements(newStatements.stream().map(stmt -> { + String ws = stmt.getPrefix().getWhitespace(); + if (!ws.contains("\n")) { + stmt = stmt.withPrefix(stmt.getPrefix().withWhitespace("\n" + ws)); + } + return stmt; + }).toList()); J.Lambda newLambda = lambda.withBody(newBlock); return m.withArguments(List.of(newLambda)); } diff --git a/src/test/java/com/canonical/devpackspring/rewrite/AddConfigurationRecipeTests.java b/src/test/java/com/canonical/devpackspring/rewrite/AddConfigurationRecipeTests.java index 3f13b30e..ff22c6b5 100644 --- a/src/test/java/com/canonical/devpackspring/rewrite/AddConfigurationRecipeTests.java +++ b/src/test/java/com/canonical/devpackspring/rewrite/AddConfigurationRecipeTests.java @@ -362,6 +362,39 @@ void testGroovyDependenciesMerge() { """)); } + @Test + void testGroovyDependenciesMergeNewLines() { + String config = """ + dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' + } + """; + G.CompilationUnit cu = parseGroovyConfig(config); + rewriteRun(spec -> spec.recipe(new AddConfigurationRecipe(cu, false)), Assertions.buildGradle(""" + dependencies { implementation 'org.springframework.boot:spring-boot-starter:3.3.0' } + """, """ + dependencies { + implementation 'org.springframework.boot:spring-boot-starter:3.3.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' } + """)); + } + + @Test + void testKotlinDependenciesMergeNewLines() { + String config = """ + dependencies { + testImplementation("org.junit.jupiter:junit-jupiter:5.11.0") + } + """; + K.CompilationUnit cu = parseKotlinConfig(config); + rewriteRun(spec -> spec.recipe(new AddConfigurationRecipe(cu, true)), Assertions.buildGradleKts(""" + dependencies { implementation("org.springframework.boot:spring-boot-starter:3.3.0") } + """, """ + dependencies { + implementation("org.springframework.boot:spring-boot-starter:3.3.0") + testImplementation("org.junit.jupiter:junit-jupiter:5.11.0") }""")); + } + @Test void testKotlinDependenciesMerge() { String config = """ From 4493e8ab720846d6eae2f158e3c9306ba935993a Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Sat, 28 Mar 2026 17:34:11 +1300 Subject: [PATCH 03/13] chore: Java 25 style fixes --- .../devpackspring/rewrite/DependencyMergeUtil.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java b/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java index 0a44c489..b843e22d 100644 --- a/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java +++ b/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java @@ -43,14 +43,14 @@ public static Statement mergeDependenciesBlock(Statement targetStmt, Statement c return null; // nothing to merge, skip it } - HashSet deps = new HashSet(); - HashMap target = new HashMap(); - HashMap source = new HashMap(); + HashSet deps = new HashSet<>(); + HashMap target = new HashMap<>(); + HashMap source = new HashMap<>(); targetDeps.forEach(x -> updateDependencies(x, deps, target)); configDeps.forEach(x -> updateDependencies(x, deps, source)); - List mergedDeps = new ArrayList(); - var sortedList = new ArrayList(deps); + List mergedDeps = new ArrayList<>(); + var sortedList = new ArrayList<>(deps); Collections.sort(sortedList); for (var key : sortedList) { Statement stm = source.get(key); @@ -94,7 +94,7 @@ private static void updateDependencies(Statement stm, HashSet deps, Hash private static List getDependenciesStatements(J.MethodInvocation m) { if (m.getArguments().size() == 1) { - org.openrewrite.java.tree.Expression arg = m.getArguments().get(0); + org.openrewrite.java.tree.Expression arg = m.getArguments().getFirst(); if (arg instanceof J.Lambda lambda && lambda.getBody() instanceof J.Block block) { return block.getStatements().stream().map(stmt -> { if (stmt instanceof J.Return ret) { From e57f07153333d5afb4fea71645bb898dfd1ae188 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Sat, 28 Mar 2026 17:35:09 +1300 Subject: [PATCH 04/13] chore: log template parsing errors --- .../rewrite/visitors/KotlinAddPluginVisitor.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/canonical/devpackspring/rewrite/visitors/KotlinAddPluginVisitor.java b/src/main/java/com/canonical/devpackspring/rewrite/visitors/KotlinAddPluginVisitor.java index a42582ad..b74ff709 100644 --- a/src/main/java/com/canonical/devpackspring/rewrite/visitors/KotlinAddPluginVisitor.java +++ b/src/main/java/com/canonical/devpackspring/rewrite/visitors/KotlinAddPluginVisitor.java @@ -20,6 +20,8 @@ import java.util.List; import com.canonical.devpackspring.rewrite.StatementUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.jspecify.annotations.NonNull; import org.openrewrite.ExecutionContext; import org.openrewrite.InMemoryExecutionContext; @@ -31,9 +33,12 @@ import org.openrewrite.kotlin.KotlinIsoVisitor; import org.openrewrite.kotlin.KotlinParser; import org.openrewrite.kotlin.tree.K; +import org.openrewrite.tree.ParseError; public class KotlinAddPluginVisitor extends KotlinIsoVisitor { + private static final Log LOG = LogFactory.getLog(KotlinAddPluginVisitor.class); + private final String pluginTemplateKotlin = "plugins {\n\tid(\"%s\") version \"%s\"\n}\n"; private final String builtInTemplateKotlin = "plugins {\n\tid(\"%s\")\n}\n"; @@ -56,7 +61,10 @@ public KotlinAddPluginVisitor(String pluginName, String pluginVersion) { Paths.get("/tmp"), context) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Could not parse as Gradle Kotlin")); - + if (templateSource instanceof ParseError error) { + LOG.error("Unable to parse: " + pluginDefinition); + throw new RuntimeException("Parser Error:" + error.printAll()); + } List statements = ((K.CompilationUnit) templateSource).getStatements(); J.Block block = (J.Block) statements.get(0); K.MethodInvocation stm = (K.MethodInvocation) block.getStatements().get(0); From 49ddf1b16d2a9e434b8603f345b6954c5dda73a3 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Sat, 28 Mar 2026 17:35:26 +1300 Subject: [PATCH 05/13] test: add default plugin tests --- .../rewrite/AddGradlePluginRecipeTests.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/test/java/com/canonical/devpackspring/rewrite/AddGradlePluginRecipeTests.java b/src/test/java/com/canonical/devpackspring/rewrite/AddGradlePluginRecipeTests.java index cc04e7fd..37a09980 100644 --- a/src/test/java/com/canonical/devpackspring/rewrite/AddGradlePluginRecipeTests.java +++ b/src/test/java/com/canonical/devpackspring/rewrite/AddGradlePluginRecipeTests.java @@ -158,4 +158,23 @@ void testKotlinAppendPlugin() { """)); } + @Test + void testKotlinAppendDefaultPlugin() { + rewriteRun(spec -> spec.recipe(new AddGradlePluginRecipe("checkstyle", null, true)), + Assertions.buildGradleKts(""" + plugins { + kotlin("jvm") version "1.9.22" + } + group = "com.example" + version = "1.0" + """, """ + plugins { + kotlin("jvm") version "1.9.22" + id("checkstyle") + } + group = "com.example" + version = "1.0" + """)); + } + } From c67e6f62c9849cd097894f465c4b12015f9cf82a Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Sat, 28 Mar 2026 17:35:44 +1300 Subject: [PATCH 06/13] test: bump requested toolchain version in test --- test-data/projects/gradle-kotlin/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/projects/gradle-kotlin/build.gradle.kts b/test-data/projects/gradle-kotlin/build.gradle.kts index c25b77fa..d4c10df0 100644 --- a/test-data/projects/gradle-kotlin/build.gradle.kts +++ b/test-data/projects/gradle-kotlin/build.gradle.kts @@ -9,7 +9,7 @@ version = "0.0.1-SNAPSHOT" java { toolchain { - languageVersion = JavaLanguageVersion.of(17) + languageVersion = JavaLanguageVersion.of(21) } } From ede3e5d8a66af98c56e9ca71e2ce08ff7a880be7 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Sat, 28 Mar 2026 17:36:17 +1300 Subject: [PATCH 07/13] fix: unpack kotlin compiler kotlin compiler resources can not be found in the default bootJar. --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index 17235781..38530a4c 100644 --- a/build.gradle +++ b/build.gradle @@ -221,3 +221,7 @@ if (useAot) { } } + +bootJar { + requiresUnpack '**/kotlin-compiler-embeddable-*.jar' +} From 166878ee282ab0ffd1eaed5ff46655522e347f70 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Sat, 28 Mar 2026 17:36:46 +1300 Subject: [PATCH 08/13] chore: add devpack-for-spring configuration --- .devpack-for-spring/.gitignore | 1 + .devpack-for-spring/plugin-configuration.yaml | 617 ++++++++++++++++++ 2 files changed, 618 insertions(+) create mode 100644 .devpack-for-spring/.gitignore create mode 100644 .devpack-for-spring/plugin-configuration.yaml diff --git a/.devpack-for-spring/.gitignore b/.devpack-for-spring/.gitignore new file mode 100644 index 00000000..9515f838 --- /dev/null +++ b/.devpack-for-spring/.gitignore @@ -0,0 +1 @@ +devpack-for-spring-cli \ No newline at end of file diff --git a/.devpack-for-spring/plugin-configuration.yaml b/.devpack-for-spring/plugin-configuration.yaml new file mode 100644 index 00000000..4c62feee --- /dev/null +++ b/.devpack-for-spring/plugin-configuration.yaml @@ -0,0 +1,617 @@ +format: + gradle: + id: io.spring.javaformat + version: 0.0.47 + default-task: format + description: Format the source code + tasks: + format: format + repository: gradlePluginPortal() + maven: + id: io.spring.javaformat:spring-javaformat-maven-plugin + version: 0.0.47 + default-task: format + description: Format the source code + tasks: + format: :apply +dependencies: + gradle: + id: io.github.rockcrafters.rockcraft + version: 1.2.4 + repository: gradlePluginPortal() + default-task: dependencies-export + description: | + Save project dependencies + tasks: + dependencies-export: dependencies-export + maven: + id: io.github.rockcrafters:rockcraft-maven-plugin + version: 1.2.4 + default-task: dependencies-export + description: | + Save project dependencies + tasks: + dependencies-export: :create-build-rock +rockcraft: + gradle: + id: io.github.rockcrafters.rockcraft + version: 1.2.4 + repository: gradlePluginPortal() + default-task: build-rock + description: | + Plugin for rock image generation + tasks: + create-rock: create-rock + build-rock: build-rock + create-build-rock: create-build-rock + build-build-rock: build-build-rock + push-rock: push-rock + push-build-rock: push-build-rock + maven: + id: io.github.rockcrafters:rockcraft-maven-plugin + version: 1.2.4 + default-task: build-rock + description: | + Plugin for rock image generation + tasks: + create-rock: + - install + - :create-rock + build-rock: + - install + - :create-rock + - :build-rock + create-build-rock: + - :create-build-rock + push-rock: + - install + - :create-rock + - :build-rock + - :push-rock + push-build-rock: + - install + - :create-build-rock + - :build-build-rock + - :push-build-rock +checkstyleGoogle: + resources: + - path: .config/checkstyle/checkstyle.xml + content: | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + gradle: + id: checkstyle + default-task: checkstyle + configuration: + gradleGroovy: | + checkstyle { + toolVersion = '13.3.0' + configDirectory = file("${rootProject.projectDir}/.config/checkstyle") + } + dependencies { + checkstyle 'com.puppycrawl.tools:checkstyle:13.3.0' + } + gradleKotlin: | + checkstyle { + toolVersion = "13.3.0" + configDirectory = file("${rootProject.projectDir}/.config/checkstyle") + } + dependencies { + checkstyle("com.puppycrawl.tools:checkstyle:13.3.0") + } + tasks: + checkstyle: + - checkStyleMain + - checkStyleTest + checkstyleMain: + - checkstyleMain + checkstyleTest: + - checkstyleTest + maven: + id: org.apache.maven.plugins:maven-checkstyle-plugin + version: 3.6.0 + default-task: check + tasks: + check: checkstyle:check + configuration: + maven: + configuration: | + + ${project.basedir}/.config/checkstyle/checkstyle.xml + + dependencies: | + + + com.puppycrawl.tools + checkstyle + 13.3.0 + + + executions: | + + + checkstyle + validate + + check + + + From 024a72f8f95c9ce4909fe46051d1fe49cc7f0b40 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Sat, 28 Mar 2026 17:39:40 +1300 Subject: [PATCH 09/13] chore: update copyright year Signed-off-by: Vladimir Petko --- .../canonical/devpackspring/rewrite/DependencyMergeUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java b/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java index b843e22d..285da94f 100644 --- a/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java +++ b/src/main/java/com/canonical/devpackspring/rewrite/DependencyMergeUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2025 the original author or authors. + * Copyright 2026 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 718ed10cf4e09369dd5afd1b50120dbb1b87253a Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Sat, 28 Mar 2026 18:05:21 +1300 Subject: [PATCH 10/13] fix: reorder configuration file priority. --- .../canonical/devpackspring/ConfigUtil.java | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/canonical/devpackspring/ConfigUtil.java b/src/main/java/com/canonical/devpackspring/ConfigUtil.java index edf983d8..7177637e 100644 --- a/src/main/java/com/canonical/devpackspring/ConfigUtil.java +++ b/src/main/java/com/canonical/devpackspring/ConfigUtil.java @@ -33,13 +33,33 @@ public abstract class ConfigUtil { private static final Log LOG = LogFactory.getLog(ConfigUtil.class); /** - * Opens configuration file stream + * Opens configuration file stream from: + * 1. System Property + * 2. Environment Variable + * 3. Current directory .devpack-for-spring/conffile + * 4. User home .config/devpack-for-spring/conffile + * 5. Embedded resource /com/canonical/devpackspring/conffile * @param environment - environment variable specifying path to the file * @param fileName - configuration file name * @return configuration file InputStream * @throws FileNotFoundException - configuration file not found */ public static InputStream openConfigurationFile(String environment, String fileName) throws FileNotFoundException { + + String pluginConfigurationFile = System.getProperty(environment); + if (pluginConfigurationFile == null) { + pluginConfigurationFile = System.getenv(environment); + } + if (pluginConfigurationFile != null) { + if (Files.exists(Path.of(pluginConfigurationFile))) { + LOG.info("Reading configuration from " + pluginConfigurationFile); + return new FileInputStream(pluginConfigurationFile); + } + else { + LOG.warn("Configuration file " + environment + "=" + pluginConfigurationFile + " does not exist."); + } + } + Path currentConfigPath = Path.of(System.getProperty("user.dir")) .resolve(".devpack-for-spring") .resolve(fileName); @@ -57,19 +77,6 @@ public static InputStream openConfigurationFile(String environment, String fileN return new FileInputStream(configPath.toFile()); } - String pluginConfigurationFile = System.getenv(environment); - if (pluginConfigurationFile == null) { - pluginConfigurationFile = System.getProperty(environment); - } - if (pluginConfigurationFile != null) { - if (Files.exists(Path.of(pluginConfigurationFile))) { - LOG.info("Reading configuration from " + pluginConfigurationFile); - return new FileInputStream(pluginConfigurationFile); - } - else { - LOG.warn("Configuration file " + environment + "=" + pluginConfigurationFile + " does not exist."); - } - } LOG.info("Reading default configuration " + fileName); return ConfigUtil.class.getResourceAsStream(String.format("/com/canonical/devpackspring/%s", fileName)); } From df9b8d64661327aa7d2e86abecce158c73274c6a Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Sat, 28 Mar 2026 18:11:43 +1300 Subject: [PATCH 11/13] chore: apply auto format --- .../java/com/canonical/devpackspring/ConfigUtil.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/canonical/devpackspring/ConfigUtil.java b/src/main/java/com/canonical/devpackspring/ConfigUtil.java index 7177637e..034fdbcf 100644 --- a/src/main/java/com/canonical/devpackspring/ConfigUtil.java +++ b/src/main/java/com/canonical/devpackspring/ConfigUtil.java @@ -33,12 +33,10 @@ public abstract class ConfigUtil { private static final Log LOG = LogFactory.getLog(ConfigUtil.class); /** - * Opens configuration file stream from: - * 1. System Property - * 2. Environment Variable - * 3. Current directory .devpack-for-spring/conffile - * 4. User home .config/devpack-for-spring/conffile - * 5. Embedded resource /com/canonical/devpackspring/conffile + * Opens configuration file stream from: 1. System Property 2. Environment Variable 3. + * Current directory .devpack-for-spring/conffile 4. User home + * .config/devpack-for-spring/conffile 5. Embedded resource + * /com/canonical/devpackspring/conffile * @param environment - environment variable specifying path to the file * @param fileName - configuration file name * @return configuration file InputStream From 688b27d1c8e05b3e866f7e37fe27ea4ddc3d7db5 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Sat, 28 Mar 2026 19:11:27 +1300 Subject: [PATCH 12/13] chore: drop logging property, does not work for child process --- .github/workflows/pr-self-hosted.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/pr-self-hosted.yml b/.github/workflows/pr-self-hosted.yml index 4341437e..9b45a302 100644 --- a/.github/workflows/pr-self-hosted.yml +++ b/.github/workflows/pr-self-hosted.yml @@ -28,7 +28,6 @@ jobs: rm -rf /home/runner/.local/state/rockcraft/log/* sudo snap install rockcraft --classic - run: | - export LOGGING_LEVEL_COM_CANONICAL_DEVPACKSPRING_PROCESSUTIL=DEBUG ./gradlew build -PexcludeTags=boot,maven-modification,maven-dependency --no-daemon --stacktrace - name: Store reports if: failure() From 29baf6ca9a88179a805ba636100b2c576658cddb Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Sat, 28 Mar 2026 19:11:53 +1300 Subject: [PATCH 13/13] chore: use java 25 in Gradle/Kotlin test. --- test-data/projects/gradle-kotlin/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/projects/gradle-kotlin/build.gradle.kts b/test-data/projects/gradle-kotlin/build.gradle.kts index d4c10df0..e28cbedd 100644 --- a/test-data/projects/gradle-kotlin/build.gradle.kts +++ b/test-data/projects/gradle-kotlin/build.gradle.kts @@ -9,7 +9,7 @@ version = "0.0.1-SNAPSHOT" java { toolchain { - languageVersion = JavaLanguageVersion.of(21) + languageVersion = JavaLanguageVersion.of(25) } }