Maven BOM support: import BOMs, and write dependencies without a version - #685
Merged
Conversation
Completes the BOM feature. A dependency can now be written without a version (org:name, org::name); the version is supplied by a Maven BOM the project (or a project it dependsOn) imports. Dep.parse/repr round-trip the version-less form; coursier's addBoms already fills a version-less request, so resolution needed nothing. The Maven importer now preserves the author's intent: a dependency written version-less in the raw pom (BOM-managed) is emitted version-less rather than frozen at the effective-pom version, so an imported build reads like the Maven one. BomIT pins both halves against a real published BOM (jackson-bom): a version-less dependency resolves to the BOM's version, and that constraint travels along dependsOn so a library's BOM governs a consumer that declares none. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtKgAYQtNSjkyDWVb4UyTr
Flips every 'no BOM / dependencyManagement not yet / inlined at import' claim across the docs and site, and gives dependencies.mdx a proper 'BOMs and version-less dependencies' section: the boms: field, writing dependencies without a version, first-wins/self-declared-wins rules, and how a BOM travels along dependsOn to govern a whole subtree. The import demo now says BOMs are extracted to boms: with managed versions stripped; the Spring Boot tutorial shows the spring-boot-dependencies BOM. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtKgAYQtNSjkyDWVb4UyTr
The DependencyShort pattern required a trailing :version, so a version-less org:name / org::name (now valid, resolved from boms:) failed IDE schema validation. Make the :version optional and document why. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtKgAYQtNSjkyDWVb4UyTr
…front A version-less dependency (org:name) resolves only if a BOM supplies its version. When NO BOM is imported, nothing possibly can, so catch that up front and fail with an actionable message naming the coordinates and the fix, instead of letting coursier retry an empty-version fetch and report a cryptic `...:"" not found`. Deliberately only the no-BOM case. The authority on what a BOM manages is coursier's own addBoms, not bleep's BomPins re-derivation (which can miss, e.g., a version written through a property it cannot interpolate) — so when BOMs are present we defer to coursier rather than risk falsely rejecting a valid build. BomIT covers the no-BOM failure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtKgAYQtNSjkyDWVb4UyTr
When collapsing an exploded build back to YAML, a cross-project whose config
is fully inherited from an extended cross-template could be written as an
explicit empty `cross: {jvm211: {}}` entry — noise for a cross id the template
already provides. The boms-support model changes shifted a collapse result
just enough to surface this on the scalameta rewrite snapshot (23 such
entries), which is why it was band-aided with a snapshot regeneration.
Fix it at the source: in BuildRewrite.withProjects, drop an empty cross entry
when an extended template (transitively) already provides that cross id. This
is round-trip safe — the project still cross-builds the id at the template's
config, so no cross id is ever lost — and the rewrite output is now
byte-identical to before the boms field, so the snapshot regeneration is
reverted.
Also add TemplateTest assertions for the template-collapse bug family: a
dependency shared across two projects lands in a common template (not dropped,
not force-inlined) and survives the round-trip; a single-project dependency
stays put; cross-projects sharing config collapse without empty cross entries.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtKgAYQtNSjkyDWVb4UyTr
CoursierResolver.Params gained a `boms` field with a forProduct5 codec that made it required on decode. Every resolve cache written before the field — all the committed snapshot-test caches, and any user's on-disk cache — then failed to load with "DecodingFailure ... params.boms: Missing required field". Decode absent-as-empty: an entry written before the field carried no boms, which is exactly the empty set. The encoder still writes the field, so new caches round-trip. No cache regeneration needed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtKgAYQtNSjkyDWVb4UyTr
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds first-class Maven BOM (
dependencyManagementimport) support — the one piece of Maven the docs used to call out as missing.What you can now do
Declare BOMs on a project and write dependencies without a version; the BOM supplies it:
boms:applies MavendependencyManagement-import semantics to every resolution the project participates in: transitives are pinned, sibling BOMs are first-wins, and a version you write yourself still wins over the BOM.org:name(Java) /org::name(Scala) — round-trip through the model and resolve from the BOM.Dep.parse/reprhandle the new form; coursier'saddBomsalready fills a version-less request.dependsOn: a platform BOM on a shared library governs every consumer, so one line pins an app's whole dependency universe (how Quarkus/Spring-Boot platforms work).bleep import-mavenextracts<dependencyManagement>scope=importentries intoboms:(read from the raw pom chain, since effective-pom flattens them away) and emits any dependency the pom wrote version-less as version-less in bleep — so the imported build reads like the Maven original instead of freezing BOM-owned versions.Also in this PR
boms:is documented, and theDependencyShortpattern now allows the version-less form (previously aorg:namedep would show an IDE validation error).docs/concepts/dependencies.mdxgains a proper BOMs and version-less dependencies section including the downstream-dependsOnflow.BomIT(3 tests, against a real published BOM): version-less resolves to the BOM's version, a library's BOM constrains a consumer that declares none, and the no-BOM case fails with the actionable error.Full build green.
🤖 Generated with Claude Code