This is an automated email from the ASF dual-hosted git repository. jamesfredley pushed a commit to branch 8.0.x in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 301b40c20acdb8650ea36cf685fbd54a6341bccb Merge: 66a25164f3 bdb98a6636 Author: James Fredley <[email protected]> AuthorDate: Wed Jul 29 18:41:58 2026 -0400 Merge pull request #16011 from apache/feat/8.0.x-legacy-command-compat Opt-in backwards compatibility for Grails 7 command plugins on Grails 8 .github/workflows/end-to-end.yml | 144 ++++ AGENTS.md | 1 + end-to-end/README.md | 111 +++ end-to-end/build.gradle | 41 + end-to-end/gradle.properties | 25 + end-to-end/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 48462 bytes .../gradle/wrapper/gradle-wrapper.properties | 9 + end-to-end/gradlew | 248 +++++++ end-to-end/gradlew.bat | 82 ++ end-to-end/legacy-commands-plugin/build.gradle | 44 ++ .../legacy/commands/plugin/GreetingService.groovy | 24 + .../commands/plugin/HelloLegacyAppCommand.groovy | 51 ++ .../plugin/HelloLegacyGrailsCommand.groovy | 45 ++ .../plugin/LegacyCommandsPluginGrailsPlugin.groovy | 32 + .../src/main/resources/META-INF/grails.factories | 15 + .../src/main/scripts/hello-legacy-script.groovy | 24 + end-to-end/legacy-commands/build.gradle | 101 +++ .../grails-app/conf/application.yml | 41 + .../legacy-commands/grails-app/conf/logback.xml | 31 + .../controllers/legacycommands/UrlMappings.groovy | 35 + .../init/legacycommands/Application.groovy | 30 + .../services/legacycommands/GreetingService.groovy | 27 + ...egacyCommandCompatibilityIntegrationSpec.groovy | 236 ++++++ end-to-end/legacy-g7-command-plugin/.sdkmanrc | 4 + end-to-end/legacy-g7-command-plugin/build.gradle | 83 +++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 48462 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + end-to-end/legacy-g7-command-plugin/gradlew | 248 +++++++ end-to-end/legacy-g7-command-plugin/gradlew.bat | 82 ++ .../legacy-g7-command-plugin/settings.gradle | 60 ++ .../g7/commands/HelloDerivedNameCommand.groovy | 39 + .../g7/commands/HelloG7PrecompiledCommand.groovy | 49 ++ .../HelloG7PrecompiledGrailsCommand.groovy | 45 ++ .../src/main/resources/META-INF/grails.factories | 1 + end-to-end/settings.gradle | 112 +++ gradle-bootstrap/build.gradle | 47 +- gradle/publish-root-config.gradle | 1 + .../GrailsApplicationContextCommandRunner.groovy | 49 +- ...railsApplicationContextCommandRunnerSpec.groovy | 100 +++ grails-core-cli-legacy/build.gradle | 67 ++ .../grails/dev/commands/ApplicationCommand.groovy | 77 ++ .../grails/dev/commands/ExecutionContext.groovy | 55 ++ .../dev/commands/GrailsApplicationCommand.groovy | 50 ++ .../dev/commands/io/FileSystemInteraction.groovy | 146 ++++ .../commands/io/FileSystemInteractionImpl.groovy | 284 +++++++ .../dev/commands/template/TemplateException.groovy | 32 + .../dev/commands/template/TemplateRenderer.groovy | 174 +++++ .../commands/template/TemplateRendererImpl.groovy | 235 ++++++ .../compat/LegacyApplicationCommandAdapter.groovy | 90 +++ .../compat/LegacyApplicationCommandProvider.groovy | 163 ++++ .../main/resources/META-INF/grails-cli.factories | 16 + .../cli/LegacyCommandRegistryLoadingSpec.groovy | 601 +++++++++++++++ .../LegacyApplicationCommandAdapterSpec.groovy | 107 +++ .../core/cli/ApplicationCommandDiagnostics.groovy | 87 +++ .../ApplicationCommandFactoryKeyProvider.groovy | 29 + .../core/cli/ApplicationCommandProvider.groovy | 32 + .../core/cli/ApplicationCommandRegistrar.groovy | 32 + .../core/cli/ApplicationCommandTargetAware.groovy | 29 + .../cli/ApplicationContextCommandRegistry.groovy | 216 +++++- .../compiler/CommandFactoriesTransformation.groovy | 14 +- .../core/io/support/GrailsFactoriesLoader.groovy | 54 ++ .../grails/util/GrailsUtilStackFiltererSpec.groovy | 23 +- .../GrailsBootstrapRegistryInitializerSpec.groovy | 19 +- .../core/cli/ApplicationCommandProviderSpec.groovy | 822 +++++++++++++++++++++ .../CommandFactoriesTransformationSpec.groovy | 16 + .../test/resources/META-INF/grails-cli.factories | 16 + grails-doc/src/en/guide/introduction/whatsNew.adoc | 26 +- .../src/en/guide/upgrading/upgrading80x.adoc | 131 +++- .../groovy/org/grails/forge/cli/CommandSpec.groovy | 50 +- .../plugin/commands/GrailsCliGradlePlugin.groovy | 83 ++- .../gradle/plugin/core/GrailsExtension.groovy | 15 + .../plugin/commands/CliAutoDiscoverySpec.groovy | 129 +++- .../commands/LegacyCommandTaskDiscoverySpec.groovy | 44 ++ .../cli-companion-autodiscovery/app/build.gradle | 37 +- .../included-digit-bearing-plugin/build.gradle | 21 + .../my-plugin-2fa-one/build.gradle | 1 + .../my-plugin-2fa-snapshot/build.gradle | 1 + .../my-plugin-2fa-timestamp/build.gradle | 1 + .../my-plugin-2fa-unspecified/build.gradle | 1 + .../included-digit-bearing-plugin/settings.gradle | 3 + .../cli-companion-autodiscovery/settings.gradle | 9 + .../legacy-command-discovery/app/build.gradle | 15 + .../legacy-command-discovery/gradle.properties | 2 + .../legacy-command-api/build.gradle | 3 + .../grails/dev/commands/ApplicationCommand.java | 28 + .../java/grails/dev/commands/ExecutionContext.java | 22 + .../legacy-command-plugin/build.gradle | 14 + .../example/legacy/HelloLegacyAppCommand.groovy | 40 + .../src/main/resources/META-INF/grails.factories | 1 + .../legacy-command-discovery/settings.gradle | 3 + .../org/grails/cli/profile/AbstractProfile.groovy | 4 +- .../ApplicationContextCommandFactory.groovy | 62 +- .../factory/GroovyScriptCommandFactory.groovy | 2 + .../grails/cli/profile/AbstractProfileSpec.groovy | 119 +++ .../ApplicationContextCommandFactorySpec.groovy | 113 +++ .../factory/LegacyPluginScriptCompatSpec.groovy | 107 +++ settings.gradle | 1 + 97 files changed, 6790 insertions(+), 107 deletions(-) diff --cc grails-doc/src/en/guide/upgrading/upgrading80x.adoc index 2389f9f34d,86ea9f63cc..cfb81c4111 --- a/grails-doc/src/en/guide/upgrading/upgrading80x.adoc +++ b/grails-doc/src/en/guide/upgrading/upgrading80x.adoc @@@ -1778,36 -1743,84 +1803,84 @@@ The renamed framework command packages |`org.apache.grails.security.cli.*` (grails-spring-security-cli) |=== - **Third-party plugins must be rebuilt against Grails 8 for their commands to be available.** - Apply the new `org.apache.grails.gradle.grails-plugin-cli` Gradle plugin, move command sources from - `grails-app/commands` to `src/cli/groovy`, and update the contract imports — recompiling then - regenerates the `grails-cli.factories` registration automatically and the companion `-cli` - artifact is published alongside the plugin (see - xref:commandLine#creatingCustomCommands[Creating Custom Commands] for the complete workflow). - Hand-authored command registrations must move from - `src/main/resources/META-INF/grails.factories` to `.../grails-cli.factories`. A Grails 7 command - plugin dropped onto a Grails 8 application unchanged will not have its commands discovered. - - **What this change means for upgrading an application.** Plugin re-releases are *not* a - prerequisite for upgrading the application itself — the impact of the CLI split is bounded as - follows: - - * *Plugins that ship no commands* are entirely unaffected by this change. - * *Command-bearing plugins that have not yet been rebuilt for Grails 8* keep their runtime - functionality — controllers, services, taglibs, and other artefacts continue to work exactly as - before, because the runtime plugin jar never depended on the command contract. Only the plugin's - *commands* are unavailable: their registrations in the legacy `grails.factories` location are - ignored (the clean break above), so they disappear from the command list rather than fail. The - commands return once the plugin publishes a Grails 8 release with a companion `-cli` artifact. - * *The application's own commands* in `grails-app/commands` need only the import rename shown - above; the build wiring is automatic. - - NOTE: The statements above are about the CLI split specifically. Independently of it, Grails 8 - removes APIs that were deprecated in Grails 7 — a Grails 7 plugin that avoided those deprecations - generally continues to work on Grails 8 unchanged, while one that relied on them (or on other - Grails 8 changes such as Spring Boot 4) needs an update for that reason. + ===== 35.1 Backwards Compatibility for Existing Command Plugins + + Grails 8 provides a deprecated, execution-only compatibility layer for already-published Grails 7 command + plugins. Application commands created with `create-command` that implement + `grails.dev.commands.ApplicationCommand` or `grails.dev.commands.GrailsApplicationCommand` remain + available without a plugin re-release when the bridge is enabled. The deprecated + `grails.dev.commands.*` types are retained in `org.apache.grails:grails-core-cli-legacy`, which is + provisioned only on the command runner classpath when both `cliAutoProvision` and + `legacyCommandSupport` are enabled (`legacyCommandSupport` defaults to `false`), and registrations + in `META-INF/grails.factories` under `grails.dev.commands.ApplicationCommand` continue to be + discovered at runtime. + + Enable the bridge in the consuming application: + + [source,groovy] + .build.gradle + ---- + grails { + legacyCommandSupport = true + } + ---- + + Or set the project property `grailsLegacyCommandSupport=true`. + + When the bridge is disabled, Grails still inspects legacy `META-INF/grails.factories` resources at + command runtime without loading their command classes. If a plugin still publishes + `grails.dev.commands.ApplicationCommand` entries, Grails logs an error naming the plugin artifact. + An unknown command also includes the remediation to enable `legacyCommandSupport` or upgrade the + plugin. This detection does not resolve application dependencies during Gradle configuration. + + Grails logs a one-time deprecation warning when it uses this compatibility layer. It is intended + to keep existing published plugins working while plugin authors migrate; it is deprecated and may + be removed in a future major release. No removal major has been announced. + + Legacy application commands do not receive a dedicated Gradle task. Run them through the generic + `runCommand` task (or the shell, which routes legacy adapters there), for example: + + [source,bash] + ---- + ./gradlew runCommand "-Pargs=generate-controller example.Book" + ---- + + Named per-command Gradle tasks remain available only for migrated commands published in a + companion `-cli` artifact. + + New and migrated commands should always compile against `org.apache.grails.core.cli.*` from + `grails-core-cli`. A plugin that intentionally continues to author commands against the deprecated + contract must declare `compileOnly 'org.apache.grails:grails-core-cli-legacy'` explicitly. Consumers + of already-published Grails 7 command plugins must opt in with + `grails { legacyCommandSupport = true }` (or declare `grailsCliLegacy` themselves). + + **What just works:** + + * Code generation scripts created with `create-script`, including scripts packaged as + `META-INF/commands/*.groovy` such as `audit-quickstart` and `s2-quickstart`, are unaffected. + * YAML commands packaged as `META-INF/commands/*.yml` are unaffected. + * Existing published Grails 7 application-command plugins continue to work on Grails 8 without a + re-release once `legacyCommandSupport` is enabled. + + **What consumers of Grails 7 command plugins must do:** + + * Enable `grails { legacyCommandSupport = true }` (or `-PgrailsLegacyCommandSupport=true`), or + declare `grailsCliLegacy 'org.apache.grails:grails-core-cli-legacy'` explicitly when + `cliAutoProvision` is disabled. + + **What plugin authors must do:** + + * Migrate application commands to the `org.apache.grails.core.cli.*` API and register them in + `META-INF/grails-cli.factories`. + * Publish commands in a companion `-cli` artifact for the CLI classpath split and per-command + Gradle task support. See xref:commandLine#creatingCustomCommands[Creating Custom Commands] for + the complete workflow. + + NOTE: The compatibility layer applies only to the legacy application-command contract. Independently + of it, a Grails 7 plugin that relies on APIs removed in Grails 8, Spring Boot 4, or other Grails 8 + changes still requires an update. -==== 36. jQuery Webjar Upgraded to 4.0.0 +==== 37. jQuery Webjar Upgraded to 4.0.0 The jQuery webjar managed by the `grails-bom` (`org.webjars.npm:jquery`) moves from 3.7.1 to 4.0.0. jQuery 4 is a major release: it drops Internet Explorer and other legacy browsers and removes long-deprecated utilities such as `jQuery.trim`, `jQuery.type`, `jQuery.isArray`, `jQuery.isFunction`, `jQuery.proxy`, and `jQuery.isWindow`.
