Skip to content

fix(core): a bare NuGet Version reports UpToDate whatever the registry publishes #113

Description

@justin13888

dependable check on a *.csproj whose PackageReference carries a bare
Version="13.0.1" reports UpToDate no matter what NuGet publishes. It cannot
ever report an available update for that shape, which is the ordinary shape of a
C# manifest.

Mechanism

nuget_constraint_to_semver (crates/dependable-core/src/semver/nuget.rs) reads a
bare version as an inclusive minimum:

// A bare version is an inclusive minimum in NuGet.
nuget_to_semver(c).map_or_else(String::new, |v| format!(">={v}"))

So Version="13.0.1" becomes >=13.0.1. That string is what
evaluate_item hands to check_version
(crates/dependable-fetch/src/check.rs, the to_semver_constraint(&item.version_constraint, ecosystem)
call). Inside check_version
(crates/dependable-core/src/semver/checker.rs):

  • latest_compatible is the newest published version the requirement matches. A
    >= lower bound matches every later release, so latest_compatible == latest_available.
  • There is no NuGet lockfile reader in the tree at all (no packages.lock.json
    parser, no LockfileKind for one), so locked_at is always None for a C#
    item and current falls back to latest_compatible.
  • The status arm is then Some(cur) if *cur >= latest_available => DependencyStatus::UpToDate, and it is taken unconditionally.

The result does not depend on the registry: whatever Newtonsoft.Json publishes,
the answer is UpToDate. --fix correspondingly never has anything to offer, and
the JSON/SARIF/report surfaces inherit the same verdict.

Why this is a defect and not a reading of NuGet

Maven's translator makes the opposite call for the identical shape.
crates/dependable-core/src/semver/maven.rs:

// A bare version is read as exact, as Hex's is.
maven_to_semver(c).map_or_else(String::new, |v| format!("={v}"))

with the stated rationale that reading a bare version as an open >= bound would
report every project as already up to date. That is exactly the outcome NuGet has
today. The argument was made in maven.rs and was never made in nuget.rs.

Either reading is defensible as NuGet resolution semantics — MSBuild does treat
a bare Version as a floor and picks the lowest release at or above it. But the
question this tool answers is "is there a newer release you could move to", and
under >= that question is unanswerable by construction.

Blast radius, and why it was not folded into #107

Changing nuget_constraint_to_semver changes check, list, fix, report, and
SARIF output for every C# user in one step:

  • every bare-Version PackageReference flips from UpToDate to whatever the
    registry actually says, which for most real projects means UpdateAvailable;
  • --fix starts offering rewrites of lines it has never touched;
  • exit codes on existing CI pipelines change accordingly.

That is a deliberate, user-visible behaviour change and wants its own PR and its
own note. #107 was about what a tree node reports, so it took the narrow route
instead: it recognizes an exact pin only where the ecosystem's existing
translator already reads one (Cargo =1.2.3, PEP 440 ==2.28.1, NuGet [1.2.3],
a bare Maven or Hex version), and deliberately leaves a bare NuGet Version
reporting an unknown version so that tree and check cannot disagree about the
same line of the same file. Closing this issue is what would let a bare
<PackageReference Version="1.2.3" /> report its version in tree as well.

Suggested shape

  • Make nuget_constraint_to_semver translate a fully specified bare version to
    ={v}, keeping >= only where the bare version is partial (1, 1.2), if
    that distinction is wanted.
  • Cover it with a check_version test that fails on the current behaviour: a bare
    13.0.1 against a published 13.0.3 must not be UpToDate.
  • Re-check the fix.rs wildcard guard alongside it — issue 92 records the same
    bare-version-is-exact question from the rewrite side and states NuGet's current
    reading as settled, so the two need to agree afterwards.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions