Invoke groovysh in the gradle project context and inspect objects like project, gradle, settings in Groovy Object Browser GUI

I have developed a simple debugging aid to be able to run groovy shell in the context of a Gradle project. I am looking for any expert advise to improve this:

// A simple init script that adds a groovysh task to the root project.
// The groovysh task is a debug task that allows you to run a groovy shell
// in the context of the project. The groovysh task also adds gradle, settings
// and project objects to the shell context. This allows you to inspect the
// project, gradle and settings objects using the Groovy ObjectBrowser GUI.
// You can also run any groovy code
gradle.projectsLoaded {
    rootProject {
        afterEvaluate { project ->
            if (!project.repositories.any{it.name == 'MavenRepo'}) {
                project.repositories {
                    // To be able to load org.apache.groovy:groovy-groovysh
                    // and its dependencies
                    mavenCentral()
                }
            }

            project.configurations {
                groovyshDependencies
            }

            project.dependencies {
                groovyshDependencies "org.apache.groovy:groovy-groovysh:4.0.13"
            }

            project.tasks.register('groovysh') {
                group 'debug'
                description 'Runs an interactive shell in the context of the project. Use :inspect command to inspect project, gradle, settings or other objects.'
                doLast {
                    URLClassLoader groovyshClassLoader = new URLClassLoader();
                    configurations.groovyshDependencies.each {File file ->
                        groovyshClassLoader.addURL(file.toURI().toURL())
                    }

                    def fileHistoryClass
                    def groovyshClass
                    def groovyShell
                    fileHistoryClass = Class.forName('jline.console.history.FileHistory', true, groovyshClassLoader)
                    groovyshClass = Class.forName('org.apache.groovy.groovysh.Groovysh', true, groovyshClassLoader)
                    if (groovyshClass) {
                        groovyShell = groovyshClass.newInstance()
                        if (groovyShell) {
                            groovyShell.interp.context.variables.put("gradle", gradle)
                            groovyShell.interp.context.variables.put("settings", gradle.settings)
                            groovyShell.interp.context.variables.put("project", project)
                            groovyShell.run('# Available objects: gradle, settings, project\n# Try :inspect project')
                        }
                    }
                }
            }
        }
    }
}

To use it:

  • Save the above script in file groovysh-task.gradle
  • Then you can use it in any Gradle project like so:
> gradlew -q --console=plain --init-script groovysh-task.gradle groovysh
Groovy Shell (4.0.13, JVM: 17.0.5)
Type ':help' or ':h' for help.
-------------------------------------------------------------------------------
groovy:000> # Available objects: gradle, settings, project
groovy:001> # Try :inspect project
  • User the following command to inspect the project object
groovy:001> :inspect project
  • You will see:

  • NOTE Starting in Groovy 4.x the Groovy Object Browser has become more useful. You can double click any table row to drill down into that object. Groovy Object Browser keeps track of all the objects you have drilled down into and shows the path of the current object. You can pop out of the stack using the Path menu.

  • If you want to use it in all projects save it as global init script in ${HOME}/.gradle/init.d/groovysh-task.gradle and then you can simply invoke the command:

> gradlew -q --console=plain groovysh

At some point I would like to convert this to a gradle plugin.

Any suggestions are welcome.

Regards,
Sandip

Looks like my post got overwritten by @Vampire 's reply.

BTW what if one is not using IntelliJ. Also, I am not familiar with Kotlin DSL yet. And if one uses the script - not as global init script then it may not be that invasive. Also the user may already be using maven central.

My original post:

I have developed a simple debugging aid to be able to run groovy shell in the context of a Gradle project. I am looking for any expert advise to improve this:

// A simple init script that adds a groovysh task to the root project.
// The groovysh task is a debug task that allows you to run a groovy shell
// in the context of the project. The groovysh task also adds gradle, settings
// and project objects to the shell context. This allows you to inspect the
// project, gradle and settings objects using the Groovy ObjectBrowser GUI.
// You can also run any groovy code
gradle.projectsLoaded {
    rootProject {
        afterEvaluate { project ->
            // Comment out the following block if your project repositories can satify
            // the org.apache.groovy:groovy-groovysh:4.0.13 dependency
            if (!project.repositories.any{it.name == 'MavenRepo'}) {
                project.repositories {
                    // To be able to load org.apache.groovy:groovy-groovysh
                    // and its dependencies
                    mavenCentral()
                }
            }

            project.configurations {
                groovyshDependencies
            }

            project.dependencies {
                groovyshDependencies "org.apache.groovy:groovy-groovysh:4.0.13"
            }

            project.tasks.register('groovysh') {
                group 'debug'
                description 'Runs an interactive shell in the context of the project. Use :inspect command to inspect project, gradle, settings or other objects.'
                doLast {
                    URLClassLoader groovyshClassLoader = new URLClassLoader();
                    configurations.groovyshDependencies.each {File file ->
                        groovyshClassLoader.addURL(file.toURI().toURL())
                    }

                    def fileHistoryClass
                    def groovyshClass
                    def groovyShell
                    fileHistoryClass = Class.forName('jline.console.history.FileHistory', true, groovyshClassLoader)
                    groovyshClass = Class.forName('org.apache.groovy.groovysh.Groovysh', true, groovyshClassLoader)
                    if (groovyshClass) {
                        groovyShell = groovyshClass.newInstance()
                        if (groovyShell) {
                            groovyShell.interp.context.variables.put("gradle", gradle)
                            groovyShell.interp.context.variables.put("settings", gradle.settings)
                            groovyShell.interp.context.variables.put("project", project)
                            groovyShell.run('# Available objects: gradle, settings, project\n# Try :inspect project')
                        }
                    }
                }
            }
        }
    }
}

To use it:

  • Save the above script in file groovysh-task.gradle
  • Then you can use it in any Gradle project like so:
> gradlew -q --console=plain --init-script groovysh-task.gradle groovysh
Groovy Shell (4.0.13, JVM: 17.0.5)
Type ':help' or ':h' for help.
-------------------------------------------------------------------------------
groovy:000> # Available objects: gradle, settings, project
groovy:001> # Try :inspect project
  • User the following command to inspect the project object
groovy:001> :inspect project
  • You will see:

  • NOTE Starting in Groovy 4.x the Groovy Object Browser has become more useful. You can double click any table row to drill down into that object. Groovy Object Browser keeps track of all the objects you have drilled down into and shows the path of the current object. You can pop out of the stack using the Path menu.

  • If you want to use it in all projects save it as global init script in ${HOME}/.gradle/init.d/groovysh-task.gradle and then you can simply invoke the command:

NOTE Please note that the script adds mavenCentral() so that it can download the required dependency. If you do not want that to happen then do not use this script as global init script. Or if your repository set can satisfy the dependency org.apache.groovy:groovy-groovysh:4.0.13 then comment it out.

> gradlew -q --console=plain groovysh

At some point I would like to convert this to a gradle plugin.

Any suggestions are welcome.

Regards,
Sandip

Oh, damn, I’m very sorry. :astonished:
No idea how that happened.
I restored the previous version and post my comment again as comment properly.
Feel free to delete your re-post, and your answer to my comment and re-post it after my re-posted comment so that we have the proper timeline again. :slight_smile:

Adding maven central if missing is a very invasive action.
It can easily change the outcome of a build, especially if you store it as global init script.

Alternatively you can just set a breakpoint and run Gradle through the IntelliJ debugger.
Then you will break at the breakpoint and can use all the IntelliJ debugger features,
including investigating objects like that, executing arbitrary expressions and so on.

Besides that, I can also recommend using the Kotlin DSL instead of the Groovy DSL in builds,
then you instantly have type-safe build scripts with much more helpful error messages if you mess-up the syntax, and an amazingly better IDE support when using a proper IDE like IntelliJ IDEA. :slight_smile: