Skip to content

Allow major and minor versions for bootVersion parameter - #1797

Merged
mhalbritter merged 2 commits into
spring-io:mainfrom
YangSiJun528:feat/gh-1479
Aug 14, 2026
Merged

Allow major and minor versions for bootVersion parameter#1797
mhalbritter merged 2 commits into
spring-io:mainfrom
YangSiJun528:feat/gh-1479

Conversation

@YangSiJun528

Copy link
Copy Markdown
Contributor

This PR adds support for resolving Spring Boot version wildcards for the bootVersion request parameter.

This allows values such as 4.x.x and 4.0.x to resolve to the latest matching Spring Boot version.

Changes

  • Add latest-version wildcard resolution to VersionParser.
  • Add VersionCapability to parse metadata-backed versions against configured values.
  • Update DefaultProjectRequestToDescriptionConverter and ProjectMetadataController to resolve bootVersion through metadata-backed version parsing.

Notes

VersionParser#parseLatest matches qualifiers exactly because it is a generic version parser that also has to handle snapshots and other qualified versions.

Spring Boot used versions such as 2.3.10.RELEASE before its versioning scheme change. Since current GA versions no longer use RELEASE, values such as 4.x.x and 4.0.x resolve as expected with the current metadata.

Closes gh-1479

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 16, 2026
@mhalbritter mhalbritter self-assigned this Aug 14, 2026
@mhalbritter mhalbritter changed the title Resolve bootVersion wildcards from metadata versions Allow major and minor versions for bootVersion parameter Aug 14, 2026
@mhalbritter mhalbritter added type: enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Aug 14, 2026
YangSiJun528 and others added 2 commits August 14, 2026 11:39
See spring-iogh-1797

Signed-off-by: sijun-yang <yangsijun5528@gmail.com>
Resolve the abbreviated forms in VersionParser.resolveLatest, next to
parse, and accept `4` and `4.0` as well. The latest general availability
version wins, so milestones and snapshots are ignored.

parse is left alone on purpose. Its `x` support was added in spring-iogh-328 for
compatibility ranges in the metadata, where an unresolved wildcard becomes
a 999 sentinel so that a range keeps covering a whole line. That is a
reasonable bound, but a fabricated coordinate for a request, and since
Version.parse has no latest versions to resolve against it was the only
possible outcome there: bootVersion=2.3.x used to generate a project with
parent 2.3.999. resolveLatest returns null instead, so the request is
rejected with a 400.

When a resolved version falls outside the platform compatibility range,
/dependencies now names that version in the error rather than the
abbreviated form that was requested.

Version numbers are now capped at nine digits, turning an oversized
bootVersion into a 400 rather than a NumberFormatException.

See spring-iogh-1797
@mhalbritter

Copy link
Copy Markdown
Contributor

Thanks for the PR! I like the idea and the shape of it, so I've taken it with some polish on top. Here's what changed and why, so nothing is a surprise.

Kept from your PR

Resolution of abbreviated bootVersion values against the versions the service actually offers, wired into both /starter.* and /dependencies, and the decision to leave VersionParser.parse alone rather than change what x means there.

What the polish changes

VersionCapability is gone. Resolution now lives in VersionParser.resolveLatest, next to parse, and InitializrMetadata.resolveBootVersion exposes it to callers. InitializrMetadata already owns bootVersions and already builds a VersionParser in updateSpringBootVersions, so it was the natural home and it saves a type.

parse is untouched, on purpose. Its x support was added in gh-328 for compatibility ranges in the metadata, where an unresolved wildcard becomes a 999 sentinel so a range keeps covering a whole release line. That is a reasonable upper bound for a range, but a fabricated coordinate for a request — and since Version.parse has no latest versions to resolve against, it was the only possible outcome there. That is the actual bug in gh-1479: bootVersion=2.3.x generated a project with parent 2.3.999. resolveLatest returns null instead, so the request is rejected with a 400.

4 and 4.0 are accepted too, not just 4.x.x and 4.0.x. gh-1479 explicitly asks for bootVersion==3, so the fully abbreviated form should work. 4 and 4.x.x are equivalent, as are 4.0 and 4.0.x.

The latest general availability version wins. Milestones and snapshots are ignored, via a new Version.isGeneralAvailability(). A tutorial URL silently landing on a milestone would be worse than a 400. The consequence is worth stating plainly: during a pre-release window, when the service offers 4.2.0-M1 but no 4.2.0 yet, bootVersion=4.2.x is a 400, and bootVersion=4 returns the newest 4.1 GA rather than the 4.2 milestone.

To your note about RELEASEisGeneralAvailability() treats both an absent qualifier and the legacy RELEASE qualifier as GA, so a service still offering 1.5.21.RELEASE/1.5.22.RELEASE resolves 1.x.x to 1.5.22.RELEASE. It isn't limited to the post-2020 scheme.

Only trailing numbers can be x, and an abbreviated form cannot carry a qualifier. 4.x.3, 4.0.x-M1 and 4.x.x-SNAPSHOT are rejected rather than falling back to a 999 coordinate — there is no "latest" to resolve a qualified wildcard against. Anything else is parsed as is, so 4.0.2-M1 still selects that exact version.

/dependencies names the resolved version when it is out of range. Previously the error echoed the abbreviated form that was requested, which told the caller nothing about why it was rejected.

Version numbers are capped at nine digits. An oversized bootVersion was reaching Integer.parseInt and surfacing as a NumberFormatException, i.e. a 500. It is now an InvalidVersionException, so a 400.

Docs. configuration-guide.adoc documents the accepted forms, the GA-only rule, the rejected forms, and both 400 cases.

Behaviour, against a local instance

Offering 4.1.1-SNAPSHOT, 4.1.0, 4.0.8-SNAPSHOT, 4.0.7, with a >=4.0.0 compatibility range:

bootVersion Result
4, 4.x, 4.x.x 4.1.0
4.0, 4.0.x 4.0.7
4.1, 4.1.x 4.1.0
4.0.7, 4.1.1-SNAPSHOT as requested
4.2.x, 5, 3.x.x 400, no matching version
4.x.3, 4.0.x-M1, 4.x.x-SNAPSHOT 400, wildcard cannot be resolved
99999999999999, nope, empty 400

/dependencies resolves identically and reports the resolved version in its payload. Note that the wildcard is x only and case-sensitive: 4.0.X and 4.0.* are 400s.

Deliberately unchanged

A version that parses but is not offered by the service is still accepted if it falls inside the compatibility range — 4.0.6 above generates a project. That is long-standing behaviour and narrowing it here would break existing callers, so it stays. Wildcards cannot reach that path in any case, since they only ever resolve to a version present in the metadata.

@mhalbritter
mhalbritter merged commit 797a9a0 into spring-io:main Aug 14, 2026
2 of 3 checks passed
@mhalbritter mhalbritter added this to the 0.25.0 milestone Aug 14, 2026
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.

CLI should allow major and minor versions for bootVersion parameter

3 participants