Skip to content

Conversation

@codeconsole
Copy link
Contributor

@codeconsole codeconsole commented Nov 25, 2025

To bom or not to bom?

This fix allows me to go to sleep tonight...

  1. Fixes version resolution
  2. Sorts versions
  3. Disables bom module file that breaks dependency management and actually forces wrong versions on transitive dependencies
  4. Removes 131 redundant versions

#15258

Gradle modules are not needed for the bom and prohibit the dependency management plugin

Working M3 Bom Artifacts
Broken M4 Bom Artifacts

grails-bom-7.0.0-M3.pom       27207 <- everything working, no module file
grails-bom-7.0.0-M4.pom       28845 
grails-bom-7.0.0-M4.module    52007 <- forces versions on everything, version overrides broken
grails-bom-7.0.0-RC2.pom      41614 <- adds 131 versions 
grails-bom-7.0.0-RC2.module   54584
grails-bom-7.0.3.pom          26621 <- removes all versions from everything
grails-bom-7.0.3.module       52007
grails-bom-7.1.0-SNAPSHOT.pom 29781 <- no module, allows version overrides

After this fix you can override any version just by modifying gradle.properties

This was not possible in 7.0.0-M4 -> 7.0.3

spring-boot.version=3.5.8
groovy.version=4.0.29

Spring Boot Does Not Use a .module file with their bom:
3.5.8
4.0.0-RC2

@github-actions github-actions bot added the bug label Nov 25, 2025
@codeconsole codeconsole changed the title Solve BOM existential crisis - Fixes Solve BOM existential crisis - Fixes #15258 Nov 25, 2025

// Disable Gradle Module Metadata (.module files) for the BOM
// This allows properties to be overridden with the Spring dependency management plugin
tasks.withType(GenerateModuleMetadata).configureEach {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cannot be done. We switched to the modules intentionally to fix gradle resolution without the dependency management plugin. The intent is to remove the dependency management plugin in grails 8

Copy link
Contributor Author

@codeconsole codeconsole Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdaugherty modules break the dependency management plugin. It doesn't work AND all this is doing is turning off a useless module for the bom ONLY. Why do you want a gradle module for a bom??

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we're ultimately adopting the platform() in Grails 8 and that requires the gradle module. It sounds like you're using both platform() and spring dependency management plugin. This is the cause of your issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdaugherty, not I am not using platform at all. It is completely broken as is.

@codeconsole codeconsole requested a review from matrei November 25, 2025 05:56
*/
class PropertyNameCalculator {

static final String GRAILS_VERSION_PROPERTY = 'grails.version'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would you ever override different versions of grails projects? Why not just change the version of the bom you're using instead?

Copy link
Contributor Author

@codeconsole codeconsole Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdaugherty Compare the before (M3) and after BOMs.

I am saying overriding is better than using a grails property for everyone because if you did that you would get version conflicts from transitive dependencies.

I am recommending using

<grails.version>7.1.0-SNAPSHOT</grails.version>

instead of

    <grails.async.version>7.1.0-SNAPSHOT</grails.async.version>
    <grails.async.core.version>7.1.0-SNAPSHOT</grails.async.core.version>
    <grails.async.gpars.version>7.1.0-SNAPSHOT</grails.async.gpars.version>
    <grails.async.rxjava.version>7.1.0-SNAPSHOT</grails.async.rxjava.version>
    <grails.async.rxjava2.version>7.1.0-SNAPSHOT</grails.async.rxjava2.version>
    <grails.async.rxjava3.version>7.1.0-SNAPSHOT</grails.async.rxjava3.version>
    <grails.bootstrap.version>7.1.0-SNAPSHOT</grails.bootstrap.version>
    <grails.cache.version>7.1.0-SNAPSHOT</grails.cache.version>
    <grails.codecs.version>7.1.0-SNAPSHOT</grails.codecs.version>
    <grails.codecs.core.version>7.1.0-SNAPSHOT</grails.codecs.core.version>
    <grails.common.version>7.1.0-SNAPSHOT</grails.common.version>
    <grails.console.version>7.1.0-SNAPSHOT</grails.console.version>
    <grails.controllers.version>7.1.0-SNAPSHOT</grails.controllers.version>
    <grails.converters.version>7.1.0-SNAPSHOT</grails.converters.version>
    <grails.core.version>7.1.0-SNAPSHOT</grails.core.version>
    <grails.data.hibernate5.version>7.1.0-SNAPSHOT</grails.data.hibernate5.version>
    <grails.data.hibernate5.core.version>7.1.0-SNAPSHOT</grails.data.hibernate5.core.version>
    <grails.data.hibernate5.dbmigration.version>7.1.0-SNAPSHOT</grails.data.hibernate5.dbmigration.version>
    <grails.data.hibernate5.spring.boot.version>7.1.0-SNAPSHOT</grails.data.hibernate5.spring.boot.version>
    <grails.data.mongodb.version>7.1.0-SNAPSHOT</grails.data.mongodb.version>
    <grails.data.mongodb.bson.version>7.1.0-SNAPSHOT</grails.data.mongodb.bson.version>
    <grails.data.mongodb.core.version>7.1.0-SNAPSHOT</grails.data.mongodb.core.version>
    <grails.data.mongodb.ext.version>7.1.0-SNAPSHOT</grails.data.mongodb.ext.version>
    <grails.data.mongodb.gson.templates.version>7.1.0-SNAPSHOT</grails.data.mongodb.gson.templates.version>
    <grails.data.mongodb.spring.boot.version>7.1.0-SNAPSHOT</grails.data.mongodb.spring.boot.version>
    <grails.data.simple.version>7.1.0-SNAPSHOT</grails.data.simple.version>
    <grails.databinding.version>7.1.0-SNAPSHOT</grails.databinding.version>
    <grails.databinding.core.version>7.1.0-SNAPSHOT</grails.databinding.core.version>
    <grails.datamapping.async.version>7.1.0-SNAPSHOT</grails.datamapping.async.version>
    <grails.datamapping.core.version>7.1.0-SNAPSHOT</grails.datamapping.core.version>
    <grails.datamapping.core.test.version>7.1.0-SNAPSHOT</grails.datamapping.core.test.version>
    <grails.datamapping.support.version>7.1.0-SNAPSHOT</grails.datamapping.support.version>
    <grails.datamapping.tck.version>7.1.0-SNAPSHOT</grails.datamapping.tck.version>
    <grails.datamapping.validation.version>7.1.0-SNAPSHOT</grails.datamapping.validation.version>
    <grails.datasource.version>7.1.0-SNAPSHOT</grails.datasource.version>
    <grails.datastore.async.version>7.1.0-SNAPSHOT</grails.datastore.async.version>
    <grails.datastore.core.version>7.1.0-SNAPSHOT</grails.datastore.core.version>
    <grails.datastore.web.version>7.1.0-SNAPSHOT</grails.datastore.web.version>
    <grails.dependencies.assets.version>7.1.0-SNAPSHOT</grails.dependencies.assets.version>
    <grails.dependencies.starter.web.version>7.1.0-SNAPSHOT</grails.dependencies.starter.web.version>
    <grails.dependencies.test.version>7.1.0-SNAPSHOT</grails.dependencies.test.version>
    <grails.domain.class.version>7.1.0-SNAPSHOT</grails.domain.class.version>
    <grails.encoder.version>7.1.0-SNAPSHOT</grails.encoder.version>
    <grails.events.version>7.1.0-SNAPSHOT</grails.events.version>
    <grails.events.compat.version>7.1.0-SNAPSHOT</grails.events.compat.version>
    <grails.events.core.version>7.1.0-SNAPSHOT</grails.events.core.version>
    <grails.events.gpars.version>7.1.0-SNAPSHOT</grails.events.gpars.version>
    <grails.events.rxjava.version>7.1.0-SNAPSHOT</grails.events.rxjava.version>
    <grails.events.rxjava2.version>7.1.0-SNAPSHOT</grails.events.rxjava2.version>
    <grails.events.rxjava3.version>7.1.0-SNAPSHOT</grails.events.rxjava3.version>
    <grails.events.spring.version>7.1.0-SNAPSHOT</grails.events.spring.version>
    <grails.events.transforms.version>7.1.0-SNAPSHOT</grails.events.transforms.version>
    <grails.fields.version>7.1.0-SNAPSHOT</grails.fields.version>
    <grails.geb.version>7.1.0-SNAPSHOT</grails.geb.version>
    <grails.gsp.version>7.1.0-SNAPSHOT</grails.gsp.version>
    <grails.gsp.core.version>7.1.0-SNAPSHOT</grails.gsp.core.version>
    <grails.gsp.spring.boot.version>7.1.0-SNAPSHOT</grails.gsp.spring.boot.version>
    <grails.i18n.version>7.1.0-SNAPSHOT</grails.i18n.version>
    <grails.interceptors.version>7.1.0-SNAPSHOT</grails.interceptors.version>
    <grails.layout.version>7.1.0-SNAPSHOT</grails.layout.version>
    <grails.logging.version>7.1.0-SNAPSHOT</grails.logging.version>
    <grails.micronaut.version>7.1.0-SNAPSHOT</grails.micronaut.version>
    <grails.mimetypes.version>7.1.0-SNAPSHOT</grails.mimetypes.version>
    <base.version>7.1.0-SNAPSHOT</base.version>
    <plugin.version>7.1.0-SNAPSHOT</plugin.version>
    <profile.version>7.1.0-SNAPSHOT</profile.version>
    <rest.api.version>7.1.0-SNAPSHOT</rest.api.version>
    <rest.api.plugin.version>7.1.0-SNAPSHOT</rest.api.plugin.version>
    <web.version>7.1.0-SNAPSHOT</web.version>
    <web.plugin.version>7.1.0-SNAPSHOT</web.plugin.version>
    <grails.rest.transforms.version>7.1.0-SNAPSHOT</grails.rest.transforms.version>
    <grails.scaffolding.version>7.1.0-SNAPSHOT</grails.scaffolding.version>
    <grails.services.version>7.1.0-SNAPSHOT</grails.services.version>
    <grails.shell.cli.version>7.1.0-SNAPSHOT</grails.shell.cli.version>
    <grails.sitemesh3.version>7.1.0-SNAPSHOT</grails.sitemesh3.version>
    <grails.spring.version>7.1.0-SNAPSHOT</grails.spring.version>
    <grails.taglib.version>7.1.0-SNAPSHOT</grails.taglib.version>
    <grails.test.core.version>7.1.0-SNAPSHOT</grails.test.core.version>
    <grails.testing.support.core.version>7.1.0-SNAPSHOT</grails.testing.support.core.version>
    <grails.testing.support.datamapping.version>7.1.0-SNAPSHOT</grails.testing.support.datamapping.version>
    <grails.testing.support.mongodb.version>7.1.0-SNAPSHOT</grails.testing.support.mongodb.version>
    <grails.testing.support.views.gson.version>7.1.0-SNAPSHOT</grails.testing.support.views.gson.version>
    <grails.testing.support.web.version>7.1.0-SNAPSHOT</grails.testing.support.web.version>
    <grails.url.mappings.version>7.1.0-SNAPSHOT</grails.url.mappings.version>
    <grails.validation.version>7.1.0-SNAPSHOT</grails.validation.version>
    <grails.views.core.version>7.1.0-SNAPSHOT</grails.views.core.version>
    <grails.views.gson.version>7.1.0-SNAPSHOT</grails.views.gson.version>
    <grails.views.markup.version>7.1.0-SNAPSHOT</grails.views.markup.version>
    <grails.web.boot.version>7.1.0-SNAPSHOT</grails.web.boot.version>
    <grails.web.common.version>7.1.0-SNAPSHOT</grails.web.common.version>
    <grails.web.core.version>7.1.0-SNAPSHOT</grails.web.core.version>
    <grails.web.databinding.version>7.1.0-SNAPSHOT</grails.web.databinding.version>
    <grails.web.gsp.version>7.1.0-SNAPSHOT</grails.web.gsp.version>
    <grails.web.gsp.taglib.version>7.1.0-SNAPSHOT</grails.web.gsp.taglib.version>
    <grails.web.jsp.version>7.1.0-SNAPSHOT</grails.web.jsp.version>
    <grails.web.mvc.version>7.1.0-SNAPSHOT</grails.web.mvc.version>
    <grails.web.taglib.version>7.1.0-SNAPSHOT</grails.web.taglib.version>
    <grails.web.url.mappings.version>7.1.0-SNAPSHOT</grails.web.url.mappings.version>
    <grails.gradle.plugins.version>7.1.0-SNAPSHOT</grails.gradle.plugins.version>
    <grails.gradle.model.version>7.1.0-SNAPSHOT</grails.gradle.model.version>
    <grails.gradle.common.version>7.1.0-SNAPSHOT</grails.gradle.common.version>
    <grails.gradle.tasks.version>7.1.0-SNAPSHOT</grails.gradle.tasks.version>
...

@jdaugherty
Copy link
Contributor

As an incremental fix, we should first merge:

#15260 which requires apache/grails-gradle-publish#16

Not having a deferred lookup is the main reason properties were broken in the bom.

@codeconsole
Copy link
Contributor Author

As an incremental fix, we should first merge:

#15260 which requires apache/grails-gradle-publish#16

Not having a deferred lookup is the main reason properties were broken in the bom.

This still

  1. Sorts versions
  2. Disables bom module file that breaks dependency management and actually forces wrong versions on transitive dependencies
  3. Removes 131 redundant versions

@jdaugherty
Copy link
Contributor

jdaugherty commented Nov 26, 2025

After publishing the other changes, the bom with properties can be seen here: https://repository.apache.org/service/local/repo_groups/snapshots-group/content/org/apache/grails/grails-bom/7.0.4-SNAPSHOT/grails-bom-7.0.4-20251126.132828-4.pom

Concerning this comment:

Number 2 is false. The spring dependency management plugin works just fine with the module metadata published. You can see this in the example project here: https://github.com/jdaugherty/grails-bom-demo-spring-dependency-management (this project downgrades spring boot with a property setting only). Again, you must not use the platform if you want the property behavior. We intentionally shipped the platform() because there isn't an alternative in gradle build script & to be consistent we defined it in both locations. We intend to remove the spring dependency management plugin in Grails 8.

For sorting versions, I'm indifferent and I think it's ok to accept.

For the 131 redundant versions, by redundant I assume you mean that a grails.version property isn't defined for the grails project? Technically if you know what you're doing, you could selectively upgrade one of those libraries with the way this is defined. This allows for the most flexibility. I think this is something that should be discussed since we launched 7.0.0 with this design. If you want to change the overall grails version, you would just select a different bom version. The current state allows for the most flexibility.

@jdaugherty
Copy link
Contributor

@codeconsole I changed the example project to make it clear that i removed platform() - the initial commit now contains the default app generated in grails forge and the second contains the way you would setup your project to use the dependency management plugin.

@codeconsole
Copy link
Contributor Author

codeconsole commented Nov 26, 2025

Number 2 is false. The spring dependency management plugin works just fine with the module metadata published. You can see this in the example project here: https://github.com/jdaugherty/grails-bom-demo-spring-dependency-management (this project downgrades spring boot with a property setting only). Again, you must not use the platform if you want the property behavior. We intentionally shipped the platform() because there isn't an alternative in gradle build script & to be consistent we defined it in both locations. We intend to remove the spring dependency management plugin in Grails 8.

@jdaugherty you are right, I was able to get RC2 working. I think the big error there was spring.boot.version was renamed to spring.boot.dependencies.version, but it also works with platform.

implementation platform("org.apache.grails:grails-bom:$grailsVersion") has no impact on the version being set

@codeconsole
Copy link
Contributor Author

codeconsole commented Nov 26, 2025

For the 131 redundant versions, by redundant I assume you mean that a grails.version property isn't defined for the grails project? Technically if you know what you're doing, you could selectively upgrade one of those libraries with the way this is defined. This allows for the most flexibility. I think this is something that should be discussed since we launched 7.0.0 with this design. If you want to change the overall grails version, you would just select a different bom version. The current state allows for the most flexibility.

@jdaugherty The emphasis here is I don't see a working scenario where you would want to set any of those versions via a property because their transitive dependencies would resolve a different version. Since they are all based off a Grails version, setting to a different version would cause unexpected transitive resolution.

@jdaugherty
Copy link
Contributor

Number 2 is false. The spring dependency management plugin works just fine with the module metadata published. You can see this in the example project here: https://github.com/jdaugherty/grails-bom-demo-spring-dependency-management (this project downgrades spring boot with a property setting only). Again, you must not use the platform if you want the property behavior. We intentionally shipped the platform() because there isn't an alternative in gradle build script & to be consistent we defined it in both locations. We intend to remove the spring dependency management plugin in Grails 8.

@jdaugherty you are right, I was able to get RC2 working. I think the big error there was spring.boot.version was renamed to spring.boot.dependencies.version, but it also works with platform.

implementation platform("org.apache.grails:grails-bom:$grailsVersion") has no impact on the version being set

The original goal of the bom changes was so we could document it & define dependencies in a central place - especially because we generate two boms (grails-gradle-bom & grails-bom). I used a prefix naming strategy originally because it was claimed that dependabot could handle versions & coordinates in the same gradle file - so I wanted to keep it simple for dependabot. What that did not mention is it only handles String versions & can't handle the map syntax. So we can rework this ...

For Gradle 9, we have to rewrite all of it anyhow - gradle doesn't allow across project resolution as of Gradle 9 (see the versions plugin & associated ticket where this was discovered without a release note in Gradle 9). We need to extract the bom logic into it's own plugin using maven specific libraries to parse poms instead of gradle - similar to what spring did and then generate documentation & the bom from that plugin. We can hack in the old name if you want, but the current property is based on the coordinate name.

As for it working with both, that's great news. We should revert the metadata disable then.

@jdaugherty
Copy link
Contributor

For the 131 redundant versions, by redundant I assume you mean that a grails.version property isn't defined for the grails project? Technically if you know what you're doing, you could selectively upgrade one of those libraries with the way this is defined. This allows for the most flexibility. I think this is something that should be discussed since we launched 7.0.0 with this design. If you want to change the overall grails version, you would just select a different bom version. The current state allows for the most flexibility.

@jdaugherty The emphasis here is I don't see a working scenario where you would want to set any of those versions via a property because their transitive dependencies would resolve a different version. Since they are all based off a Grails version, setting to a different version would cause unexpected transitive resolution.

Isn't that only true if the transitive dependency is in the bom? What if someone wanted to pull in fields because a new default template was added - then that dependency doesn't matter. I could see this be true for a lot of grails projects.

@jdaugherty
Copy link
Contributor

We discussed this PR in the weekly meeting. Given that the multiple properties allow for the option to customize select libraries, we want to keep that feature.

I believe the only other feature this PR contributed was sorting the properties. If you want to update the PR for that we can merge this. Otherwise, we'll plan on closing this one.

@codeconsole
Copy link
Contributor Author

We discussed this PR in the weekly meeting. Given that the multiple properties allow for the option to customize select libraries, we want to keep that feature.

I believe the only other feature this PR contributed was sorting the properties. If you want to update the PR for that we can merge this. Otherwise, we'll plan on closing this one.

@jdaugherty can you give an actual use case where you would use these properties? There is no grouping on any of them which makes no difference if you import them directly.

<grails.async.gpars.version>7.1.0-SNAPSHOT</grails.async.gpars.version>
<grails.events.gpars.version>7.1.0-SNAPSHOT</grails.events.gpars.version>

why would you set

grails.async.gpars.version=7.1.0-SNAPSHOT

instead of just putting it in the buld.gradle directly? what is the point?

implementation 'org.apache.grails.asyncs:grails-async-gpars:7.1.0-SNAPSHOT'

I could, possibly, understand grails.async.version, but is there really a use case for introducing 130 individual properties with the same exact version? What is the point of that?

The decision to keep 130 properties sounds like it is going to introduce a lot of unknown behavior by the end user.

@jdaugherty
Copy link
Contributor

The reason we kept the dependency management plugin is it uses properties to be able to quickly set versions. There's a version for each project because each project is a separate jar.

The only reason they're the same version is because of the mono repo, but technically people can choose different versions. There also isn't "unknown" behavior by leaving properties in a pom. We're opting to keep the properties to keep the flexibility of prior grails versions.

@jdaugherty
Copy link
Contributor

@codeconsole I haven't seen an example of where it's bad to keep the flexibility of the pom properties. I also haven't seen any updates to this PR. do you wish to abandon it?

@codeconsole
Copy link
Contributor Author

@codeconsole I haven't seen an example of where it's bad to keep the flexibility of the pom properties. I also haven't seen any updates to this PR. do you wish to abandon it?

I am finding it quite hard to understand why you would ever set a version for an individual dependency instead of a group?
What value does that serve? Isn't it overly redundant?

With my previous bom code, versions were set on groups, which did have value.

As I asked previously:

what is the advantage of

grails.async.gpars.version=7.1.0-SNAPSHOT

over

implementation 'org.apache.grails.asyncs:grails-async-gpars:7.1.0-SNAPSHOT'

why would you ever do the former??

@matrei
Copy link
Contributor

matrei commented Dec 10, 2025

If the property is the same for all Grails modules, couldn't you just use the org.apache.grails:grails-bom with that version instead?

@jdaugherty
Copy link
Contributor

@codeconsole To answer your question: the advantage of the property approach is you can define properties in more ways independently of the project. This is the only reason we kept the dependency management plugin. As long as we keep that plugin, we should keep the property support since it's central to including that plugin.

@jdaugherty
Copy link
Contributor

Also, as for why someone would want to upgrade, I have given previous examples of that - fields or more isolated downstream projects could very well be updated instead of something like grails-core.

So the property support + arbitrary upgrades is a valid use case.

@codeconsole
Copy link
Contributor Author

@jdaugherty I am still not understanding your explanation for these 130 properties that all have the same version 7.1.0-SNAPSHOT Can you please provide an example when you would use any of them and the value of doing that over just specifying the dependency in build.gradle?

Properties in boms are typically for dependency groups, not individual child dependencies of a project.

With my previous bom code, versions were set on groups, which did have value.

As I asked previously:

what is the advantage of

grails.async.gpars.version=7.1.0-SNAPSHOT

over

implementation 'org.apache.grails.asyncs:grails-async-gpars:7.1.0-SNAPSHOT'

why would you ever do the former??

@codeconsole
Copy link
Contributor Author

codeconsole commented Dec 12, 2025

If the property is the same for all Grails modules, couldn't you just use the org.apache.grails:grails-bom with that version instead?

you could, but them you would get that version's bom dependencies. I would argue more for removing the 100+ module properties altogether.

I think the bom now is overwhelmed with so many redundant properties.

I'd recommend any of the following:

  1. Go back to the previous format that actually grouped them and providing something useful. However, the value is probably quite limited since they are all released as the same version.
        org.grails.plugins:gsp::
        org.grails:grails-datastore-gorm-hibernate5::
        org.grails:grails-datastore-gorm-mongodb::
        org.grails:grails-datastore-async,grails-datastore-core,grails-datastore-gorm,grails-datastore-gorm-a
sync,grails-datastore-gorm-support,grails-datastore-gorm-rx,grails-datastore-gorm-test,grails-datastore-gorm-
validation,grails-datastore-web,grails-datastore-gorm-tck,grails-gorm-testing-support:::grails-datastore
  1. Use the grails.version
  2. Remove the properties from the bom and just hardcode the version.

@jamesfredley
Copy link
Contributor

It's not about which of these is better. Since Grails 7 applications are still generated with the Spring Dependency Management Grails Plugin, we support both pathways to override the grails artifact versions.

In addition to just using the new grails-bom version, which 99.999% of user will do.

Overriding just one or a few of these artifacts will rarely be done, but we must maintain support for it via

  1. Spring Dependency Management Plugin Properties
    grails.async.gpars.version=7.1.0-SNAPSHOT

  2. Adjusting the dependency directly
    implementation 'org.apache.grails.asyncs:grails-async-gpars:7.1.0-SNAPSHOT'

Users can pick which path they want to follow and having both is consistent with prior Grails versions.

Removing the grails.*.*.version properties from the bom will remove the ability to adjust the versions via properties which we can't do in the middle of 7.x.x releases: https://docs.spring.io/spring-boot/3.5/gradle-plugin/managing-dependencies.html#managing-dependencies.dependency-management-plugin.customizing

For Grails 8, #14142 will be addressed.

@codeconsole
Copy link
Contributor Author

It's not about which of these is better. Since Grails 7 applications are still generated with the Spring Dependency Management Grails Plugin, we support both pathways to override the grails artifact versions.

This isn't a question of better. It's about proper bom design and using bom versions how they are intended. Versions are more there to specify versions for dependency groups, not individual submodules. There is no history of these 100+ versions existing. Why introduce them now? It's variable overkill. This looks more like a bug than a feature.

The correct approach would either be: 1. remove them completely or 2. create the appropriate groups (which is how historically it was done).

Please explain what the expected behavior is in it's current design for:

grails.async.gpars.version=7.1.0-SNAPSHOT

will this also update grails async or just grails.async.gpars? if so, is the correct property name grails.async?

Is this introduction of a 130 individual versions documented anywhere with usage instructions?

@jdaugherty
Copy link
Contributor

@codeconsole You're not presenting a technical argument here. We answered your questions:

  • There are more versions because we went to a mono repo. We decided to do this based on continuing to recommend the dependency management plugin & it's support for property based configuration.
  • As for the naming, this decision was to make it easy to configure based on the grails project name. We went with this in 7.0 so we can't change it now. The time to raise the name problem would have been prior to the 7.0 release.
  • As for a specific example, you seem to have not accepted the example I gave concerning fields. Could we have grouped these, yes. We decided against it to be more flexible.
  • As for adding a dependency via gradle instead, that isn't what's under question. These versions exist only because of the property support in dependency management. You don't even have to change code with the property support. An environment variable of ORG_GRADLE_grails.fields.version=7.0.3 would override the version. Removing the properties, removes this support.

I'm a -1 on this PR / code change until a technical reason can be given on why we should limit end apps flexibility & choice.

@jamesfredley jamesfredley added this to the grails:8.0.0-M1 milestone Dec 17, 2025
@codeconsole
Copy link
Contributor Author

codeconsole commented Dec 22, 2025

@jdaugherty The 130 versions doesn't have anything to do with the mono-repo. It has to do with, all of the sudden, introducing variables for versions specific to the project. There should never be a 1-1 correlation between dependencies and version variables. Can you give an example of anyone else that does this??

project submodules versions should be hard coded
project dependencies should be grouped

This is how things worked previously.

Have you looked at the Spring Boot Bom? What I am suggesting models exactly what Spring Boot does and was how everything worked before.

For example, if Spring were to follow your suggestion, they would need to introduce 100's of versions as well:

...
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-resttestclient</artifactId>
  <version>4.0.1</version>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-rsocket</artifactId>
  <version>4.0.1</version>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-rsocket-test</artifactId>
  <version>4.0.1</version>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-security</artifactId>
  <version>4.0.1</version>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-security-oauth2-authorization-server</artifactId>
  <version>4.0.1</version>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-security-oauth2-client</artifactId>
  <version>4.0.1</version>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-security-oauth2-resource-server</artifactId>
  <version>4.0.1</version>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-security-saml2</artifactId>
  <version>4.0.1</version>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-security-test</artifactId>
  <version>4.0.1</version>
</dependency>
...

If Spring did what you were suggesting, they would have to add versions to their bom for:

spring-boot,spring-boot-activemq,spring-boot-actuator,spring-boot-actuator-autoconfigure,spring-boot-amqp,spring-boot-artemis,spring-boot-autoconfigure,spring-boot-autoconfigure-classic,spring-boot-autoconfigure-classic-modules,spring-boot-autoconfigure-processor,spring-boot-batch,spring-boot-batch-jdbc,spring-boot-buildpack-platform,spring-boot-cache,spring-boot-cache-test,spring-boot-cassandra,spring-boot-cloudfoundry,spring-boot-configuration-metadata,spring-boot-configuration-processor,spring-boot-couchbase,spring-boot-data-cassandra,spring-boot-data-cassandra-test,spring-boot-data-commons,spring-boot-data-couchbase,spring-boot-data-couchbase-test,spring-boot-data-elasticsearch,spring-boot-data-elasticsearch-test,spring-boot-data-jdbc,spring-boot-data-jdbc-test,spring-boot-data-jpa,spring-boot-data-jpa-test,spring-boot-data-ldap,spring-boot-data-ldap-test,spring-boot-data-mongodb,spring-boot-data-mongodb-test,spring-boot-data-neo4j,spring-boot-data-neo4j-test,spring-boot-data-r2dbc,spring-boot-data-r2dbc-test,spring-boot-data-redis,spring-boot-data-redis-test,spring-boot-data-rest,spring-boot-devtools,spring-boot-docker-compose,spring-boot-elasticsearch,spring-boot-flyway,spring-boot-freemarker,spring-boot-graphql,spring-boot-graphql-test,spring-boot-groovy-templates,spring-boot-gson,spring-boot-h2console,spring-boot-hateoas,spring-boot-hazelcast,spring-boot-health,spring-boot-hibernate,spring-boot-http-client,spring-boot-http-codec,spring-boot-http-converter,spring-boot-integration,spring-boot-jackson,spring-boot-jackson2,spring-boot-jarmode-tools,spring-boot-jdbc,spring-boot-jdbc-test,spring-boot-jersey,spring-boot-jetty,spring-boot-jms,spring-boot-jooq,spring-boot-jooq-test,spring-boot-jpa,spring-boot-jpa-test,spring-boot-jsonb,spring-boot-kafka,spring-boot-kotlinx-serialization-json,spring-boot-ldap,spring-boot-liquibase,spring-boot-loader,spring-boot-mail,spring-boot-micrometer-metrics,spring-boot-micrometer-metrics-test,spring-boot-micrometer-observation,spring-boot-micrometer-tracing,spring-boot-micrometer-tracing-brave,spring-boot-micrometer-tracing-opentelemetry,spring-boot-micrometer-tracing-test,spring-boot-mongodb,spring-boot-mustache,spring-boot-neo4j,spring-boot-netty,spring-boot-opentelemetry,spring-boot-persistence,spring-boot-properties-migrator,spring-boot-pulsar,spring-boot-quartz,spring-boot-r2dbc,spring-boot-reactor,spring-boot-reactor-netty,spring-boot-restclient,spring-boot-restclient-test,spring-boot-restdocs,spring-boot-resttestclient,spring-boot-rsocket,spring-boot-rsocket-test,spring-boot-security,spring-boot-security-oauth2-authorization-server,spring-boot-security-oauth2-client,spring-boot-security-oauth2-resource-server,spring-boot-security-saml2,spring-boot-security-test,spring-boot-sendgrid,spring-boot-servlet,spring-boot-session,spring-boot-session-data-redis,spring-boot-session-jdbc,spring-boot-sql,spring-boot-starter,spring-boot-starter-activemq,spring-boot-starter-activemq-test,spring-boot-starter-actuator,spring-boot-starter-actuator-test,spring-boot-starter-amqp,spring-boot-starter-amqp-test,spring-boot-starter-artemis,spring-boot-starter-artemis-test,spring-boot-starter-aspectj,spring-boot-starter-aspectj-test,spring-boot-starter-batch,spring-boot-starter-batch-jdbc,spring-boot-starter-batch-jdbc-test,spring-boot-starter-batch-test,spring-boot-starter-cache,spring-boot-starter-cache-test,spring-boot-starter-cassandra,spring-boot-starter-cassandra-test,spring-boot-starter-classic,spring-boot-starter-cloudfoundry,spring-boot-starter-cloudfoundry-test,spring-boot-starter-couchbase,spring-boot-starter-couchbase-test,spring-boot-starter-data-cassandra,spring-boot-starter-data-cassandra-test,spring-boot-starter-data-cassandra-reactive,spring-boot-starter-data-cassandra-reactive-test,spring-boot-starter-data-couchbase,spring-boot-starter-data-couchbase-test,spring-boot-starter-data-couchbase-reactive,spring-boot-starter-data-couchbase-reactive-test,spring-boot-starter-data-elasticsearch,spring-boot-starter-data-elasticsearch-test,spring-boot-starter-data-jdbc,spring-boot-starter-data-jdbc-test,spring-boot-starter-data-jpa,spring-boot-starter-data-jpa-test,spring-boot-starter-data-ldap,spring-boot-starter-data-ldap-test,spring-boot-starter-data-mongodb,spring-boot-starter-data-mongodb-test,spring-boot-starter-data-mongodb-reactive,spring-boot-starter-data-mongodb-reactive-test,spring-boot-starter-data-neo4j,spring-boot-starter-data-neo4j-test,spring-boot-starter-data-r2dbc,spring-boot-starter-data-r2dbc-test,spring-boot-starter-data-redis,spring-boot-starter-data-redis-test,spring-boot-starter-data-redis-reactive,spring-boot-starter-data-redis-reactive-test,spring-boot-starter-data-rest,spring-boot-starter-data-rest-test,spring-boot-starter-elasticsearch,spring-boot-starter-elasticsearch-test,spring-boot-starter-flyway,spring-boot-starter-flyway-test,spring-boot-starter-freemarker,spring-boot-starter-freemarker-test,spring-boot-starter-graphql,spring-boot-starter-graphql-test,spring-boot-starter-groovy-templates,spring-boot-starter-groovy-templates-test,spring-boot-starter-gson,spring-boot-starter-gson-test,spring-boot-starter-hateoas,spring-boot-starter-hateoas-test,spring-boot-starter-hazelcast,spring-boot-starter-hazelcast-test,spring-boot-starter-integration,spring-boot-starter-integration-test,spring-boot-starter-jackson,spring-boot-starter-jackson-test,spring-boot-starter-jdbc,spring-boot-starter-jdbc-test,spring-boot-starter-jersey,spring-boot-starter-jersey-test,spring-boot-starter-jetty,spring-boot-starter-jetty-runtime,spring-boot-starter-jms,spring-boot-starter-jms-test,spring-boot-starter-jooq,spring-boot-starter-jooq-test,spring-boot-starter-json,spring-boot-starter-jsonb,spring-boot-starter-jsonb-test,spring-boot-starter-kafka,spring-boot-starter-kafka-test,spring-boot-starter-kotlinx-serialization-json,spring-boot-starter-kotlinx-serialization-json-test,spring-boot-starter-ldap,spring-boot-starter-ldap-test,spring-boot-starter-liquibase,spring-boot-starter-liquibase-test,spring-boot-starter-log4j2,spring-boot-starter-logback,spring-boot-starter-logging,spring-boot-starter-mail,spring-boot-starter-mail-test,spring-boot-starter-micrometer-metrics,spring-boot-starter-micrometer-metrics-test,spring-boot-starter-mongodb,spring-boot-starter-mongodb-test,spring-boot-starter-mustache,spring-boot-starter-mustache-test,spring-boot-starter-neo4j,spring-boot-starter-neo4j-test,spring-boot-starter-oauth2-authorization-server,spring-boot-starter-oauth2-client,spring-boot-starter-oauth2-resource-server,spring-boot-starter-opentelemetry,spring-boot-starter-opentelemetry-test,spring-boot-starter-pulsar,spring-boot-starter-pulsar-test,spring-boot-starter-quartz,spring-boot-starter-quartz-test,spring-boot-starter-r2dbc,spring-boot-starter-r2dbc-test,spring-boot-starter-reactor-netty,spring-boot-starter-restclient,spring-boot-starter-restclient-test,spring-boot-starter-rsocket,spring-boot-starter-rsocket-test,spring-boot-starter-security,spring-boot-starter-security-test,spring-boot-starter-security-oauth2-authorization-server,spring-boot-starter-security-oauth2-authorization-server-test,spring-boot-starter-security-oauth2-client,spring-boot-starter-security-oauth2-client-test,spring-boot-starter-security-oauth2-resource-server,spring-boot-starter-security-oauth2-resource-server-test,spring-boot-starter-security-saml2,spring-boot-starter-security-saml2-test,spring-boot-starter-sendgrid,spring-boot-starter-sendgrid-test,spring-boot-starter-session-data-redis,spring-boot-starter-session-data-redis-test,spring-boot-starter-session-jdbc,spring-boot-starter-session-jdbc-test,spring-boot-starter-test,spring-boot-starter-test-classic,spring-boot-starter-thymeleaf,spring-boot-starter-thymeleaf-test,spring-boot-starter-tomcat,spring-boot-starter-tomcat-runtime,spring-boot-starter-validation,spring-boot-starter-validation-test,spring-boot-starter-web,spring-boot-starter-web-services,spring-boot-starter-webclient,spring-boot-starter-webclient-test,spring-boot-starter-webflux,spring-boot-starter-webflux-test,spring-boot-starter-webmvc,spring-boot-starter-webmvc-test,spring-boot-starter-webservices,spring-boot-starter-webservices-test,spring-boot-starter-websocket,spring-boot-starter-websocket-test,spring-boot-starter-zipkin,spring-boot-test,spring-boot-test-autoconfigure,spring-boot-test-classic-modules,spring-boot-testcontainers,spring-boot-thymeleaf,spring-boot-tomcat,spring-boot-transaction,spring-boot-validation,spring-boot-web-server,spring-boot-webclient,spring-boot-webclient-test,spring-boot-webflux,spring-boot-webflux-test,spring-boot-webmvc,spring-boot-webmvc-test,spring-boot-webservices,spring-boot-webservices-test,spring-boot-websocket,spring-boot-webtestclient,spring-boot-zipkin,spring-boot-starter-zipkin-test

but they didn't. Why is that? Are you saying what Spring Boot is doing "limit end apps flexibility & choice"?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants