diff --git a/src/main/java/org/openrewrite/java/flyway/AlignFlywayModuleScopeWithFlywayCore.java b/src/main/java/org/openrewrite/java/flyway/AlignFlywayModuleScopeWithFlywayCore.java
new file mode 100644
index 000000000..00ae042ba
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/flyway/AlignFlywayModuleScopeWithFlywayCore.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2026 the original author or authors.
+ *
+ * Licensed under the Moderne Source Available License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://docs.moderne.io/licensing/moderne-source-available-license
+ *
+ * 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 org.openrewrite.java.flyway;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.jspecify.annotations.Nullable;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Option;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.maven.ChangeDependencyScope;
+import org.openrewrite.maven.MavenIsoVisitor;
+import org.openrewrite.xml.tree.Xml;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class AlignFlywayModuleScopeWithFlywayCore extends Recipe {
+
+ @Option(displayName = "Flyway module artifactId",
+ description = "ArtifactId of the Flyway database module to align with `flyway-core`.",
+ example = "flyway-database-postgresql")
+ String artifactId;
+
+ String displayName = "Align Flyway module scope with flyway-core";
+
+ String description = "Ensures Flyway database modules keep the same declared Maven scope as `flyway-core` " +
+ "when migrations add or touch those dependencies.";
+
+ @Override
+ public TreeVisitor, ExecutionContext> getVisitor() {
+ return new MavenIsoVisitor() {
+ @Override
+ public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) {
+ Xml.Document d = super.visitDocument(document, ctx);
+ String flywayCoreScope = findDeclaredScope(d.getRoot(), "org.flywaydb", "flyway-core");
+ if (!"test".equals(flywayCoreScope)) {
+ return d;
+ }
+ return (Xml.Document) new ChangeDependencyScope("org.flywaydb", artifactId, "test").getVisitor().visitNonNull(d, ctx);
+ }
+
+ private @Nullable String findDeclaredScope(Xml.Tag tag, String groupId, String artifactId) {
+ if (isDependency(groupId, artifactId, tag)) {
+ return tag.getChildValue("scope").orElse(null);
+ }
+ java.util.List> content = tag.getContent();
+ if (content == null) {
+ return null;
+ }
+ for (Object child : content) {
+ if (child instanceof Xml.Tag) {
+ String scope = findDeclaredScope((Xml.Tag) child, groupId, artifactId);
+ if (scope != null || isDependency(groupId, artifactId, (Xml.Tag) child)) {
+ return scope;
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean isDependency(String groupId, String artifactId, Xml.Tag tag) {
+ return "dependency".equals(tag.getName()) &&
+ groupId.equals(tag.getChildValue("groupId").orElse(null)) &&
+ artifactId.equals(tag.getChildValue("artifactId").orElse(null));
+ }
+ };
+ }
+}
diff --git a/src/main/resources/META-INF/rewrite/flyway-10.yml b/src/main/resources/META-INF/rewrite/flyway-10.yml
index 7d64a7c80..ff574d1ef 100644
--- a/src/main/resources/META-INF/rewrite/flyway-10.yml
+++ b/src/main/resources/META-INF/rewrite/flyway-10.yml
@@ -24,9 +24,17 @@ preconditions:
- org.openrewrite.Singleton
recipeList:
- org.openrewrite.java.flyway.AddFlywayModulePostgreSQL
+ - org.openrewrite.java.flyway.AlignFlywayModuleScopeWithFlywayCore:
+ artifactId: flyway-database-postgresql
- org.openrewrite.java.flyway.AddFlywayModuleMySQL
+ - org.openrewrite.java.flyway.AlignFlywayModuleScopeWithFlywayCore:
+ artifactId: flyway-mysql
- org.openrewrite.java.flyway.AddFlywayModuleOracle
+ - org.openrewrite.java.flyway.AlignFlywayModuleScopeWithFlywayCore:
+ artifactId: flyway-database-oracle
- org.openrewrite.java.flyway.AddFlywayModuleSqlServer
+ - org.openrewrite.java.flyway.AlignFlywayModuleScopeWithFlywayCore:
+ artifactId: flyway-sqlserver
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.flyway.AddFlywayModulePostgreSQL
diff --git a/src/test/java/org/openrewrite/java/flyway/MigrateToFlyway10Test.java b/src/test/java/org/openrewrite/java/flyway/MigrateToFlyway10Test.java
index 845513cbb..66ea04b9f 100644
--- a/src/test/java/org/openrewrite/java/flyway/MigrateToFlyway10Test.java
+++ b/src/test/java/org/openrewrite/java/flyway/MigrateToFlyway10Test.java
@@ -17,6 +17,7 @@
import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
+import org.openrewrite.Issue;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;
@@ -284,4 +285,185 @@ void addSqlServerDependency() {
)
);
}
+
+ @Issue("https://github.com/openrewrite/rewrite-spring/issues/1052")
+ @Test
+ void addPostgresDependencyWithTestScope() {
+ assertFlywayModuleAddedWithTestScope(
+ "org.postgresql",
+ "postgresql",
+ "flyway-database-postgresql"
+ );
+ }
+
+ @Issue("https://github.com/openrewrite/rewrite-spring/issues/1052")
+ @Test
+ void preserveExistingPostgresDependencyTestScope() {
+ rewriteRun(
+ //language=xml
+ pomXml(
+ pom(
+ "",
+ "\t4.0.0",
+ "\t",
+ "\t\torg.springframework.boot",
+ "\t\tspring-boot-starter-parent",
+ "\t\t3.3.12",
+ "\t\t",
+ "\t",
+ "\tcom.example",
+ "\tdemo",
+ "\t0.0.1-SNAPSHOT",
+ "\t",
+ "\t\t",
+ "\t\t\torg.flywaydb",
+ "\t\t\tflyway-core",
+ "\t\t\ttest",
+ "\t\t",
+ "\t\t",
+ "\t\t\torg.flywaydb",
+ "\t\t\tflyway-database-postgresql",
+ "\t\t\ttest",
+ "\t\t",
+ "\t\t",
+ "\t\t\torg.postgresql",
+ "\t\t\tpostgresql",
+ "\t\t\truntime",
+ "\t\t",
+ "\t",
+ ""
+ ),
+ pom(
+ "",
+ "\t4.0.0",
+ "\t",
+ "\t\torg.springframework.boot",
+ "\t\tspring-boot-starter-parent",
+ "\t\t3.3.12",
+ "\t\t",
+ "\t",
+ "\tcom.example",
+ "\tdemo",
+ "\t0.0.1-SNAPSHOT",
+ "\t",
+ "\t\t",
+ "\t\t\torg.flywaydb",
+ "\t\t\tflyway-core",
+ "\t\t\ttest",
+ "\t\t",
+ "\t\t",
+ "\t\t\torg.flywaydb",
+ "\t\t\tflyway-database-postgresql",
+ "\t\t\ttest",
+ "\t\t",
+ "\t\t",
+ "\t\t\torg.postgresql",
+ "\t\t\tpostgresql",
+ "\t\t\truntime",
+ "\t\t",
+ "\t",
+ ""
+ )
+ )
+ );
+ }
+
+ @Issue("https://github.com/openrewrite/rewrite-spring/issues/1052")
+ @Test
+ void addMySQLDependencyWithTestScope() {
+ assertFlywayModuleAddedWithTestScope(
+ "com.mysql",
+ "mysql-connector-j",
+ "flyway-mysql"
+ );
+ }
+
+ @Issue("https://github.com/openrewrite/rewrite-spring/issues/1052")
+ @Test
+ void addOracleDependencyWithTestScope() {
+ assertFlywayModuleAddedWithTestScope(
+ "com.oracle.database.jdbc",
+ "ojdbc11",
+ "flyway-database-oracle"
+ );
+ }
+
+ @Issue("https://github.com/openrewrite/rewrite-spring/issues/1052")
+ @Test
+ void addSqlServerDependencyWithTestScope() {
+ assertFlywayModuleAddedWithTestScope(
+ "com.microsoft.sqlserver",
+ "mssql-jdbc",
+ "flyway-sqlserver"
+ );
+ }
+
+ private void assertFlywayModuleAddedWithTestScope(String databaseGroupId, String databaseArtifactId, String flywayModuleArtifactId) {
+ rewriteRun(
+ //language=xml
+ pomXml(
+ pom(
+ "",
+ "\t4.0.0",
+ "\t",
+ "\t\torg.springframework.boot",
+ "\t\tspring-boot-starter-parent",
+ "\t\t3.3.12",
+ "\t\t",
+ "\t",
+ "\tcom.example",
+ "\tdemo",
+ "\t0.0.1-SNAPSHOT",
+ "\t",
+ "\t\t",
+ "\t\t\torg.flywaydb",
+ "\t\t\tflyway-core",
+ "\t\t\ttest",
+ "\t\t",
+ "\t\t",
+ ("\t\t\t%s").formatted(databaseGroupId),
+ ("\t\t\t%s").formatted(databaseArtifactId),
+ "\t\t\truntime",
+ "\t\t",
+ "\t",
+ ""
+ ),
+ pom(
+ "",
+ "\t4.0.0",
+ "\t",
+ "\t\torg.springframework.boot",
+ "\t\tspring-boot-starter-parent",
+ "\t\t3.3.12",
+ "\t\t",
+ "\t",
+ "\tcom.example",
+ "\tdemo",
+ "\t0.0.1-SNAPSHOT",
+ "\t",
+ "\t\t",
+ "\t\t\torg.flywaydb",
+ ("\t\t\t%s").formatted(flywayModuleArtifactId),
+ "\t\t\ttest",
+ "\t\t",
+ "\t\t",
+ "\t\t\torg.flywaydb",
+ "\t\t\tflyway-core",
+ "\t\t\ttest",
+ "\t\t",
+ "\t\t",
+ ("\t\t\t%s").formatted(databaseGroupId),
+ ("\t\t\t%s").formatted(databaseArtifactId),
+ "\t\t\truntime",
+ "\t\t",
+ "\t",
+ ""
+ )
+ )
+ );
+ }
+
+ private static String pom(String... lines) {
+ return String.join("\n", lines) + "\n";
+ }
}