Skip to content

fix(version): match a constraint with a numbered pre-release - #150

Open
roxblnfk wants to merge 5 commits into
1.xfrom
fix/exact-prerelease
Open

roxblnfk wants to merge 5 commits into
1.xfrom
fix/exact-prerelease

Conversation

@roxblnfk

@roxblnfk roxblnfk commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

What was changed

  • A constraint with a numbered pre-release now matches that release: 3.5.0-beta.1, 3.5.0-RC1, 2025.1.0-rc.2@rc, and every other stability keyword too (pre.3, preview5, nightly20250503, b2…). It used to match nothing.
  • A pre-release is a stability with its number (PreRelease), ordered by the Stability weights the minimum-stability filter already uses: nightly < … < alpha < preview < beta < pre < RC < stable, then by number. Spelling does not matter (rc.1 = RC1).
  • 3.5.0-beta.1 matches neither beta.2 nor the final 3.5.0. Ranges take the pre-release as their bound: ^3.5.0-beta.1 covers beta.1 and later up to 4.0.0; >=, >, <, <=, ~ work the same way. Composer semver still handles the version number and the operators.
  • Without @stability, the stability comes from the pre-release. A bare -beta still means a minimum stability.
  • ReleasesCollection::sortByVersion() uses the same order (Version::compare()). Its string replacement matched only the exact enum spelling, so rc.1 sorted below beta.2.

Why?

Constraint took beta.1 for a feature suffix, while Version parsed the same text as a stability and kept no suffix, so the suffix check always failed. Hit by roadrunner-php/cli#49 (VersionSelectionTest::installsExactPreRelease).

Checklist

  • Tested
    • Tested manually
    • Unit tests added
  • Documentation

@github-actions github-actions Bot added bug Something isn't working tests labels Oct 10, 2026
@codecov

codecov Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/Module/Binary/BinaryVersion.php 100.00% <100.00%> (ø)
...odule/Repository/Collection/ReleasesCollection.php 100.00% <100.00%> (ø)
src/Module/Version/Constraint.php 97.46% <100.00%> (+4.78%) ⬆️
src/Module/Version/PreRelease.php 100.00% <100.00%> (ø)
src/Module/Version/Version.php 100.00% <100.00%> (+6.52%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@roxblnfk
roxblnfk force-pushed the fix/exact-prerelease branch from 14dffed to 785e255 Compare October 10, 2026 15:56
fix(repository): sort pre-releases by stability weight and number
docs: show pre-release constraints

A constraint like `3.5.0-beta.1` took `beta.1` for a feature suffix, which no parsed version carries, so it matched nothing. A pre-release is now a stability with its number, ordered by the same `Stability` weights the minimum-stability filter uses, so every stability keyword works, `beta.1` matches neither `beta.2` nor the final release, and ranges like `^3.5.0-beta.1` work. Sorting releases uses the same order, which also fixes spellings like `rc.1` that the old string replacement missed.

Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
…tail after it

fix(version): read a version number with any count of parts
fix(binary): read every dotted part of the version a binary prints
test(version): pin the minimum stability a pre-release sets for an upper bound
docs: note that a pre-release in a constraint sets the minimum stability for any operator
refactor(version): drop the redundant pre-release parameter and narrow PreRelease::stability() to private

`3.5.0-RC1` no longer matches `3.5.0-RC1-linux` or `3.5.0-RC1.2`; range operators still accept such tails.

`1.2.3.4` is now the whole number instead of `1.2.3` with a `4` suffix, so it is stable and 4-part constraints match it; the installed binary's `--version` output is read the same way, so it satisfies such a constraint. Composer reads four parts at most, so a longer number reaches it as a patch: `1.2.3.4.5` as `1.2.3.4-p5`. A numeric suffix still orders as a part of the number only when the version has no stability keyword, which is now recorded at parse time: `1.0.0-preview.0.4` sorts before `1.0.0`.

Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
…stead of throwing

fix(version): read a pre-release glued to the number, like `2.0.0rc1`, with the whole number
test(version): cover unreadable versions and pre-releases without a separator

Now that the fourth and later number parts reach Composer, a tag like
`20250101.1.2.3` made Semver throw out of isSatisfiedBy() and abort the
download; it now just does not satisfy the constraint. A tag like
`2.0.0rc1` parsed as number `2.0`, so it missed an exact `2.0.0-rc1` and
sorted below `2.0.0-alpha`. Binary output takes a glued pre-release
keyword only, so `1.2.3_amd64` still reads as `1.2.3`.

Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
…, as sorting does

fix(version): ignore trailing zero number parts when comparing and matching versions
fix(version): stop reading a letter glued to the number, like `1.0.0x`, as a dev branch
test(version): pin mixed tails out of the number and binary output with other numbers around the version
docs: separate the pre-release stability note from the next heading; describe tail order and trailing zeros in the skill

`>3.5.0-RC1` now takes `3.5.0-RC1-linux` and `<=3.5.0-RC1` no longer does; exact
constraints still skip it. `1.2.3`, `1.2.3.0` and `1.2.3.4.0` compare as their
shortest form, and `1.2.3.4.0` satisfies `==1.2.3.4`.

Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant