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.

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

Labels

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants