Skip to content

bump: order npm cooldown versions by semver - #24097

Open
hyuraku wants to merge 1 commit into
Homebrew:mainfrom
hyuraku:npm-cooldown-semver
Open

hyuraku wants to merge 1 commit into
Homebrew:mainfrom
hyuraku:npm-cooldown-semver

Conversation

@hyuraku

@hyuraku hyuraku commented Sep 26, 2026

Copy link
Copy Markdown
Contributor

brew bump skips npm releases published within the last day. Its cooldown check uses Version, which incorrectly ranks 1.2.3-next.1 above 1.2.3. This can leave no eligible version; brew bump then falls back to latest and silently bypasses the cooldown. The npm code also mistakes build metadata such as 1.2.3+build-1 for a prerelease because it checks for a hyphen.

Use Vulns::Semver for npm version precedence and prerelease detection. Invalid semver versions still fall back to Version; PyPI and RubyGems behavior is unchanged.


  • Have you followed our Contributing guidelines?
  • Have you checked for other open Pull Requests for the same change?
  • Have you explained what your changes do? Performance claims (e.g. "this is faster") must include brew benchmark results.
  • Have you explained why you'd like these changes included, not just what they do?
  • For bug fixes, have you given step-by-step brew commands to reproduce the bug?
  • Have you written new tests (excluding integration tests)? Here's an example.
  • Have you successfully run brew lgtm (style, typechecking and tests) locally?

  • I did not use AI/LLM to create this PR, or I disclosed the tool/model below and reviewed its output; I did not attribute commits to AI and will answer maintainer questions and review comments myself without AI/LLM.

AI (Claude Code, Opus 5) assisted in tracking down the cause of the TODO and in writing the change and the tests. I reviewed the change and ran brew lgtm locally.

Copilot AI lite review requested due to automatic review settings September 26, 2026 08:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

馃煛 Changes recommended

The npm SemVer logic can still be bypassed by the earlier Version guard.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
What changed in this PR

Updates npm cooldown version handling to use SemVer precedence and correct prerelease detection.

Changes:

  • Adds Vulns::Semver.prerelease?.
  • Applies SemVer comparisons to npm cooldown candidates.
  • Adds SemVer and npm cooldown regression tests.

A moderate issue remains: the earlier Version guard can bypass the npm-specific SemVer logic.

File Summary
Library/鈥婬omebrew/鈥媣ulns/鈥媠emver.rb Adds SemVer prerelease detection.
Library/鈥婬omebrew/鈥媡est/鈥媣ulns/鈥媠emver_spec.rb Tests prerelease and build metadata handling.
Library/鈥婬omebrew/鈥媡est/鈥媎ev-cmd/鈥媌ump_spec.rb Tests npm prerelease cooldown ordering.
Library/鈥婬omebrew/鈥媎ev-cmd/鈥媌ump.rb Updates npm cooldown filtering to use SemVer.

馃挕 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.


current_str = current.to_s
current_is_prerelease = current_str.include?("-")
latest_str = latest.to_s
# @param right the version to compare against
sig { params(left: String, right: String).returns(Integer) }
def semver_compare(left, right)
Vulns::Semver.compare(left, right) || (Version.new(left) <=> Version.new(right)) || 0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's not use Vulns for this; Homebrew's Version class is better here I think but CC @p-linnane @andrew for thoughts

@andrew andrew left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The bug reproduces against Version on main:

1.2.3-next.1 <=> 1.2.3    =  1
1.2.4-next.2 <=> 1.2.4    =  1
1.2.3+build-1 <=> 1.2.3   =  1
1.2.3-alpha.1 <=> 1.2.3   = -1

Version orders the four prerelease keywords it has tokens for (alpha, beta, pre, rc) correctly and ranks every other npm dist-tag identifier such as next or canary, plus all build metadata, above the release. Adding semver precedence to Version itself would reorder every formula, so the npm path needs a separate comparator.

@MikeMcQuaid on the namespace: agreed that bump should take its comparator from core rather than from Vulns. vulns/repology.rb requires utils/repology.rb, so the dependency direction through vulns/ is vulns to core, and this would be the first dependency going the other way. Instead of reverting to Version, move Library/Homebrew/vulns/semver.rb to Library/Homebrew/semver.rb as Homebrew::Semver, update the call sites in vulns/vulnerability.rb and move the spec across. Behaviour stays the same and bump depends only on core.

Two places either side of the npm loop still compare with Version:

  • return if latest <= current in version_with_cooldown. With current at 1.2.3-next.1 and npm latest at 1.2.3 this returns before the loop runs and the caller falls back to latest, bypassing the cooldown this PR fixes. The guard requires the semver comparison for the Npm strategy, so it moves below the case strategy dispatch or compares per strategy.
  • cooldown_skipped = (latest if cooldown_version && cooldown_version < latest) in livecheck_result. In the scenario the new test covers (current 1.2.3-next.1, latest 1.2.4, returning 1.2.4-next.2) Version ranks 1.2.4-next.2 above 1.2.4, so cooldown_skipped is nil and brew bump prints 1.2.4-next.2 as the candidate while omitting the cooldown hold on 1.2.4. The previous code skipped 1.2.4-next.2 and returned current, so the report was correct. Either that comparison takes the same comparator or the npm branch returns the skipped version alongside the chosen one, with a test entering through retrieve_versions_by_arch.

One smaller point is that semver_compare ends || 0, so when both the semver parse and Version#<=> fail the two versions compare equal, both next guards fall through and the unparseable version is returned as a bump candidate. The previous code skipped it, so returning nil and skipping the candidate restores that.

@MikeMcQuaid

Copy link
Copy Markdown
Member

``, move Library/Homebrew/vulns/semver.rb to `Library/Homebrew/semver.rb` as `Homebrew::Semver`, update the call sites in `vulns/vulnerability.rb` and move the spec across. Behaviour stays the same and `bump` depends only on core.

Agreed, makes sense to me.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants