You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by ja...@apache.org on 2026/06/02 11:03:41 UTC

(grails-core) branch grails8-groovy5-sb4 updated (ae5fa9765f -> 95b8e7fcd1)

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

jamesfredley pushed a change to branch grails8-groovy5-sb4
in repository https://gitbox.apache.org/repos/asf/grails-core.git


    from ae5fa9765f Minimize the Groovy 5 diff to bare essentials
     new b61bd6a9ed Remove changes unrelated to Groovy 5
     new e24e503e15 Correct boot4-disabled integration test blocker comment
     new df3fd3e22a Fix Groovy 5 VariableScopeVisitor NPE on null method exceptions array
     new 95b8e7fcd1 Bump javaparser-core to 3.28.2 for Groovy 5

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .gitignore                                         |  2 -
 dependencies.gradle                                |  2 +-
 .../boot4-disabled-integration-test-config.gradle  | 13 ++++---
 .../test/groovy/grails/async/PromiseSpec.groovy    |  2 +-
 .../groovy/grails/async/GparsPromiseSpec.groovy    |  2 +-
 .../async/factory/rxjava/RxJavaPromiseSpec.groovy  |  1 -
 .../grails/build/logging/GrailsConsoleSpec.groovy  |  4 +-
 .../grails/compiler/injection/GrailsASTUtils.java  | 30 +++++++++------
 .../AbstractMethodDecoratingTransformation.groovy  | 11 +-----
 .../datastore/mapping/reflect/AstUtils.groovy      | 30 +++++++++------
 grails-forge/settings.gradle                       | 41 ---------------------
 .../features/scaffolding/ScaffoldingSpec.groovy    | 13 +++----
 settings.gradle                                    | 43 ----------------------
 13 files changed, 55 insertions(+), 139 deletions(-)


(grails-core) 04/04: Bump javaparser-core to 3.28.2 for Groovy 5

Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jamesfredley pushed a commit to branch grails8-groovy5-sb4
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 95b8e7fcd1a0ac9c21bbb95f3f3bbedca8b6aa3e
Author: James Fredley <ja...@users.noreply.github.com>
AuthorDate: Tue Jun 2 07:00:48 2026 -0400

    Bump javaparser-core to 3.28.2 for Groovy 5
    
    Groovy 5's org.apache.groovy:groovy-groovydoc:5.0.7-SNAPSHOT transitively
    requires com.github.javaparser:javaparser-core:3.28.2, but the BOM pinned
    3.28.1, so validateDependencyVersions failed on every module applying the docs
    config. Align the BOM with what Groovy 5 ships.
    
    Assisted-by: claude-code:claude-4.8-opus
---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index 1e5232850c..465ad63950 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -33,7 +33,7 @@ ext {
             'gradle-spock.version'          : '2.3-groovy-4.0',
             'grails-publish-plugin.version' : '1.0.0-M1',
             'jansi.version'                 : '2.4.2',
-            'javaparser-core.version'       : '3.28.1',
+            'javaparser-core.version'       : '3.28.2',
             'jline.version'                 : '3.30.6',
             'jline2.version'                : '2.14.6',
             'jna.version'                   : '5.18.1',


(grails-core) 03/04: Fix Groovy 5 VariableScopeVisitor NPE on null method exceptions array

Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jamesfredley pushed a commit to branch grails8-groovy5-sb4
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit df3fd3e22a46efc5f2295640ee280903390512be
Author: James Fredley <ja...@users.noreply.github.com>
AuthorDate: Tue Jun 2 06:59:32 2026 -0400

    Fix Groovy 5 VariableScopeVisitor NPE on null method exceptions array
    
    Groovy 5's VariableScopeVisitor.visitConstructorOrMethod reads the method's
    exceptions array without a null check. Grails AST transforms routinely create
    methods via ClassNode.addMethod(..., null, ...), leaving a null exceptions
    array, which NPE'd during the canonicalization phase (e.g. the generated
    getTransactionManager() while compiling :grails-datamapping-tck).
    
    This was previously masked by three try/catch NullPointerException guards (in
    GrailsASTUtils.processVariableScopes, AstUtils.processVariableScopes, and
    AbstractMethodDecoratingTransformation) that swallowed the NPE and left the
    method's variable scope uncomputed.
    
    MethodNode.exceptions is final with no setter, so normalize at the
    processVariableScopes chokepoints: when the exceptions array is null, run the
    VariableScopeVisitor against a proxy MethodNode that shares the same parameters
    and code but carries an empty exceptions array, then copy the recomputed
    variable scope back onto the real method. The third site now routes its
    dummy-SourceUnit visit through processVariableScopes as well.
    
    All three NPE-swallow guards are removed; :grails-datamapping-tck:compileGroovy
    compiles cleanly.
    
    Assisted-by: claude-code:claude-4.8-opus
---
 .../grails/compiler/injection/GrailsASTUtils.java  | 30 +++++++++++++---------
 .../AbstractMethodDecoratingTransformation.groovy  | 11 ++------
 .../datastore/mapping/reflect/AstUtils.groovy      | 30 +++++++++++++---------
 3 files changed, 38 insertions(+), 33 deletions(-)

diff --git a/grails-core/src/main/groovy/org/grails/compiler/injection/GrailsASTUtils.java b/grails-core/src/main/groovy/org/grails/compiler/injection/GrailsASTUtils.java
index 58a7e72abd..80f6aaa615 100644
--- a/grails-core/src/main/groovy/org/grails/compiler/injection/GrailsASTUtils.java
+++ b/grails-core/src/main/groovy/org/grails/compiler/injection/GrailsASTUtils.java
@@ -1507,18 +1507,24 @@ public class GrailsASTUtils {
     }
 
     public static void processVariableScopes(SourceUnit source, ClassNode classNode, MethodNode methodNode) {
-        // Groovy 5 VariableScopeVisitor NPE on certain Grails AST transformation outputs.
-        // See org.grails.datastore.mapping.reflect.AstUtils.processVariableScopes for context.
-        try {
-            VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(source);
-            if (methodNode == null) {
-                scopeVisitor.visitClass(classNode);
-            } else {
-                scopeVisitor.prepareVisit(classNode);
-                scopeVisitor.visitMethod(methodNode);
-            }
-        } catch (NullPointerException ignored) {
-            // Groovy 5 VariableScopeVisitor NPE - upstream bug
+        VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(source);
+        if (methodNode == null) {
+            scopeVisitor.visitClass(classNode);
+            return;
+        }
+        scopeVisitor.prepareVisit(classNode);
+        if (methodNode.getExceptions() == null) {
+            // Groovy 5's VariableScopeVisitor reads the method's exceptions array without a null check, and AST
+            // transforms routinely create methods via ClassNode.addMethod(..., null, ...). MethodNode.exceptions is
+            // final, so recompute scopes on a proxy that shares the same parameters and code but carries an empty
+            // exceptions array, then copy the computed scope back onto the real method.
+            MethodNode proxy = new MethodNode(methodNode.getName(), methodNode.getModifiers(), methodNode.getReturnType(),
+                    methodNode.getParameters(), ClassNode.EMPTY_ARRAY, methodNode.getCode());
+            proxy.setDeclaringClass(methodNode.getDeclaringClass());
+            scopeVisitor.visitMethod(proxy);
+            methodNode.setVariableScope(proxy.getVariableScope());
+        } else {
+            scopeVisitor.visitMethod(methodNode);
         }
     }
 
diff --git a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/transform/AbstractMethodDecoratingTransformation.groovy b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/transform/AbstractMethodDecoratingTransformation.groovy
index ff2f393709..f7d2a9dcf2 100644
--- a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/transform/AbstractMethodDecoratingTransformation.groovy
+++ b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/transform/AbstractMethodDecoratingTransformation.groovy
@@ -36,7 +36,6 @@ import org.codehaus.groovy.ast.expr.MethodCallExpression
 import org.codehaus.groovy.ast.stmt.BlockStatement
 import org.codehaus.groovy.ast.stmt.Statement
 import org.codehaus.groovy.ast.tools.GenericsUtils
-import org.codehaus.groovy.classgen.VariableScopeVisitor
 import org.codehaus.groovy.control.ErrorCollector
 import org.codehaus.groovy.control.SourceUnit
 import org.codehaus.groovy.transform.sc.StaticCompileTransformation
@@ -356,14 +355,8 @@ abstract class AbstractMethodDecoratingTransformation extends AbstractGormASTTra
         classNode.addMethod(renamedMethodNode)
 
         // Use a dummy source unit to process the variable scopes to avoid the issue where this is run twice producing an error.
-        // Groovy 5 VariableScopeVisitor NPE: see AstUtils.processVariableScopes for the upstream bug.
-        try {
-            VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(new SourceUnit('dummy', 'dummy', source.getConfiguration(), source.getClassLoader(), new ErrorCollector(source.getConfiguration())))
-            scopeVisitor.prepareVisit(classNode)
-            scopeVisitor.visitMethod(renamedMethodNode)
-        } catch (NullPointerException ignored) {
-            // Groovy 5 VariableScopeVisitor NPE - upstream bug
-        }
+        SourceUnit dummySource = new SourceUnit('dummy', 'dummy', source.getConfiguration(), source.getClassLoader(), new ErrorCollector(source.getConfiguration()))
+        processVariableScopes(dummySource, classNode, renamedMethodNode)
 
         return renamedMethodNode
     }
diff --git a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/reflect/AstUtils.groovy b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/reflect/AstUtils.groovy
index 226be88ee2..9514f4fbec 100644
--- a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/reflect/AstUtils.groovy
+++ b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/reflect/AstUtils.groovy
@@ -245,18 +245,24 @@ class AstUtils {
     }
 
     static void processVariableScopes(SourceUnit source, ClassNode classNode, MethodNode methodNode) {
-        // Groovy 5 canonicalization throws NPE from VariableScopeVisitor (e.g. on DataServiceRoutingProductDataService).
-        // Swallow it - the transform has already produced valid bytecode.
-        try {
-            VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(source)
-            if (methodNode == null) {
-                scopeVisitor.visitClass(classNode)
-            } else {
-                scopeVisitor.prepareVisit(classNode)
-                scopeVisitor.visitMethod(methodNode)
-            }
-        } catch (NullPointerException ignored) {
-            // Groovy 5 VariableScopeVisitor NPE - upstream bug, see comment above
+        VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(source)
+        if (methodNode == null) {
+            scopeVisitor.visitClass(classNode)
+            return
+        }
+        scopeVisitor.prepareVisit(classNode)
+        if (methodNode.exceptions == null) {
+            // Groovy 5's VariableScopeVisitor reads the method's exceptions array without a null check, and AST
+            // transforms routinely create methods via ClassNode.addMethod(..., null, ...). MethodNode.exceptions is
+            // final, so recompute scopes on a proxy that shares the same parameters and code but carries an empty
+            // exceptions array, then copy the computed scope back onto the real method.
+            MethodNode proxy = new MethodNode(methodNode.name, methodNode.modifiers, methodNode.returnType,
+                    methodNode.parameters, ClassNode.EMPTY_ARRAY, methodNode.code)
+            proxy.declaringClass = methodNode.declaringClass
+            scopeVisitor.visitMethod(proxy)
+            methodNode.variableScope = proxy.variableScope
+        } else {
+            scopeVisitor.visitMethod(methodNode)
         }
     }
 


(grails-core) 02/04: Correct boot4-disabled integration test blocker comment

Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jamesfredley pushed a commit to branch grails8-groovy5-sb4
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit e24e503e153fdccecdae7187112f922766585cdf
Author: James Fredley <ja...@users.noreply.github.com>
AuthorDate: Mon Jun 1 22:55:35 2026 -0400

    Correct boot4-disabled integration test blocker comment
    
    The comment claimed two blockers, but the SiteMesh3/Spring 7 one is bogus:
    SiteMesh3 in these grails-test-examples apps is env-gated behind
    SITEMESH3_TESTING_ENABLED on both 8.0.x and this branch, and the base runs
    these same integrationTests fine on Spring 7. The sole real blocker is the
    Groovy 5 indy=false controller-action param-scope bug, which re-running
    VariableScopeVisitor does not fix (needs an upstream Groovy change). Document
    that accurately and link the standalone reproducer.
    
    Assisted-by: claude-code:claude-4.8-opus
---
 gradle/boot4-disabled-integration-test-config.gradle | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/gradle/boot4-disabled-integration-test-config.gradle b/gradle/boot4-disabled-integration-test-config.gradle
index abc8d7844a..b3cc09a20e 100644
--- a/gradle/boot4-disabled-integration-test-config.gradle
+++ b/gradle/boot4-disabled-integration-test-config.gradle
@@ -15,12 +15,13 @@
  *  limitations under the License.
  */
 
-// integrationTest is disabled for modules applying this file. Two blockers:
-//   - SiteMesh3 decorator/layout is not compatible with Spring Framework 7.
-//   - Groovy 5 + indy=false: controller action parameters resolve to propertyMissing
-//     on the controller instead of the local, throwing MissingPropertyException after
-//     ControllerActionTransformer wraps the method body. Functional tests (indy=true) pass.
-// Re-enable per module once its blocker is resolved.
+// integrationTest is disabled for modules applying this file.
+// Blocker: Groovy 5 + indy=false drops the scope of controller action parameters/locals
+// after ControllerActionTransformer re-parents the body inside a TryCatchStatement, so they
+// resolve to propertyMissing on the controller and throw MissingPropertyException. Re-running
+// VariableScopeVisitor does not rebind them; this needs an upstream Groovy fix. indy=true passes.
+// Reproducer: https://github.com/jamesfredley/groovy5-controller-action-param-scope-bug
+// Re-enable once the upstream fix lands.
 
 tasks.named('integrationTest') {
     enabled = false


(grails-core) 01/04: Remove changes unrelated to Groovy 5

Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jamesfredley pushed a commit to branch grails8-groovy5-sb4
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit b61bd6a9edb5f09f672005473d4c890f8e70aa73
Author: James Fredley <ja...@users.noreply.github.com>
AuthorDate: Mon Jun 1 22:29:55 2026 -0400

    Remove changes unrelated to Groovy 5
    
    Further stripping non-Groovy-5 changes so the PR is scoped to Groovy 5 only:
    
    - settings.gradle and grails-forge/settings.gradle: removed redundant
      dependencyResolutionManagement blocks. The base already applies
      GrailsRepoSettingsPlugin (id 'org.apache.grails.buildsrc.repo'), which
      declares FAIL_ON_PROJECT_REPOS and the org.apache.groovy.*-SNAPSHOT repo;
      ./gradlew help confirms Groovy 5 still resolves without these blocks.
    - grails-forge ScaffoldingSpec: reverted - it was a Spring Boot 4 output
      forwarding test tweak (per its own comment), not Groovy 5.
    - .gitignore: removed a stray .sisyphus/ entry (tooling).
    - async PromiseSpec/GparsPromiseSpec/RxJavaPromiseSpec: reverted
      whitespace-only changes.
    - GrailsConsoleSpec: reverted a single-line @IgnoreIf reformatting.
    
    Assisted-by: claude-code:claude-4.8-opus
---
 .gitignore                                         |  2 -
 .../test/groovy/grails/async/PromiseSpec.groovy    |  2 +-
 .../groovy/grails/async/GparsPromiseSpec.groovy    |  2 +-
 .../async/factory/rxjava/RxJavaPromiseSpec.groovy  |  1 -
 .../grails/build/logging/GrailsConsoleSpec.groovy  |  4 +-
 grails-forge/settings.gradle                       | 41 ---------------------
 .../features/scaffolding/ScaffoldingSpec.groovy    | 13 +++----
 settings.gradle                                    | 43 ----------------------
 8 files changed, 9 insertions(+), 99 deletions(-)

diff --git a/.gitignore b/.gitignore
index 4e063ed278..ca770d0c55 100644
--- a/.gitignore
+++ b/.gitignore
@@ -63,5 +63,3 @@ tmp/
 !etc/bin
 etc/bin/results
 .vscode/
-
-.sisyphus/
diff --git a/grails-async/core/src/test/groovy/grails/async/PromiseSpec.groovy b/grails-async/core/src/test/groovy/grails/async/PromiseSpec.groovy
index 665b1d016f..4426722aea 100644
--- a/grails-async/core/src/test/groovy/grails/async/PromiseSpec.groovy
+++ b/grails-async/core/src/test/groovy/grails/async/PromiseSpec.groovy
@@ -42,7 +42,7 @@ class PromiseSpec extends Specification {
             result == '*10*'
 
     }
-
+    
     void 'Test promise timeout handling'() {
         
         when: 'a promise that takes a while is created'
diff --git a/grails-async/gpars/src/test/groovy/grails/async/GparsPromiseSpec.groovy b/grails-async/gpars/src/test/groovy/grails/async/GparsPromiseSpec.groovy
index 9780ca6308..7462d5c5ce 100644
--- a/grails-async/gpars/src/test/groovy/grails/async/GparsPromiseSpec.groovy
+++ b/grails-async/gpars/src/test/groovy/grails/async/GparsPromiseSpec.groovy
@@ -47,7 +47,7 @@ class GparsPromiseSpec extends Specification {
         then: 'the result is decorated'
             result == '*10*'
     }
-
+    
     void 'Test promise timeout handling'() {
         
         when: 'a promise that takes longer than the timeout'
diff --git a/grails-async/rxjava/src/test/groovy/org/grails/async/factory/rxjava/RxJavaPromiseSpec.groovy b/grails-async/rxjava/src/test/groovy/org/grails/async/factory/rxjava/RxJavaPromiseSpec.groovy
index 828bc1f660..7108caafa8 100644
--- a/grails-async/rxjava/src/test/groovy/org/grails/async/factory/rxjava/RxJavaPromiseSpec.groovy
+++ b/grails-async/rxjava/src/test/groovy/org/grails/async/factory/rxjava/RxJavaPromiseSpec.groovy
@@ -47,7 +47,6 @@ class RxJavaPromiseSpec extends Specification {
             result == '*10*'
 
     }
-
     void 'Test promise timeout handling'() {
 
         when: 'a promise that takes a while is created'
diff --git a/grails-bootstrap/src/test/groovy/grails/build/logging/GrailsConsoleSpec.groovy b/grails-bootstrap/src/test/groovy/grails/build/logging/GrailsConsoleSpec.groovy
index 25681aa5a9..4f99dd9650 100644
--- a/grails-bootstrap/src/test/groovy/grails/build/logging/GrailsConsoleSpec.groovy
+++ b/grails-bootstrap/src/test/groovy/grails/build/logging/GrailsConsoleSpec.groovy
@@ -43,9 +43,7 @@ import java.util.regex.Pattern
  * @author Tom Bujok
  * @since 2.3
  */
-@IgnoreIf({
-    !GrailsConsole.instance.isAnsiEnabled()
-})
+@IgnoreIf({ !GrailsConsole.instance.isAnsiEnabled() })
 class GrailsConsoleSpec extends Specification {
 
     static final String RESET = Pattern.quote(Ansi.ansi().reset().toString())
diff --git a/grails-forge/settings.gradle b/grails-forge/settings.gradle
index 5f040f0bbc..3c3a65805d 100644
--- a/grails-forge/settings.gradle
+++ b/grails-forge/settings.gradle
@@ -78,44 +78,3 @@ includeBuild('..') {
 includeBuild('../grails-gradle') {
     name = 'grails-gradle'
 }
-
-dependencyResolutionManagement {
-    repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
-    repositories {
-        // mavenLocal() // Keep, this will be uncommented and used by CI (groovy-joint-workflow)
-        mavenCentral()
-        maven { url = 'https://repo.grails.org/grails/restricted' }
-        maven {
-            url = 'https://repository.apache.org/content/groups/snapshots'
-            content {
-                includeVersionByRegex('org[.]apache[.]grails.*', '.*', '.*-SNAPSHOT')
-                includeVersionByRegex('org[.]apache[.]groovy.*', '.*', '.*-SNAPSHOT')
-            }
-        }
-        maven {
-            url = 'https://repository.apache.org/content/groups/staging'
-            content {
-                includeGroupByRegex('org[.]apache[.]grails.*')
-            }
-        }
-        maven {
-            url = 'https://central.sonatype.com/repository/maven-snapshots'
-            content {
-                includeVersionByRegex('cloud[.]wondrify.*', '.*', '.*-SNAPSHOT')
-                includeGroup('org.spockframework')
-            }
-            mavenContent {
-                snapshotsOnly()
-            }
-        }
-        maven {
-            url = 'https://repository.apache.org/content/groups/staging'
-            content {
-                includeModuleByRegex('org[.]apache[.]grails[.]gradle', 'grails-publish')
-            }
-            mavenContent {
-                releasesOnly()
-            }
-        }
-    }
-}
diff --git a/grails-forge/test-core/src/test/groovy/org/grails/forge/features/scaffolding/ScaffoldingSpec.groovy b/grails-forge/test-core/src/test/groovy/org/grails/forge/features/scaffolding/ScaffoldingSpec.groovy
index 0664a1de63..302fca5ff4 100644
--- a/grails-forge/test-core/src/test/groovy/org/grails/forge/features/scaffolding/ScaffoldingSpec.groovy
+++ b/grails-forge/test-core/src/test/groovy/org/grails/forge/features/scaffolding/ScaffoldingSpec.groovy
@@ -40,16 +40,15 @@ class ScaffoldingSpec extends CommandSpec {
         '''
         String output = executeGradle('runCommand', '-Pargs=generate-controller example.grails.Bird').output
 
-        then: 'generated files exist'
+        then:
+        output ==~ /(?s).*Rendered template Controller\.groovy to destination grails-app[\/\\]controllers[\/\\]example[\/\\]grails[\/\\]BirdController\.groovy(?s).*/
+        output ==~ /(?s).*Rendered template Service\.groovy to destination grails-app[\/\\]services[\/\\]example[\/\\]grails[\/\\]BirdService\.groovy(?s).*/
+        output ==~ /(?s).*Rendered template Spec\.groovy to destination src[\/\\]test[\/\\]groovy[\/\\]example[\/\\]grails[\/\\]BirdControllerSpec\.groovy(?s).*/
+        output ==~ /(?s).*Rendered template ServiceSpec\.groovy to destination src[\/\\]test[\/\\]groovy[\/\\]example[\/\\]grails[\/\\]BirdServiceSpec\.groovy(?s).*/
         new File(dir, 'grails-app/controllers/example/grails/BirdController.groovy').exists()
         new File(dir, 'grails-app/services/example/grails/BirdService.groovy').exists()
         new File(dir, 'src/test/groovy/example/grails/BirdControllerSpec.groovy').exists()
-        new File(dir, 'src/test/groovy/example/grails/BirdServiceSpec.groovy').exists()
-
-        and: 'output contains rendering messages or task completed successfully'
-        // Spring Boot 4 may not forward forked JVM println output to Gradle's captured output.
-        // Verify via output pattern when available, but file existence above is the primary assertion.
-        output.contains('runCommand') || output.contains('Rendered template')
+        new File(dir, 'src/test/groovy/example/grails/BirdControllerSpec.groovy').exists()
     }
 
     @Override
diff --git a/settings.gradle b/settings.gradle
index 7f6115578f..cf9363143c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -536,46 +536,3 @@ for (String pattern in DirectoryScanner.defaultExcludes) {
         DirectoryScanner.removeDefaultExclude(pattern)
     }
 }
-
-dependencyResolutionManagement {
-    repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
-    repositories {
-        // mavenLocal() // Keep, this will be uncommented and used by CI (groovy-joint-workflow)
-        maven { url = 'https://repo.grails.org/grails/restricted' }
-        maven {
-            url = 'https://repository.apache.org/content/groups/snapshots'
-            content {
-                includeVersionByRegex('org[.]apache[.]grails.*', '.*', '.*-SNAPSHOT')
-                includeVersionByRegex('org[.]apache[.]groovy.*', '.*', '.*-SNAPSHOT')
-            }
-        }
-        maven {
-            url = 'https://central.sonatype.com/repository/maven-snapshots'
-            content {
-                includeVersionByRegex('cloud[.]wondrify.*', '.*', '.*-SNAPSHOT')
-            }
-            mavenContent {
-                snapshotsOnly()
-            }
-        }
-        maven {
-            url = 'https://central.sonatype.com/repository/maven-snapshots/'
-            content {
-                includeGroup('org.spockframework')
-            }
-            mavenContent {
-                snapshotsOnly()
-            }
-        }
-        maven {
-            url = 'https://repository.apache.org/content/groups/staging'
-            content {
-                includeModuleByRegex('org[.]apache[.]grails[.]gradle', 'grails-publish')
-                includeModuleByRegex('org[.]apache[.]groovy[.]geb', 'geb-.*')
-            }
-            mavenContent {
-                releasesOnly()
-            }
-        }
-    }
-}