Skip to content

Update a BOM version property declared locally but imported by a parent - #8876

Merged
sambsnyd merged 2 commits into
mainfrom
upgrade-dependency-version-bom-property
Sep 17, 2026
Merged

sambsnyd merged 2 commits into
mainfrom
upgrade-dependency-version-bom-property

Conversation

@Jammy-Louie

@Jammy-Louie Jammy-Louie commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

A pom can declare the version property that an ancestor uses to import a BOM. UpgradeDependencyVersion never updated that property, so the module silently stays on the old version.

The shape, using the published spring-cloud-starter-parent:2025.0.0 as an example:

<!-- the parent, resolved from a repository -->
<properties>
    <spring-cloud.version>2025.0.0</spring-cloud.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
<!-- the application pom, in the repository -->
<properties>
    <spring-cloud.version>2024.0.0</spring-cloud.version>  <!-- decides the version, never updated -->
</properties>

Maven resolves the inherited dependencyManagement against the child's effective properties, so the child's value is the one that picks the release train. Overriding it this way is the documented approach for Spring Cloud and Spring Boot.

What broke

The scanner only begins looking for a property once it sees a tag containing ${...}, and from there searches upward through parent poms. That covers a child referencing ${x} that a parent declares. The reverse never starts: the reference lives in the ancestor, which is not a source file, so no tag in the repository contains ${...} at all. The dependencies are versionless because the BOM manages them, and the property is a bare number in <properties> with nothing tying it to the BOM.

Three outcomes today:

  • Parent outside the repository: nothing changes at all.
  • Parent inside the repository: the parent's property is bumped and the child's override is left stale, so the repository becomes internally inconsistent while the child silently stays on the old version.
  • With overrideManagedVersion: an explicit <version> is pinned onto the dependency and the property is still left behind.

The editing visitor does not save it either. Its property branch is gated on dm.getRequestedBom() == null, so BOM-managed dependencies never reach it.

The fix

The scanner now also reads each pom's resolved dependency management, which retains the raw expression from whichever pom declared the import:

requestedBom = ManagedDependency.Imported(gav=org.springframework.boot:spring-boot-dependencies:${spring-boot.version})
bomGav       = org.springframework.boot:spring-boot-dependencies:4.0.6

When the imported BOM's own coordinates match the recipe's globs and its requested version is a property, the newer BOM version is recorded against whichever source pom declares that property: the pom being visited, or the nearest parent still in the sources via the existing storeParentPomProperty. Matching on the BOM's own coordinates keeps this scoped, so upgrading a single dependency that a BOM happens to manage does not move the BOM's version property.

Tests

UpgradeDependencyVersionTest.ImportedBomVersionProperty covers the remote parent import, a child overriding a property its in-source parent declares (both poms must reach the new version), overrideManagedVersion changing the property rather than pinning a version onto the dependency, and the negative case where the BOM's coordinates do not match and the property must be left alone. The first three fail before this change.

@Jammy-Louie
Jammy-Louie force-pushed the upgrade-dependency-version-bom-property branch from 847c60b to 5e16c76 Compare September 14, 2026 15:02
@Jammy-Louie
Jammy-Louie marked this pull request as ready for review September 14, 2026 15:13
@kdelay

kdelay commented Sep 17, 2026

Copy link
Copy Markdown

PomProperty has propertyValue in equals/hashCode and pomProperties is a HashSet, while the applying visitor breaks on the first entry matching (path, name). Previously only the isDependencyTag branch wrote there; storeImportedBomVersionProperty now writes from resolved dependency management keyed on the BOM. With a glob recipe (org.junit*/*) over a multi-module build, the BOM and a directly-versioned artifact can share one property and resolve to different versions, so the winner depends on hash order. Key by path+name instead?

@sambsnyd

Copy link
Copy Markdown
Member

That's a good point @kdelay !

…ut don't have the exact same set of published versions
@sambsnyd
sambsnyd merged commit 19c8dfa into main Sep 17, 2026
1 check passed
@sambsnyd
sambsnyd deleted the upgrade-dependency-version-bom-property branch September 17, 2026 07:19
@github-project-automation github-project-automation Bot moved this from In Progress to Done in OpenRewrite Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants