Skip to content

fix(core): Item::is_pinned tests the spelling of a pin, not whether it is one #118

Description

@justin13888

Item::is_pinned (crates/dependable-core/src/item.rs) decides whether fix may move a constraint. It is a spelling test:

pub fn is_pinned(&self) -> bool {
    let c = self.version_constraint.trim_start();
    c.starts_with('=') && !c.starts_with("==")
}

So a Cargo =1.2.3 is protected from a plain dependable fix, and an npm "lodash": "1.2.3" is not — even though both name exactly one release. The difference is which characters the ecosystem uses to write a pin, not whether the author pinned anything.

Why this surfaces now

#106 (refactor/92-ecosystem-aware-wildcard, shipped: the ecosystem-aware wildcard decline) formally records what a bare version means in each ecosystem, via Ecosystem::bare_version and BareVersion. It states that npm, Composer, pub, Hex and Poetry read a bare version as BareVersion::Exact — one release and no other. Ecosystem::bare_version_is_exact() is precisely the predicate that would close this gap, and is_pinned does not consult it.

That makes the inconsistency a recorded one rather than an implicit one: the repository now asserts in dependable-core that an npm 1.2.3 is an exact pin, while is_pinned continues to answer false for it.

Confirmed behaviour

Against check_version_for and is_pinned on the current tree, with versions ["1.2.3", "1.9.0"]:

constraint ecosystem lockfile status latest_compatible is_pinned plain fix
=1.2.3 Rust 1.2.3 UpdateAvailable 1.2.3 true skipped
1.2.3 Npm 1.2.3 UpdateAvailable 1.9.0 false rewritten to 1.9.0

The npm row moves because to_version_req normalizes a bare 1.2.3 to ^1.2.3 — the Rust semver crate's reading, not node-semver's — so the checker reports a 1.9.0 update against a constraint node-semver admits only 1.2.3 for. fix then takes it, because no guard in rewrite_constraint objects: a full three-component version is not a wildcard and not a partial version, and fix.rs asserts that shape stays rewritable on purpose.

So the same bare npm version is read two ways in one codebase: Exact by Ecosystem::bare_version, and ^1.2.3 by the checker that decides whether to offer an update at all.

What needs deciding

  1. Whether is_pinned should become semantic — ecosystem.bare_version_is_exact() && the constraint is a full concrete version — or whether the pin question belongs somewhere that already knows the ecosystem. Item does not currently carry one.
  2. Whether the checker's caret reading of a bare npm version is itself the bug, in which case fixing that removes the UpdateAvailable that gets fix involved, and is_pinned matters less. These are two fixes for one symptom and only one may be wanted.
  3. What --all should then do. Today it deliberately moves a Cargo =1.2.3; whatever is_pinned comes to mean, --all should keep overriding it.

Out of scope

Ranges, dist-tags, wildcards, and partial versions — #106 settled how fix treats those. This is only about a constraint that names exactly one release.

Related but distinct: #107 is about reporting an exact pin's version on a tree node rather than unknown. This issue is about protecting one from being rewritten. They share the "does this constraint admit exactly one version" question and may want a common helper.

Came out of the review of #106.

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