You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by jd...@apache.org on 2026/04/09 05:43:26 UTC

(grails-core) 04/04: Ensure dependencies match the grails-bom on micronaut

This is an automated email from the ASF dual-hosted git repository.

jdaugherty pushed a commit to branch spring-boot-4
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 2b9e07544387200a2e0f17c8519acb19c6d2924f
Author: James Daugherty <jd...@jdresources.net>
AuthorDate: Thu Apr 9 01:43:14 2026 -0400

    Ensure dependencies match the grails-bom on micronaut
---
 dependencies.gradle                                | 13 +++++
 .../src/en/guide/upgrading/upgrading80x.adoc       | 33 ++++++++++++-
 .../forge/build/dependencies/Coordinate.java       |  4 ++
 .../forge/build/dependencies/Dependency.java       | 29 ++++++++---
 .../build/dependencies/DependencyCoordinate.java   |  8 ++++
 .../forge/build/gradle/GradleDependency.java       |  4 +-
 .../grails/forge/feature/grails/GrailsBase.java    |  7 ++-
 .../gradle/plugin/core/GrailsGradlePlugin.groovy   | 56 ++++++++++++++++++----
 grails-micronaut/build.gradle                      |  5 +-
 grails-test-examples/issue-11767/build.gradle      |  2 +-
 .../micronaut-groovy-only/build.gradle             |  2 +-
 grails-test-examples/micronaut/build.gradle        |  4 +-
 .../mongodb/springboot/build.gradle                |  2 +-
 .../plugins/issue-11767/build.gradle               |  2 +-
 .../plugins/micronaut-singleton/build.gradle       |  2 +-
 15 files changed, 146 insertions(+), 27 deletions(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index b0e1a95bfb..4665bf6bcf 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -83,6 +83,8 @@ ext {
             'rxjava2.version'             : '2.2.21',
             'rxjava3.version'             : '3.1.12',
             'selenium.version'            : '4.38.0',
+            'kotlin.version'              : '2.2.21',
+            'mockito.version'             : '5.20.0',
             'spock.version'               : '2.3-groovy-4.0',
             'sitemesh.version'            : '2.6.0',
             'starter-sitemesh.version'    : '3.2.2',
@@ -103,6 +105,7 @@ ext {
             'bootstrap-icons'          : "org.webjars.npm:bootstrap-icons:${bomDependencyVersions['bootstrap-icons.version']}",
             'commons-codec'            : "commons-codec:commons-codec:${bomDependencyVersions['commons-codec.version']}",
             'commons-lang3'            : "org.apache.commons:commons-lang3:${bomDependencyVersions['commons-lang3.version']}",
+            'byte-buddy-agent'         : "net.bytebuddy:byte-buddy-agent:${gradleBomDependencyVersions['byte-buddy.version']}",
             'geb-spock'                : "org.apache.groovy.geb:geb-spock:${bomDependencyVersions['geb-spock.version']}",
             // start - restate the groovy-bom includes here because the spring dependency management will pick the library from spring-boot-dependencies otherwise
             'groovy'              : "org.apache.groovy:groovy:${bomDependencyVersions['groovy.version']}",
@@ -136,6 +139,16 @@ ext {
             'groovy-xml'          : "org.apache.groovy:groovy-xml:${bomDependencyVersions['groovy.version']}",
             'groovy-yaml'         : "org.apache.groovy:groovy-yaml:${bomDependencyVersions['groovy.version']}",
             // end - restate the groovy-bom here because the spring dependency management
+            // start - restate versions managed by Spring Boot / Spock BOMs so enforcedPlatform can enforce them
+            // when micronaut is used (Micronaut 5 platform declares higher versions that override preferences)
+            'kotlin-stdlib'            : "org.jetbrains.kotlin:kotlin-stdlib:${bomDependencyVersions['kotlin.version']}",
+            'kotlin-stdlib-common'     : "org.jetbrains.kotlin:kotlin-stdlib-common:${bomDependencyVersions['kotlin.version']}",
+            'kotlin-stdlib-jdk7'       : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${bomDependencyVersions['kotlin.version']}",
+            'kotlin-stdlib-jdk8'       : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${bomDependencyVersions['kotlin.version']}",
+            'mockito-core'             : "org.mockito:mockito-core:${bomDependencyVersions['mockito.version']}",
+            'spock-core'               : "org.spockframework:spock-core:${bomDependencyVersions['spock.version']}",
+            'spock-spring'             : "org.spockframework:spock-spring:${bomDependencyVersions['spock.version']}",
+            // end - restate versions managed by inherited BOMs
             'jquery'                   : "org.webjars.npm:jquery:${bomDependencyVersions['jquery.version']}",
             'liquibase-hibernate5'     : "org.liquibase:liquibase:${bomDependencyVersions['liquibase-hibernate5.version']}",
             'liquibase-hibernate5-cdi' : "org.liquibase:liquibase-cdi:${bomDependencyVersions['liquibase-hibernate5.version']}",
diff --git a/grails-doc/src/en/guide/upgrading/upgrading80x.adoc b/grails-doc/src/en/guide/upgrading/upgrading80x.adoc
index 8b0259a3c5..5247ad406a 100644
--- a/grails-doc/src/en/guide/upgrading/upgrading80x.adoc
+++ b/grails-doc/src/en/guide/upgrading/upgrading80x.adoc
@@ -143,7 +143,9 @@ import org.springframework.http.HttpStatus
 response.status = HttpStatus.FOUND.value()
 ----
 
-==== 7. Micronaut Integration - CLASSIC Boot Loader Removed
+==== 7. Micronaut Integration Changes
+
+===== 7.1 CLASSIC Boot Loader Removed
 
 Spring Boot 4 removed the `CLASSIC` loader implementation (`LoaderImplementation.CLASSIC`).
 In Grails 7, the Grails Gradle Plugin automatically configured `bootJar` and `bootWar` tasks to use the `CLASSIC` loader when the `grails-micronaut` plugin was detected.
@@ -167,6 +169,35 @@ tasks.withType(BootArchive).configureEach {
 * The `grails-micronaut` plugin continues to work with Spring Boot 4's default loader.
 If you experience issues running your Micronaut-enabled Grails application via `java -jar`, please report them to the https://github.com/apache/grails-core/issues[Grails issue tracker].
 
+===== 7.2 enforcedPlatform Required for grails-bom
+
+When using Micronaut, the `grails-bom` **must** be applied as `enforcedPlatform` instead of `platform`.
+The Micronaut 5 platform declares its own managed dependency versions that conflict with those managed by the `grails-bom`.
+Without `enforcedPlatform`, Gradle's default conflict resolution (highest version wins) allows the Micronaut platform to silently override `grails-bom` versions, leading to binary incompatibilities at runtime.
+
+For example, without `enforcedPlatform`:
+
+* **Groovy** is upgraded from 4.0.x to 5.0.x -- Groovy 5 has breaking API changes (e.g. `ArrayGroovyMethods` moved) that cause `NoClassDefFoundError` at runtime
+* **Spock** is upgraded from 2.3-groovy-4.0 to 2.4-groovy-5.0 -- compiled against Groovy 5, incompatible with Groovy 4 classes
+* **Kotlin**, **Mockito**, **Byte Buddy** are upgraded to versions not tested with the Grails stack
+
+Using `enforcedPlatform` makes all `grails-bom` constraints strictly versioned so they cannot be overridden by any transitive dependency.
+
+The Grails Gradle Plugin will error at configuration time if `grails-micronaut` is detected and `grails-bom` is not applied as `enforcedPlatform`.
+
+[source,groovy]
+.build.gradle
+----
+dependencies {
+    // enforcedPlatform is required when using grails-micronaut
+    implementation enforcedPlatform("org.apache.grails:grails-bom:$grailsVersion")
+
+    implementation 'org.apache.grails:grails-micronaut'
+}
+----
+
+NOTE: Applications generated by the Grails Forge automatically use `enforcedPlatform` when the Micronaut feature is selected.
+
 ==== 8. Enum Serialization Default Changed
 
 As announced in the Grails 7.0.2 deprecation notice, the `SimpleEnumMarshaller` is now the default for JSON and XML enum serialization in Grails 8.
diff --git a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/dependencies/Coordinate.java b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/dependencies/Coordinate.java
index c96a4c3702..5fb61cdf75 100644
--- a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/dependencies/Coordinate.java
+++ b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/dependencies/Coordinate.java
@@ -50,4 +50,8 @@ public interface Coordinate {
     String getVersion();
 
     boolean isPom();
+
+    default boolean isEnforced() {
+        return false;
+    }
 }
diff --git a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/dependencies/Dependency.java b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/dependencies/Dependency.java
index c61dd6777c..bc43b5fc7b 100644
--- a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/dependencies/Dependency.java
+++ b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/dependencies/Dependency.java
@@ -36,6 +36,7 @@ public final class Dependency {
     private final int order;
     private final boolean annotationProcessorPriority;
     private final boolean pom;
+    private final boolean enforced;
 
     private Dependency(Scope scope,
                        String groupId,
@@ -46,7 +47,8 @@ public final class Dependency {
                        boolean requiresLookup,
                        boolean annotationProcessorPriority,
                        int order,
-                       boolean pom) {
+                       boolean pom,
+                       boolean enforced) {
         this.scope = scope;
         this.groupId = groupId;
         this.artifactId = artifactId;
@@ -57,6 +59,7 @@ public final class Dependency {
         this.annotationProcessorPriority = annotationProcessorPriority;
         this.order = order;
         this.pom = pom;
+        this.enforced = enforced;
     }
 
     private Dependency(Scope scope,
@@ -67,8 +70,9 @@ public final class Dependency {
                        boolean requiresLookup,
                        boolean annotationProcessorPriority,
                        int order,
-                       boolean pom) {
-        this(scope, groupId, artifactId, version, versionProperty, null, requiresLookup, annotationProcessorPriority, order, pom);
+                       boolean pom,
+                       boolean enforced) {
+        this(scope, groupId, artifactId, version, versionProperty, null, requiresLookup, annotationProcessorPriority, order, pom, enforced);
     }
 
     public Scope getScope() {
@@ -101,6 +105,10 @@ public final class Dependency {
         return pom;
     }
 
+    public boolean isEnforced() {
+        return enforced;
+    }
+
     public static Builder builder() {
         return new Builder();
     }
@@ -119,7 +127,8 @@ public final class Dependency {
                 false,
                 annotationProcessorPriority,
                 order,
-                coordinate.isPom());
+                coordinate.isPom(),
+                coordinate.isEnforced());
     }
 
     public Dependency scope(Scope newScope) {
@@ -132,7 +141,8 @@ public final class Dependency {
                 requiresLookup,
                 annotationProcessorPriority,
                 order,
-                pom);
+                pom,
+                enforced);
     }
 
     public boolean isAnnotationProcessorPriority() {
@@ -172,6 +182,7 @@ public final class Dependency {
         private boolean template = false;
         private boolean annotationProcessorPriority = false;
         private boolean pom = false;
+        private boolean enforced = false;
 
         public Builder scope(@NonNull Scope scope) {
             if (template) {
@@ -325,6 +336,11 @@ public final class Dependency {
             return this;
         }
 
+        public Builder enforced(boolean enforced) {
+            this.enforced = enforced;
+            return this;
+        }
+
         public Dependency build() {
             Objects.requireNonNull(scope, "The dependency scope must be set");
             Objects.requireNonNull(artifactId, "The artifact id must be set");
@@ -352,7 +368,8 @@ public final class Dependency {
                     requiresLookup,
                     annotationProcessorPriority,
                     order,
-                    pom);
+                    pom,
+                    enforced);
         }
 
         private Builder copy() {
diff --git a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/dependencies/DependencyCoordinate.java b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/dependencies/DependencyCoordinate.java
index 913dd66f19..6df627bc3c 100644
--- a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/dependencies/DependencyCoordinate.java
+++ b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/dependencies/DependencyCoordinate.java
@@ -34,6 +34,7 @@ public class DependencyCoordinate implements Coordinate, Ordered {
     private final String version;
     private final int order;
     private final boolean pom;
+    private final boolean enforced;
 
     public DependencyCoordinate(Dependency dependency) {
         this(dependency, false);
@@ -49,6 +50,7 @@ public class DependencyCoordinate implements Coordinate, Ordered {
         }
         order = dependency.getOrder();
         pom = dependency.isPom();
+        enforced = dependency.isEnforced();
     }
 
     public DependencyCoordinate(String groupId,
@@ -61,6 +63,7 @@ public class DependencyCoordinate implements Coordinate, Ordered {
         this.version = version;
         this.order = order;
         this.pom = pom;
+        this.enforced = false;
     }
 
     @Override
@@ -91,6 +94,11 @@ public class DependencyCoordinate implements Coordinate, Ordered {
         return pom;
     }
 
+    @Override
+    public boolean isEnforced() {
+        return enforced;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) {
diff --git a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/gradle/GradleDependency.java b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/gradle/GradleDependency.java
index 7440148f40..0f72ed14a4 100644
--- a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/gradle/GradleDependency.java
+++ b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/gradle/GradleDependency.java
@@ -104,7 +104,9 @@ public class GradleDependency extends DependencyCoordinate {
     public String toSnippet() {
         String optionalSpace = gradleConfiguration == INTEGRATION_TEST_IMPLEMENTATION_TEST_FIXTURES ? "" : " ";
         String snippet = gradleConfiguration.getConfigurationName() + optionalSpace;
-        if (isPom()) {
+        if (isPom() && isEnforced()) {
+            snippet += "enforcedPlatform(";
+        } else if (isPom()) {
             snippet += "platform(";
         } else if (gradleConfiguration == INTEGRATION_TEST_IMPLEMENTATION_TEST_FIXTURES) {
             snippet += "(";
diff --git a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/feature/grails/GrailsBase.java b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/feature/grails/GrailsBase.java
index da9a07bc45..edd85eee68 100644
--- a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/feature/grails/GrailsBase.java
+++ b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/feature/grails/GrailsBase.java
@@ -24,6 +24,7 @@ import org.grails.forge.application.generator.GeneratorContext;
 import org.grails.forge.build.dependencies.Dependency;
 import org.grails.forge.feature.DefaultFeature;
 import org.grails.forge.feature.Feature;
+import org.grails.forge.feature.micronaut.GrailsMicronaut;
 import org.grails.forge.options.Options;
 import org.grails.forge.template.URLTemplate;
 
@@ -55,11 +56,15 @@ public class GrailsBase implements DefaultFeature {
     @Override
     public void apply(GeneratorContext generatorContext) {
 
-        // This ensures that grails-bom forces milestones to override snapshots
+        // When micronaut is used, enforcedPlatform is required to prevent the Micronaut
+        // platform from overriding grails-bom managed versions (e.g. Groovy 5 over Groovy 4)
+        boolean useEnforced = generatorContext.isFeaturePresent(GrailsMicronaut.class);
+
         generatorContext.addDependency(Dependency.builder()
                 .groupId("org.apache.grails")
                 .artifactId("grails-bom")
                 .pom(true)
+                .enforced(useEnforced)
                 .version("$grailsVersion")
                 .implementation());
 
diff --git a/grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/core/GrailsGradlePlugin.groovy b/grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/core/GrailsGradlePlugin.groovy
index 88fd83777b..3d94a550f7 100644
--- a/grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/core/GrailsGradlePlugin.groovy
+++ b/grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/core/GrailsGradlePlugin.groovy
@@ -41,6 +41,7 @@ import org.gradle.api.Task
 import org.gradle.api.artifacts.Configuration
 import org.gradle.api.artifacts.Dependency
 import org.gradle.api.artifacts.DependencyResolveDetails
+import org.gradle.api.artifacts.ModuleDependency
 import org.gradle.api.artifacts.DependencySet
 import org.gradle.api.attributes.AttributeMatchingStrategy
 import org.gradle.api.file.DuplicatesStrategy
@@ -277,7 +278,7 @@ class GrailsGradlePlugin implements Plugin<Project> {
 ${importStatements}
                     }
                 }
-            """
+            """ as String
         }
     }
 
@@ -394,10 +395,10 @@ ${importStatements}
 
             Task buildPropertiesTask = project.tasks.create('buildProperties')
             Map<String, Object> buildPropertiesContents = [
-                'grails.env': Environment.isSystemSet() ? Environment.getCurrent().getName() : Environment.PRODUCTION.getName(),
-                'info.app.name': project.name,
-                'info.app.version': project.version instanceof Serializable ? project.version : project.version.toString(),
-                'info.app.grailsVersion': project.properties.get('grailsVersion')
+                    'grails.env': Environment.isSystemSet() ? Environment.getCurrent().getName() : Environment.PRODUCTION.getName(),
+                    'info.app.name': project.name,
+                    'info.app.version': project.version instanceof Serializable ? project.version : project.version.toString(),
+                    'info.app.grailsVersion': project.properties.get('grailsVersion')
             ]
 
             buildPropertiesTask.inputs.properties(buildPropertiesContents)
@@ -421,7 +422,7 @@ ${importStatements}
     @CompileStatic
     protected void configureMicronaut(Project project) {
         project.afterEvaluate {
-            boolean micronautEnabled = project.getConfigurations().getByName('implementation').getDependencies().findAll { Dependency dep -> dep.group == 'org.apache.grails' && dep.name == 'grails-micronaut' } as boolean
+            boolean micronautEnabled = project.getConfigurations().getByName('runtimeClasspath').getAllDependencies().findAll { Dependency dep -> dep.group == 'org.apache.grails' && dep.name == 'grails-micronaut' } as boolean
             if (!micronautEnabled) {
                 return
             }
@@ -438,6 +439,12 @@ ${importStatements}
                 throw new GradleException('`micronautPlatformVersion` property must be set to use the Grails Micronaut plugin.')
             }
 
+            // Validate that grails-bom is applied as enforcedPlatform. Micronaut 5's platform
+            // declares Groovy 5, Kotlin 2.3, etc. which override the grails-bom via conflict
+            // resolution. enforcedPlatform makes all BOM constraints strictly versioned so they
+            // cannot be overridden by transitive dependencies.
+            validateEnforcedBom(project)
+
             // grails-micronaut exports the platform, but force the version to the user specified version
             project.configurations.configureEach { Configuration configuration ->
                 configuration.resolutionStrategy.eachDependency { DependencyResolveDetails details ->
@@ -453,6 +460,36 @@ ${importStatements}
         }
     }
 
+    /**
+     * Validates that grails-bom is applied as an enforcedPlatform when micronaut is used.
+     * Without enforcedPlatform, the Micronaut platform's version constraints (e.g. Groovy 5,
+     * Kotlin 2.3, Spock 2.4) will override the grails-bom versions via conflict resolution.
+     */
+    @CompileStatic
+    protected static void validateEnforcedBom(Project project) {
+        Configuration implConfig = project.configurations.findByName('implementation')
+        if (implConfig == null) {
+            return
+        }
+
+        for (Dependency dep : implConfig.dependencies) {
+            if (dep.name == 'grails-bom' && dep instanceof ModuleDependency) {
+                Object categoryAttr = ((ModuleDependency) dep).attributes.getAttribute(
+                        org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE
+                )
+                if (categoryAttr != null && categoryAttr.toString() == org.gradle.api.attributes.Category.ENFORCED_PLATFORM) {
+                    return // correctly configured
+                }
+                throw new GradleException(
+                        "Project '${project.name}' uses Micronaut but applies grails-bom as a regular platform. " +
+                                'Micronaut\'s platform declares higher versions of Groovy, Spock, and Kotlin that will ' +
+                                'override the grails-bom via conflict resolution. Change to:\n\n' +
+                                '    implementation enforcedPlatform(project(\':grails-bom\'))\n'
+                )
+            }
+        }
+    }
+
     @CompileStatic
     protected void configureGroovy(Project project) {
         final String groovyVersion = project.properties['groovy.version']
@@ -890,9 +927,9 @@ ${importStatements}
             }
 
             Map<String, String> replaceTokens = [
-                'info.app.name': project.name,
-                'info.app.version': project.version?.toString(),
-                'info.app.grailsVersion': grailsVersion
+                    'info.app.name': project.name,
+                    'info.app.version': project.version?.toString(),
+                    'info.app.grailsVersion': grailsVersion
             ]
 
             task.from(project.relativePath('src/main/templates')) { spec ->
@@ -1031,6 +1068,7 @@ ${importStatements}
 
     @CompileStatic
     private static final class OnlyOneGrailsPlugin {
+
         String pluginClassname
     }
 }
diff --git a/grails-micronaut/build.gradle b/grails-micronaut/build.gradle
index a2dfd64072..a47f2a6190 100644
--- a/grails-micronaut/build.gradle
+++ b/grails-micronaut/build.gradle
@@ -39,8 +39,9 @@ dependencies {
     api platform("io.micronaut.platform:micronaut-platform:$micronautPlatformVersion")
     api 'io.micronaut.spring:micronaut-spring-boot-starter'
 
-    // For the grails plugin interface
-    compileOnly platform(project(':grails-bom'))
+    // For the grails plugin interface. enforcedPlatform ensures Groovy 4.x from the
+    // grails-bom wins over Groovy 5.x from the micronaut platform during compilation.
+    compileOnly enforcedPlatform(project(':grails-bom'))
     compileOnly project(':grails-core')
 }
 
diff --git a/grails-test-examples/issue-11767/build.gradle b/grails-test-examples/issue-11767/build.gradle
index 949b36c6b0..aaea342506 100644
--- a/grails-test-examples/issue-11767/build.gradle
+++ b/grails-test-examples/issue-11767/build.gradle
@@ -28,7 +28,7 @@ group = 'issue11767.app'
 apply plugin: 'org.apache.grails.gradle.grails-web'
 
 dependencies {
-    implementation platform(project(':grails-bom'))
+    implementation enforcedPlatform(project(':grails-bom'))
 
     implementation 'org.apache.grails:grails-dependencies-starter-web'
     implementation 'org.apache.grails:grails-micronaut'
diff --git a/grails-test-examples/micronaut-groovy-only/build.gradle b/grails-test-examples/micronaut-groovy-only/build.gradle
index b23298c91e..942e545cf6 100644
--- a/grails-test-examples/micronaut-groovy-only/build.gradle
+++ b/grails-test-examples/micronaut-groovy-only/build.gradle
@@ -34,7 +34,7 @@ apply plugin: 'org.apache.grails.gradle.grails-web'
 // processors when grails-micronaut is present on the classpath.
 
 dependencies {
-    implementation platform(project(':grails-bom'))
+    implementation enforcedPlatform(project(':grails-bom'))
 
     implementation 'org.apache.grails:grails-dependencies-starter-web'
     implementation 'org.apache.grails:grails-micronaut'
diff --git a/grails-test-examples/micronaut/build.gradle b/grails-test-examples/micronaut/build.gradle
index 0dfb5421d6..e8fb8045ff 100644
--- a/grails-test-examples/micronaut/build.gradle
+++ b/grails-test-examples/micronaut/build.gradle
@@ -37,7 +37,7 @@ apply plugin: 'cloud.wondrify.asset-pipeline'
 apply plugin: 'org.apache.grails.gradle.grails-gsp'
 
 dependencies {
-    implementation platform(project(':grails-bom'))
+    implementation enforcedPlatform(project(':grails-bom'))
 
     implementation "io.micronaut:micronaut-http-client:$micronautHttpClientVersion"
     implementation "io.micronaut:micronaut-retry:$micronautHttpClientVersion"
@@ -51,7 +51,7 @@ dependencies {
     annotationProcessor 'io.micronaut:micronaut-inject-java'
     annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
 
-    testAndDevelopmentOnly platform(project(':grails-bom'))
+    testAndDevelopmentOnly enforcedPlatform(project(':grails-bom'))
     testAndDevelopmentOnly 'org.apache.grails:grails-dependencies-assets'
 
     runtimeOnly 'cloud.wondrify:asset-pipeline-grails'
diff --git a/grails-test-examples/mongodb/springboot/build.gradle b/grails-test-examples/mongodb/springboot/build.gradle
index 5855d0b24e..2282d12127 100644
--- a/grails-test-examples/mongodb/springboot/build.gradle
+++ b/grails-test-examples/mongodb/springboot/build.gradle
@@ -27,7 +27,7 @@ apply plugin: 'org.springframework.boot'
 
 dependencies {
 
-    implementation platform(project(':grails-bom'))
+    implementation enforcedPlatform(project(':grails-bom'))
 
     implementation 'org.apache.grails:grails-data-mongodb-spring-boot'
     implementation 'org.apache.groovy:groovy'
diff --git a/grails-test-examples/plugins/issue-11767/build.gradle b/grails-test-examples/plugins/issue-11767/build.gradle
index 1dff7f2e8f..ee62b49682 100644
--- a/grails-test-examples/plugins/issue-11767/build.gradle
+++ b/grails-test-examples/plugins/issue-11767/build.gradle
@@ -32,7 +32,7 @@ tasks.withType(Jar).configureEach {
 }
 
 dependencies {
-    implementation platform(project(':grails-bom'))
+    implementation enforcedPlatform(project(':grails-bom'))
     implementation 'org.apache.grails:grails-core'
     implementation 'org.apache.grails:grails-micronaut'
 
diff --git a/grails-test-examples/plugins/micronaut-singleton/build.gradle b/grails-test-examples/plugins/micronaut-singleton/build.gradle
index 0fc1861438..1dc402f1cc 100644
--- a/grails-test-examples/plugins/micronaut-singleton/build.gradle
+++ b/grails-test-examples/plugins/micronaut-singleton/build.gradle
@@ -28,7 +28,7 @@ group = 'com.example.grails.plugins'
 apply plugin: 'org.apache.grails.gradle.grails-plugin'
 
 dependencies {
-    implementation platform(project(':grails-bom'))
+    implementation enforcedPlatform(project(':grails-bom'))
 
     api 'org.apache.grails:grails-dependencies-starter-web'
     api 'org.apache.grails:grails-micronaut'