From 1a6496d55cd2f7e4ba35063953535c33602c4b91 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Mon, 1 Jun 2026 14:33:40 -0700 Subject: [PATCH 1/5] Update SiteMesh 3 to 3.3.0-SNAPSHOT for Spring Boot 4 SiteMesh 3.2.x targets Spring Boot 3, while the 8.x line runs on Spring Boot 4. Bump starter-sitemesh and spring-webmvc-sitemesh from the 3.2.2 release to 3.3.0-SNAPSHOT, the Boot 4-compatible line. The org.sitemesh snapshot resolution exemptions are already present in GrailsRepoSettingsPlugin (pluginManagement and dependencyResolution management), so the snapshot resolves without further repo changes. --- dependencies.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 85a89a6f0f5..a3fdc6dd62b 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -93,8 +93,8 @@ ext { 'selenium.version' : '4.38.0', 'sitemesh.version' : '2.6.0', 'spock.version' : '2.3-groovy-4.0', - 'starter-sitemesh.version' : '3.2.2', - 'spring-webmvc-sitemesh.version': '3.2.2', + 'starter-sitemesh.version' : '3.3.0-SNAPSHOT', + 'spring-webmvc-sitemesh.version': '3.3.0-SNAPSHOT', // Spring Boot 4 no longer manages spring-retry; pin it here so the // grails-shell-cli SpringRetryCompilerAutoConfiguration's unversioned // reference resolves and consumer apps using @Retryable get a known version. From 76143a78e1d7fbb382f0a54b0dc9018783b3d87b Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Mon, 1 Jun 2026 14:51:56 -0700 Subject: [PATCH 2/5] Enable SiteMesh 3 on the 8.x (Spring Boot 4) line The grails-sitemesh3 and grails-gsp-spring-boot modules were disabled while SiteMesh 3.2.x lacked Spring Boot 4 support. With org.sitemesh 3.3.0-SNAPSHOT both modules compile and pass their tests against Boot 4, so re-enable them and make SiteMesh 3 the default GSP layout decorator for generated applications. - settings.gradle: include grails-sitemesh3 and grails-gsp-spring-boot (project + projectDir) - grails-sitemesh3: re-enable the publish plugin and restore it to the published projects list so its version is managed by grails-bom - grails-forge (GrailsGsp) and grails-profiles web profile: generate apps against grails-sitemesh3 instead of grails-layout --- gradle/publish-root-config.gradle | 2 +- grails-gsp/grails-sitemesh3/build.gradle | 2 +- grails-profiles/web/profile.yml | 2 +- settings.gradle | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/gradle/publish-root-config.gradle b/gradle/publish-root-config.gradle index 6757868df34..98d5b245ac3 100644 --- a/gradle/publish-root-config.gradle +++ b/gradle/publish-root-config.gradle @@ -72,7 +72,7 @@ def publishedProjects = [ 'grails-scaffolding', 'grails-services', 'grails-shell-cli', - // TODO: 'grails-sitemesh3', // Sitemesh3 has not been updated to support boot 4 + 'grails-sitemesh3', 'grails-spring', 'grails-taglib', 'grails-test-core', diff --git a/grails-gsp/grails-sitemesh3/build.gradle b/grails-gsp/grails-sitemesh3/build.gradle index 676039b0355..5b600e9f5d0 100644 --- a/grails-gsp/grails-sitemesh3/build.gradle +++ b/grails-gsp/grails-sitemesh3/build.gradle @@ -23,7 +23,7 @@ plugins { id 'org.apache.grails.buildsrc.dependency-validator' id 'org.apache.grails.gradle.grails-plugin' id 'org.apache.grails.buildsrc.compile' - // TODO: id 'org.apache.grails.buildsrc.publish' + id 'org.apache.grails.buildsrc.publish' id 'org.apache.grails.buildsrc.sbom' id 'org.apache.grails.gradle.grails-code-style' } diff --git a/grails-profiles/web/profile.yml b/grails-profiles/web/profile.yml index bc8d2f0d979..f02234e38f6 100644 --- a/grails-profiles/web/profile.yml +++ b/grails-profiles/web/profile.yml @@ -44,7 +44,7 @@ dependencies: - scope: implementation coords: "org.apache.grails:grails-url-mappings" - scope: implementation - coords: "org.apache.grails:grails-layout" + coords: "org.apache.grails:grails-sitemesh3" - scope: implementation coords: "org.apache.grails:grails-interceptors" - scope: implementation diff --git a/settings.gradle b/settings.gradle index cf9363143c2..9414e2c1c43 100644 --- a/settings.gradle +++ b/settings.gradle @@ -164,14 +164,14 @@ include( // gsp 'grails-gsp-core', 'grails-gsp', - // TODO: 'grails-sitemesh3', // sitemesh3 does not support boot 4 at this time + 'grails-sitemesh3', 'grails-layout', 'grails-taglib', 'grails-web-gsp', 'grails-web-gsp-taglib', 'grails-web-jsp', 'grails-web-taglib', - // TODO: 'grails-gsp-spring-boot', // sitemesh3 does not support boot4 at this time + 'grails-gsp-spring-boot', 'grails-datastore-core', 'grails-datastore-web', @@ -235,14 +235,14 @@ project(':grails-dependencies-assets').projectDir = file('grails-dependencies/as project(':grails-gsp-core').projectDir = file('grails-gsp/core') project(':grails-gsp').projectDir = file('grails-gsp/plugin') -// TODO: project(':grails-sitemesh3').projectDir = file('grails-gsp/grails-sitemesh3') +project(':grails-sitemesh3').projectDir = file('grails-gsp/grails-sitemesh3') project(':grails-layout').projectDir = file('grails-gsp/grails-layout') project(':grails-taglib').projectDir = file('grails-gsp/grails-taglib') project(':grails-web-gsp').projectDir = file('grails-gsp/grails-web-gsp') project(':grails-web-gsp-taglib').projectDir = file('grails-gsp/grails-web-gsp-taglib') project(':grails-web-jsp').projectDir = file('grails-gsp/grails-web-jsp') project(':grails-web-taglib').projectDir = file('grails-gsp/grails-web-taglib') -// TODO: project(':grails-gsp-spring-boot').projectDir = file('grails-gsp/spring-boot') +project(':grails-gsp-spring-boot').projectDir = file('grails-gsp/spring-boot') // Data Mapping - Documentation include 'grails-data-docs-guide-developer' From cbaba897707ddb4d0b73e1e16b04f29533b327b1 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Mon, 1 Jun 2026 15:53:25 -0700 Subject: [PATCH 3/5] Resolve org.sitemesh snapshots in generated applications Generated apps now depend on grails-sitemesh3, which transitively pulls org.sitemesh:*:3.3.0-SNAPSHOT. SNAPSHOT builds of Grails already add the Apache snapshot repository for org.apache.grails/groovy; mirror that for org.sitemesh by adding the Sonatype maven-snapshots repository (filtered to org.sitemesh, snapshotsOnly) when the Grails version is a SNAPSHOT. Applied to both generators: - grails-forge: GradleRepository.getDefaultRepositories - grails-shell-cli: CreateAppCommand.createRepositoryList The repository is only added for SNAPSHOT builds, so released apps are unaffected; a released org.sitemesh:3.3.0 must be pinned before 8.x GA. --- .../grails/forge/build/gradle/GradleRepository.java | 11 +++++++++++ .../cli/profile/commands/CreateAppCommand.groovy | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/gradle/GradleRepository.java b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/gradle/GradleRepository.java index 06ab59206b5..6b5a2538198 100644 --- a/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/gradle/GradleRepository.java +++ b/grails-forge/grails-forge-core/src/main/java/org/grails/forge/build/gradle/GradleRepository.java @@ -66,6 +66,17 @@ static Set getDefaultRepositories(String grailsVersion) { ), List.of(VersionType.SNAPSHOT) )); + repositories.add(new DefaultGradleRepository( + repositories.size(), + "https://central.sonatype.com/repository/maven-snapshots", + null, + List.of( + new VersionRegexRepoFilter( + "org[.]sitemesh.*", ".*", ".*-SNAPSHOT" + ) + ), + List.of(VersionType.SNAPSHOT) + )); repositories.add(new DefaultGradleRepository( repositories.size(), "https://repository.apache.org/content/groups/staging", diff --git a/grails-shell-cli/src/main/groovy/org/grails/cli/profile/commands/CreateAppCommand.groovy b/grails-shell-cli/src/main/groovy/org/grails/cli/profile/commands/CreateAppCommand.groovy index be05ee4254c..61a22347a21 100644 --- a/grails-shell-cli/src/main/groovy/org/grails/cli/profile/commands/CreateAppCommand.groovy +++ b/grails-shell-cli/src/main/groovy/org/grails/cli/profile/commands/CreateAppCommand.groovy @@ -517,6 +517,10 @@ class CreateAppCommand extends ArgumentCompletingCommand implements ProfileRepos GrailsGradleRepository repository = new GrailsGradleRepository(url: 'https://repository.apache.org/content/groups/snapshots', snapshotsOnly: true) repository.includeOnly('org[.]apache[.](grails|groovy).*', '.*', '.*-SNAPSHOT') configuredRepositories.add(repository) + + GrailsGradleRepository sitemeshSnapshots = new GrailsGradleRepository(url: 'https://central.sonatype.com/repository/maven-snapshots', snapshotsOnly: true) + sitemeshSnapshots.includeOnly('org[.]sitemesh.*', '.*', '.*-SNAPSHOT') + configuredRepositories.add(sitemeshSnapshots) } configuredRepositories.unique() } From fd5a874386749d7dff98186b2b821ccdee8e2204 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Tue, 2 Jun 2026 07:32:39 -0700 Subject: [PATCH 4/5] Remove orphaned boot4-disabled-integration-test-config.gradle The only documented blocker for this integration-test opt-out was SiteMesh3's incompatibility with Spring Framework 7, which this PR resolves by enabling SiteMesh 3 (3.3.0-SNAPSHOT) on the 8.0.x line. All modules that applied this file already had their 'apply from' references removed upstream, leaving the config file orphaned. Removing the dead file. --- ...t4-disabled-integration-test-config.gradle | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 gradle/boot4-disabled-integration-test-config.gradle diff --git a/gradle/boot4-disabled-integration-test-config.gradle b/gradle/boot4-disabled-integration-test-config.gradle deleted file mode 100644 index e56dd57a002..00000000000 --- a/gradle/boot4-disabled-integration-test-config.gradle +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -// TODO: BOOT4 - Integration tests disabled due to Spring Boot 4 incompatibilities. -// -// Modules applying this file have their integrationTest task disabled because of -// external plugin/library incompatibilities with Spring Boot 4 / Spring Framework 7. -// -// Known blockers: -// - SiteMesh3: Decorator/layout not compatible with Spring Framework 7 -// -// Re-enable each module's integrationTest when its blocking dependency is updated. -// Search for 'boot4-disabled-integration-test-config' to find all affected modules. - -tasks.named('integrationTest') { - enabled = false -} From c9fa3610c8b50cd9167a3e5eef9f6b02aee90695 Mon Sep 17 00:00:00 2001 From: James Daugherty Date: Wed, 3 Jun 2026 09:45:38 -0400 Subject: [PATCH 5/5] Revert grails-layout removal in default profile --- grails-profiles/web/profile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grails-profiles/web/profile.yml b/grails-profiles/web/profile.yml index f02234e38f6..bc8d2f0d979 100644 --- a/grails-profiles/web/profile.yml +++ b/grails-profiles/web/profile.yml @@ -44,7 +44,7 @@ dependencies: - scope: implementation coords: "org.apache.grails:grails-url-mappings" - scope: implementation - coords: "org.apache.grails:grails-sitemesh3" + coords: "org.apache.grails:grails-layout" - scope: implementation coords: "org.apache.grails:grails-interceptors" - scope: implementation