Skip to content

Potential solution to Maven POM generation issue#4199

Draft
janhoy wants to merge 1 commit intoapache:mainfrom
janhoy:fix-maven-pom-bom
Draft

Potential solution to Maven POM generation issue#4199
janhoy wants to merge 1 commit intoapache:mainfrom
janhoy:fix-maven-pom-bom

Conversation

@janhoy
Copy link
Contributor

@janhoy janhoy commented Mar 9, 2026

Ref https://lists.apache.org/thread/w0tqdcjyo0wlbf7xjtc0ll9tjhwor8n2

I asked Claude to analyze and fix. This was the result.

Problem

Maven consumers of solr-api, solr-solrj, and solr-core 10.x get invalid POM errors:

[ERROR] 'dependencies.dependency.version' for com.fasterxml.jackson.core:jackson-annotations:jar is missing.
[WARNING] The POM for org.apache.solr:solr-core:jar:10.x.x is invalid, transitive dependencies (if any) will not be available

Jackson dependencies in gradle/libs.versions.toml have no pinned version — they rely on the
Jackson BOM for version resolution at Gradle build time. Gradle's maven-publish plugin faithfully
omits <version> from the generated POM for such entries, since from Gradle's perspective the BOM
import in <dependencyManagement> should cover it.

However, Maven's model validator rejects POMs where <dependencies> entries lack <version>
elements, even when a BOM import is present — particularly solr-core, which has no Jackson BOM
entry in its <dependencyManagement> at all (its internal platform entry is stripped during POM
generation).

Fix

Extend the existing pom.withXml block in gradle/maven/defaults-maven.gradle to post-process
generated POMs: after the internal platform removal, collect all Gradle-resolved artifact versions
from compileClasspath/runtimeClasspath, then inject explicit <version> elements for any
<dependency> entry that is missing one.

This uses the actual BOM-resolved versions (e.g. jackson-annotations:2.20,
jackson-core:2.20.1, jackson-databind:2.20.1) and applies centrally to all published modules
without requiring version pinning in libs.versions.toml or changes to individual build.gradle
files.

@janhoy janhoy marked this pull request as draft March 9, 2026 18:21
@janhoy janhoy requested a review from Copilot March 9, 2026 18:23
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates Solr’s Gradle Maven publishing defaults to post-process generated POMs and inject explicit <version> elements for dependencies where Gradle omits them (notably BOM-managed dependencies like Jackson), preventing “missing version” validation failures for Maven consumers.

Changes:

  • Collect resolved dependency versions from Gradle configurations used for publishing.
  • Inject missing <version> nodes into generated POM <dependencies> entries during pom.withXml processing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +191 to +195
config.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def id = artifact.moduleVersion.id
def key = "${id.group}:${id.name}"
if (!resolvedVersions.containsKey(key)) {
resolvedVersions[key] = id.version
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

config.resolvedConfiguration.resolvedArtifacts forces artifact resolution/download just to discover versions and relies on Gradle’s deprecated resolvedConfiguration API. Consider using config.incoming.resolutionResult (or config.incoming.dependencies + ResolutionResult) to read selected module versions without artifact downloads and with better forward-compatibility.

Suggested change
config.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def id = artifact.moduleVersion.id
def key = "${id.group}:${id.name}"
if (!resolvedVersions.containsKey(key)) {
resolvedVersions[key] = id.version
def resolutionResult = config.incoming.resolutionResult
resolutionResult.allComponents.each { component ->
def id = component.id
if (id instanceof org.gradle.api.artifacts.component.ModuleComponentIdentifier) {
def key = "${id.group}:${id.module}"
if (!resolvedVersions.containsKey(key)) {
resolvedVersions[key] = id.version
}

Copilot uses AI. Check for mistakes.
Comment on lines +200 to +208
root.dependencies?.dependency?.each { dep ->
def versionNodes = dep.version
if (!versionNodes || !versionNodes.text()) {
def key = "${dep.groupId.text()}:${dep.artifactId.text()}"
def resolvedVersion = resolvedVersions[key]
if (resolvedVersion) {
dep.appendNode('version', resolvedVersion)
}
}
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

If a dependency is missing <version> and resolvedVersions doesn’t contain a match, the POM will remain invalid but publishing will still succeed. It would be safer to fail the build (or at least log a warning/error) when a missing version can’t be resolved, so broken POMs aren’t published silently.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@dsmiley dsmiley left a comment

Choose a reason for hiding this comment

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

I would like to see a reference to somewhere showing this is the industry practice to the solution. Like multiple conversation threads ~recent on a platform like Gradle Forums or Stackoverflow.

I don't like all the imperative code in our Gradle build, and this adds more of that. Yeah I write my own fair share of it... but... declarative is better.

@dsmiley
Copy link
Contributor

dsmiley commented Mar 9, 2026

I wonder what smoketest / validation might verify the result?

@janhoy
Copy link
Contributor Author

janhoy commented Mar 9, 2026

Agree this may fix a symptom, not the cause. Is this related to our discussion lately where the «platform» module is not aware of BOMs and thus we needed the workaround in gradle to help it use the BOM versions?

Perhaps the proper fix is to publish a parent/platform POM for Solr to have a location for all our BOMs? Dunno.

@bram-atmire
Copy link

I wonder what smoketest / validation might verify the result?

As a downstream Maven consumer (DSpace, upgrading to Solr 10 for Spring Boot 4 compatibility), here's what we observed:

The impact is more severe than it may appear: Maven doesn't just skip the versionless Jackson dependencies. When Maven's model validator marks the POM as invalid, it drops all transitive dependencies from the affected module (~50+ for solr-core), not just the ones missing . Our workaround was to explicitly declare all solr-core transitive dependencies in our own POM, which is brittle and maintenance-heavy.

For a smoketest: a minimal Maven pom.xml that depends on the published solr-core artifact, followed by mvn dependency:tree -Dverbose, would catch this immediately. If Maven emits "The POM for org.apache.solr:solr-core:jar:10.x.x is invalid, transitive dependencies (if any) will not be available", the published POM is broken. This could be a post-publish CI step.

@janhoy
Copy link
Contributor Author

janhoy commented Mar 9, 2026

I wonder what smoketest / validation might verify the result?

Isn't it strage that Maven Central accepted our maven artifacts without warnings (or did it?).

This open JIRA https://issues.apache.org/jira/browse/SOLR-18069 is in the same alley. The suggestion is to add a step to smoke tester that downloads maven artifacts and tries to use them in a mini project with maven, which will resolve and catch many kids of potential issues I guess.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants