diff --git a/.github/workflows/groovy-joint-workflow.yml b/.github/workflows/groovy-joint-workflow.yml index 05acefba531..6949e938c4d 100644 --- a/.github/workflows/groovy-joint-workflow.yml +++ b/.github/workflows/groovy-joint-workflow.yml @@ -160,6 +160,11 @@ jobs: run: | sed -i "/['\"]groovy\.version['\"][[:space:]]*:/c\'groovy.version':'${{ needs.build_groovy.outputs.groovyVersion }}'," dependencies.gradle - name: "🔨 Build and test Grails using the locally built Groovy snapshot" + # -PskipMicronautProjects excludes the Grails-Micronaut "island" (grails-micronaut, + # grails-micronaut-bom, and the micronaut-tied test-examples) from the build graph. + # That island intentionally pins Groovy 5 / Spock 2.4-groovy-5.0 via grails-micronaut-bom, + # which is incompatible with the Groovy 4.x snapshot this job swaps in above. + # See https://github.com/apache/grails-core/issues/15613. run: > ./gradlew build --continue @@ -167,6 +172,7 @@ jobs: -x groovydoc -PgebAtCheckWaiting -PskipCodeStyle + -PskipMicronautProjects -PmaxTestParallel=3 env: GRAILS_INCLUDE_MAVEN_LOCAL: true \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 1155d31dbd1..3f7bf4ca9ae 100644 --- a/gradle.properties +++ b/gradle.properties @@ -55,6 +55,11 @@ gradleCycloneDxPluginVersion=3.0.0 # micronaut libraries not in the bom due to the potential for spring mismatches micronautPlatformVersion=5.0.0-M2 +# Pass -PskipMicronautProjects (presence-based, like skipFunctionalTests / skipCodeStyle) +# to drop the Grails-Micronaut "island" (grails-micronaut, grails-micronaut-bom, and +# the micronaut-tied test-examples) from the build graph. See settings.gradle for the +# gating logic and grails-core#15613 for the rationale. + # Libraries only specific to test apps, these should not be exposed ersatzVersion=4.0.1 grailsSpringSecurityVersion=8.0.0-SNAPSHOT diff --git a/grails-doc/build.gradle b/grails-doc/build.gradle index 4d67c5a7de7..18b4b39eb58 100644 --- a/grails-doc/build.gradle +++ b/grails-doc/build.gradle @@ -140,17 +140,29 @@ String getVersion(String artifact) { def generateBomDocumentation = tasks.register('generateBomDocumentation') generateBomDocumentation.configure { Task it -> - it.dependsOn(':grails-bom:extractConstraints', ':grails-hibernate5-bom:extractConstraints', ':grails-micronaut-bom:extractConstraints') + // grails-micronaut-bom is gated by -PskipMicronautProjects in settings.gradle (see grails-core#15613). + // findProject returns null when the flag is set; the Micronaut BOM page is then omitted. + def grailsMicronautBom = findProject(':grails-micronaut-bom') + + def extractConstraintsDeps = [':grails-bom:extractConstraints', ':grails-hibernate5-bom:extractConstraints'] + if (grailsMicronautBom != null) { + extractConstraintsDeps << ':grails-micronaut-bom:extractConstraints' + } + it.dependsOn(*extractConstraintsDeps) it.description = 'Generates AsciiDoc tables listing Group, Artifact, and Version for all BOM variants (default, hibernate5, micronaut).' it.group = 'documentation' it.inputs.files(project(':grails-bom').layout.projectDirectory.asFileTree) it.inputs.files(project(':grails-hibernate5-bom').layout.projectDirectory.asFileTree) - it.inputs.files(project(':grails-micronaut-bom').layout.projectDirectory.asFileTree) + if (grailsMicronautBom != null) { + it.inputs.files(grailsMicronautBom.layout.projectDirectory.asFileTree) + } it.outputs.file(project.layout.projectDirectory.file('src/en/ref/Versions/Grails BOM.adoc')) it.outputs.file(project.layout.projectDirectory.file('src/en/ref/Versions/Grails BOM Hibernate5.adoc')) - it.outputs.file(project.layout.projectDirectory.file('src/en/ref/Versions/Grails BOM Micronaut.adoc')) + if (grailsMicronautBom != null) { + it.outputs.file(project.layout.projectDirectory.file('src/en/ref/Versions/Grails BOM Micronaut.adoc')) + } def versionsDir = project.layout.projectDirectory.dir('src/en/ref/Versions') it.doFirst { @@ -166,8 +178,13 @@ generateBomDocumentation.configure { Task it -> def grailsBomHibernate5ConstraintFile = project(':grails-hibernate5-bom').layout.buildDirectory.file('grails-hibernate5-bom-constraints.adoc') // Micronaut BOM - def bomMicronautDocumentFile = project.layout.projectDirectory.file('src/en/ref/Versions/Grails BOM Micronaut.adoc') - def grailsBomMicronautConstraintFile = project(':grails-micronaut-bom').layout.buildDirectory.file('grails-micronaut-bom-constraints.adoc') + def bomMicronautDocumentFile = grailsMicronautBom != null + ? project.layout.projectDirectory.file('src/en/ref/Versions/Grails BOM Micronaut.adoc') + : null + def grailsBomMicronautConstraintFile = grailsMicronautBom?.layout?.buildDirectory?.file('grails-micronaut-bom-constraints.adoc') + def micronautBomCrossRefSuffix = grailsMicronautBom != null + ? ' | link:Grails%20BOM%20Micronaut.html[Grails Micronaut BOM]' + : '' it.doLast { // Generate default BOM page @@ -176,7 +193,7 @@ generateBomDocumentation.configure { Task it -> writer.writeLine '' writer.writeLine 'This document provides information about the dependencies defined in the default Grails BOM - also known as `org.apache.grails:grails-bom`. The default BOM uses Hibernate 5 compatible dependency versions.' writer.writeLine '' - writer.writeLine 'NOTE: Variant BOMs are also available: link:Grails%20BOM%20Hibernate5.html[Grails Hibernate 5 BOM] | link:Grails%20BOM%20Micronaut.html[Grails Micronaut BOM]' + writer.writeLine "NOTE: Variant BOMs are also available: link:Grails%20BOM%20Hibernate5.html[Grails Hibernate 5 BOM]${micronautBomCrossRefSuffix}" writer.writeLine '' writer.write(grailsBomConstraintFile.get().asFile.text) writer.writeLine '' @@ -189,25 +206,27 @@ generateBomDocumentation.configure { Task it -> writer.writeLine '' writer.writeLine 'This document provides information about the dependencies defined in `org.apache.grails:grails-hibernate5-bom`. This BOM inherits from the default `grails-bom` and explicitly enforces Hibernate 5 compatible dependency versions for consumers using `enforcedPlatform`.' writer.writeLine '' - writer.writeLine 'See also: link:Grails%20BOM.html[Default BOM] | link:Grails%20BOM%20Micronaut.html[Grails Micronaut BOM]' + writer.writeLine "See also: link:Grails%20BOM.html[Default BOM]${micronautBomCrossRefSuffix}" writer.writeLine '' writer.write(grailsBomHibernate5ConstraintFile.get().asFile.text) writer.writeLine '' } it.logger.lifecycle "BOM Hibernate 5 Dependency Page generated to: ${bomHibernate5DocumentFile.asFile.absolutePath}" - // Generate Micronaut BOM page - bomMicronautDocumentFile.asFile.withWriter { writer -> - writer.writeLine '== Grails Micronaut BOM Dependencies' - writer.writeLine '' - writer.writeLine 'This document provides information about the dependencies defined in `org.apache.grails:grails-micronaut-bom`. This BOM inherits from the default `grails-bom` and layers Micronaut-specific dependency overrides on top. Consumers integrating with Micronaut should apply this BOM as `enforcedPlatform` so the Micronaut platform cannot override the managed versions.' - writer.writeLine '' - writer.writeLine 'See also: link:Grails%20BOM.html[Default BOM] | link:Grails%20BOM%20Hibernate5.html[Grails Hibernate 5 BOM]' - writer.writeLine '' - writer.write(grailsBomMicronautConstraintFile.get().asFile.text) - writer.writeLine '' + if (grailsMicronautBom != null) { + // Generate Micronaut BOM page + bomMicronautDocumentFile.asFile.withWriter { writer -> + writer.writeLine '== Grails Micronaut BOM Dependencies' + writer.writeLine '' + writer.writeLine 'This document provides information about the dependencies defined in `org.apache.grails:grails-micronaut-bom`. This BOM inherits from the default `grails-bom` and layers Micronaut-specific dependency overrides on top. Consumers integrating with Micronaut should apply this BOM as `enforcedPlatform` so the Micronaut platform cannot override the managed versions.' + writer.writeLine '' + writer.writeLine 'See also: link:Grails%20BOM.html[Default BOM] | link:Grails%20BOM%20Hibernate5.html[Grails Hibernate 5 BOM]' + writer.writeLine '' + writer.write(grailsBomMicronautConstraintFile.get().asFile.text) + writer.writeLine '' + } + it.logger.lifecycle "BOM Micronaut Dependency Page generated to: ${bomMicronautDocumentFile.asFile.absolutePath}" } - it.logger.lifecycle "BOM Micronaut Dependency Page generated to: ${bomMicronautDocumentFile.asFile.absolutePath}" } } diff --git a/settings.gradle b/settings.gradle index b92e62ca16a..2cc1ba3650f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -73,6 +73,20 @@ buildCache { rootProject.name = 'grails.core.ROOT' +// Presence-based toggle (matches project convention: skipFunctionalTests, skipCodeStyle, etc.). +// When -PskipMicronautProjects is passed (regardless of value), the Grails-Micronaut "island" +// is excluded from the build graph entirely: +// * grails-micronaut (Grails plugin that re-exports the Micronaut platform) +// * grails-micronaut-bom (overrides Groovy/Spock to Groovy 5 / Spock 2.4-groovy-5.0) +// * the five grails-test-examples that consume grails-micronaut-bom +// +// Used by .github/workflows/groovy-joint-workflow.yml so the joint Groovy 4 snapshot +// build does not try to compile Spock specs against a Groovy-5-only Spock artifact. +// Consumers that reference :grails-micronaut-bom (e.g. grails-doc:generateBomDocumentation) +// must guard those references with findProject(':grails-micronaut-bom') != null. +// See https://github.com/apache/grails-core/issues/15613. +def skipMicronautProjects = providers.gradleProperty('skipMicronautProjects').isPresent() + include( 'grails-bootstrap', 'grails-cache', @@ -119,7 +133,6 @@ include( 'grails-base-bom', 'grails-bom', // the default bom 'grails-hibernate5-bom', - 'grails-micronaut-bom', // Docs 'grails-doc', @@ -192,15 +205,11 @@ include( // wrapper 'grails-wrapper', - - // micronaut plugin for integration with Grails - 'grails-micronaut', ) project(':grails-bom').projectDir = file('grails-bom/default') project(':grails-base-bom').projectDir = file('grails-bom/base') project(':grails-hibernate5-bom').projectDir = file('grails-bom/hibernate5') -project(':grails-micronaut-bom').projectDir = file('grails-bom/micronaut') // Instead of using : to separate nested projects, fix the project directories by setting the projectDir explicitly // (Preserve the parent directory name as part of the nested project name) @@ -400,20 +409,15 @@ include( // TODO: 'grails-test-examples-gsp-spring-boot', 'grails-test-examples-hyphenated', 'grails-test-examples-issue-11102', - 'grails-test-examples-issue-11767', 'grails-test-examples-issue-15228', 'grails-test-examples-issue-698-domain-save-npe', 'grails-test-examples-issue-views-182', - 'grails-test-examples-micronaut', - 'grails-test-examples-micronaut-groovy-only', 'grails-test-examples-namespaces', 'grails-test-examples-plugins-exploded', - 'grails-test-examples-plugins-issue-11767', 'grails-test-examples-plugins-issue11005', 'grails-test-examples-plugins-loadafter', 'grails-test-examples-plugins-loadfirst', 'grails-test-examples-plugins-loadsecond', - 'grails-test-examples-plugins-micronaut-singleton', 'grails-test-examples-config-report', 'grails-test-examples-scaffolding', 'grails-test-examples-scaffolding-fields', @@ -444,17 +448,12 @@ project(':grails-test-examples-hyphenated').projectDir = file('grails-test-examp project(':grails-test-examples-issue-views-182').projectDir = file('grails-test-examples/issue-views-182') project(':grails-test-examples-issue-11102').projectDir = file('grails-test-examples/issue-11102') project(':grails-test-examples-demo33').projectDir = file('grails-test-examples/demo33') -project(':grails-test-examples-micronaut').projectDir = file('grails-test-examples/micronaut') -project(':grails-test-examples-micronaut-groovy-only').projectDir = file('grails-test-examples/micronaut-groovy-only') project(':grails-test-examples-plugins-loadfirst').projectDir = file('grails-test-examples/plugins/loadfirst') project(':grails-test-examples-plugins-loadsecond').projectDir = file('grails-test-examples/plugins/loadsecond') project(':grails-test-examples-plugins-loadafter').projectDir = file('grails-test-examples/plugins/loadafter') project(':grails-test-examples-plugins-issue11005').projectDir = file('grails-test-examples/plugins/issue11005') -project(':grails-test-examples-issue-11767').projectDir = file('grails-test-examples/issue-11767') project(':grails-test-examples-issue-15228').projectDir = file('grails-test-examples/issue-15228') project(':grails-test-examples-plugins-exploded').projectDir = file('grails-test-examples/plugins/exploded') -project(':grails-test-examples-plugins-issue-11767').projectDir = file('grails-test-examples/plugins/issue-11767') -project(':grails-test-examples-plugins-micronaut-singleton').projectDir = file('grails-test-examples/plugins/micronaut-singleton') project(':grails-test-examples-cache').projectDir = file('grails-test-examples/cache') project(':grails-test-examples-config-report').projectDir = file('grails-test-examples/config-report') project(':grails-test-examples-scaffolding').projectDir = file('grails-test-examples/scaffolding') @@ -470,6 +469,30 @@ includeBuild('./build-logic') { name = 'build-logic-root' } +// Grails-Micronaut "island" - kept on its own dependency graph because the Micronaut +// platform pins Groovy 5 / Spock 2.4-groovy-5.0, which is incompatible with the +// Groovy 4 / Spock 2.3-groovy-4.0 stack the rest of grails-core targets. Skipping +// these projects (via -PskipMicronautProjects) keeps Micronaut's version overrides +// from leaking into builds where Groovy 4 is in play (notably the joint validation +// build that swaps in an apache/groovy 4.x snapshot). +if (!skipMicronautProjects) { + include( + 'grails-micronaut-bom', + 'grails-micronaut', + 'grails-test-examples-issue-11767', + 'grails-test-examples-micronaut', + 'grails-test-examples-micronaut-groovy-only', + 'grails-test-examples-plugins-issue-11767', + 'grails-test-examples-plugins-micronaut-singleton', + ) + project(':grails-micronaut-bom').projectDir = file('grails-bom/micronaut') + project(':grails-test-examples-issue-11767').projectDir = file('grails-test-examples/issue-11767') + project(':grails-test-examples-micronaut').projectDir = file('grails-test-examples/micronaut') + project(':grails-test-examples-micronaut-groovy-only').projectDir = file('grails-test-examples/micronaut-groovy-only') + project(':grails-test-examples-plugins-issue-11767').projectDir = file('grails-test-examples/plugins/issue-11767') + project(':grails-test-examples-plugins-micronaut-singleton').projectDir = file('grails-test-examples/plugins/micronaut-singleton') +} + // Due to https://github.com/gradle/gradle/issues/2986 , we can't change the global exclude for profiles, it must be done everywhere for (String pattern in DirectoryScanner.defaultExcludes) { if (pattern.contains('gitignore') || pattern.contains('gitattributes')) {