mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-11 14:27:45 +01:00
dep_freshness: accept Cargo's =x.y.z as the exact pin it is
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
Cargo's bare `"0.19.1"` is a caret range; its exact operator is a single `=`. The shared EXACT regex only knew pip's `==`, so the one form that is actually an exact pin in Cargo was reported as "not exact-pinned" against itself (platformer/proto-bevy, 2026-09-06). Strip the operator in the Rust parser; the constraint string keeps it so the report shows what is written. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
347d31ce4f
commit
92d7151bdf
@ -4,11 +4,14 @@ A dependency is either a bare string (`serde = "1.0.229"`) or a table with a
|
|||||||
`version` key; path and git dependencies have no registry version and are
|
`version` key; path and git dependencies have no registry version and are
|
||||||
skipped. Cargo's bare `"1.0"` is a caret range, so only a full three-part
|
skipped. Cargo's bare `"1.0"` is a caret range, so only a full three-part
|
||||||
version counts as pinned — `"1.0"` is reported unpinned, which is correct.
|
version counts as pinned — `"1.0"` is reported unpinned, which is correct.
|
||||||
|
Cargo's own exact operator is a single `=` (`"=1.0.229"`), which the shared
|
||||||
|
EXACT regex does not know (it spells pip's `==`), so it is stripped here.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import tomllib
|
import tomllib
|
||||||
|
|
||||||
from dep_freshness._tables import CARGO
|
from dep_freshness._tables import CARGO
|
||||||
@ -40,12 +43,18 @@ def parse(path: Path) -> list[Dep]:
|
|||||||
constraint = str(spec)
|
constraint = str(spec)
|
||||||
if not constraint:
|
if not constraint:
|
||||||
continue
|
continue
|
||||||
pinned = exact_pin(constraint)
|
pinned = exact_pin(constraint.strip().removeprefix("="))
|
||||||
if pinned and pinned.count(".") < 2:
|
if pinned and pinned.count(".") < 2:
|
||||||
pinned = None # "1.0" is a caret range in Cargo, not a pin
|
pinned = None # "1.0" is a caret range in Cargo, not a pin
|
||||||
deps.append(Dep(
|
deps.append(
|
||||||
ecosystem=CARGO, name=str(name), constraint=constraint,
|
Dep(
|
||||||
path=path, line=lines.get(str(name), 0), pinned=pinned,
|
ecosystem=CARGO,
|
||||||
dev=is_dev,
|
name=str(name),
|
||||||
))
|
constraint=constraint,
|
||||||
|
path=path,
|
||||||
|
line=lines.get(str(name), 0),
|
||||||
|
pinned=pinned,
|
||||||
|
dev=is_dev,
|
||||||
|
)
|
||||||
|
)
|
||||||
return deps
|
return deps
|
||||||
|
|||||||
@ -63,7 +63,7 @@ def test_a_missing_lockfile_is_a_library_not_a_violation(tmp_path):
|
|||||||
|
|
||||||
def test_pubspec_reads_the_lockfile_when_one_is_committed(tmp_path):
|
def test_pubspec_reads_the_lockfile_when_one_is_committed(tmp_path):
|
||||||
write(tmp_path, "pubspec.yaml", PUBSPEC)
|
write(tmp_path, "pubspec.yaml", PUBSPEC)
|
||||||
write(tmp_path, "pubspec.lock", "packages:\n http:\n version: \"1.5.0\"\n")
|
write(tmp_path, "pubspec.lock", 'packages:\n http:\n version: "1.5.0"\n')
|
||||||
deps = _by_name(pubspec.parse(tmp_path / "pubspec.yaml"))
|
deps = _by_name(pubspec.parse(tmp_path / "pubspec.yaml"))
|
||||||
assert deps["http"].locked == "1.5.0"
|
assert deps["http"].locked == "1.5.0"
|
||||||
|
|
||||||
@ -84,7 +84,8 @@ def test_requirements_bare_names_parse_as_unpinned(tmp_path):
|
|||||||
|
|
||||||
def test_requirements_handles_extras_markers_and_pins(tmp_path):
|
def test_requirements_handles_extras_markers_and_pins(tmp_path):
|
||||||
path = write(
|
path = write(
|
||||||
tmp_path, "requirements.txt",
|
tmp_path,
|
||||||
|
"requirements.txt",
|
||||||
'ruff==0.16.5\nuvicorn[standard]==0.30.0\nfoo>=1.0 ; python_version>"3.9"\n',
|
'ruff==0.16.5\nuvicorn[standard]==0.30.0\nfoo>=1.0 ; python_version>"3.9"\n',
|
||||||
)
|
)
|
||||||
deps = {d.name: d for d in python.parse_requirements(path)}
|
deps = {d.name: d for d in python.parse_requirements(path)}
|
||||||
@ -94,7 +95,10 @@ def test_requirements_handles_extras_markers_and_pins(tmp_path):
|
|||||||
|
|
||||||
|
|
||||||
def test_pyproject_reads_project_and_group_dependencies(tmp_path):
|
def test_pyproject_reads_project_and_group_dependencies(tmp_path):
|
||||||
path = write(tmp_path, "pyproject.toml", """\
|
path = write(
|
||||||
|
tmp_path,
|
||||||
|
"pyproject.toml",
|
||||||
|
"""\
|
||||||
[project]
|
[project]
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = ["httpx==0.28.1"]
|
dependencies = ["httpx==0.28.1"]
|
||||||
@ -102,7 +106,8 @@ dependencies = ["httpx==0.28.1"]
|
|||||||
dev = ["pytest==8.4.2"]
|
dev = ["pytest==8.4.2"]
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
lint = ["ruff==0.16.5"]
|
lint = ["ruff==0.16.5"]
|
||||||
""")
|
""",
|
||||||
|
)
|
||||||
deps = {d.name: d for d in python.parse_pyproject(path)}
|
deps = {d.name: d for d in python.parse_pyproject(path)}
|
||||||
assert deps["httpx"].pinned == "0.28.1"
|
assert deps["httpx"].pinned == "0.28.1"
|
||||||
assert deps["pytest"].pinned == "8.4.2"
|
assert deps["pytest"].pinned == "8.4.2"
|
||||||
@ -123,18 +128,21 @@ def test_a_package_manager_without_a_version_is_ignored(tmp_path):
|
|||||||
|
|
||||||
|
|
||||||
def test_peer_dependencies_are_marked_as_such(tmp_path):
|
def test_peer_dependencies_are_marked_as_such(tmp_path):
|
||||||
path = write(tmp_path, "package.json",
|
path = write(tmp_path, "package.json", '{"peerDependencies": {"react": ">=19"}}')
|
||||||
'{"peerDependencies": {"react": ">=19"}}')
|
|
||||||
assert javascript.parse_package_json(path)[0].peer
|
assert javascript.parse_package_json(path)[0].peer
|
||||||
|
|
||||||
|
|
||||||
def test_package_json_reads_engines_and_overrides(tmp_path):
|
def test_package_json_reads_engines_and_overrides(tmp_path):
|
||||||
path = write(tmp_path, "package.json", """\
|
path = write(
|
||||||
|
tmp_path,
|
||||||
|
"package.json",
|
||||||
|
"""\
|
||||||
{"engines": {"node": "24.20.0"},
|
{"engines": {"node": "24.20.0"},
|
||||||
"dependencies": {"react": "19.2.0", "local": "workspace:*"},
|
"dependencies": {"react": "19.2.0", "local": "workspace:*"},
|
||||||
"devDependencies": {"vitest": "^4.1.10"},
|
"devDependencies": {"vitest": "^4.1.10"},
|
||||||
"resolutions": {"semver": "7.7.1"}}
|
"resolutions": {"semver": "7.7.1"}}
|
||||||
""")
|
""",
|
||||||
|
)
|
||||||
deps = {d.name: d for d in javascript.parse_package_json(path)}
|
deps = {d.name: d for d in javascript.parse_package_json(path)}
|
||||||
assert deps["node"].pinned == "24.20.0"
|
assert deps["node"].pinned == "24.20.0"
|
||||||
assert deps["react"].pinned == "19.2.0"
|
assert deps["react"].pinned == "19.2.0"
|
||||||
@ -144,18 +152,28 @@ def test_package_json_reads_engines_and_overrides(tmp_path):
|
|||||||
|
|
||||||
|
|
||||||
def test_cargo_treats_a_two_part_version_as_a_range(tmp_path):
|
def test_cargo_treats_a_two_part_version_as_a_range(tmp_path):
|
||||||
path = write(tmp_path, "Cargo.toml", """\
|
path = write(
|
||||||
|
tmp_path,
|
||||||
|
"Cargo.toml",
|
||||||
|
"""\
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = "1.0.229"
|
serde = "1.0.229"
|
||||||
loose = "1.0"
|
loose = "1.0"
|
||||||
local = { path = "../x" }
|
local = { path = "../x" }
|
||||||
tabled = { version = "0.20.0" }
|
tabled = { version = "0.20.0" }
|
||||||
""")
|
bevy = "=0.19.1"
|
||||||
|
exact_table = { version = "=1.2.3", features = ["x"] }
|
||||||
|
""",
|
||||||
|
)
|
||||||
deps = {d.name: d for d in rust.parse(path)}
|
deps = {d.name: d for d in rust.parse(path)}
|
||||||
assert deps["serde"].pinned == "1.0.229"
|
assert deps["serde"].pinned == "1.0.229"
|
||||||
assert deps["loose"].pinned is None
|
assert deps["loose"].pinned is None
|
||||||
assert deps["tabled"].pinned == "0.20.0"
|
assert deps["tabled"].pinned == "0.20.0"
|
||||||
assert "local" not in deps
|
assert "local" not in deps
|
||||||
|
# Cargo's real exact operator; the constraint keeps the `=` for the report.
|
||||||
|
assert deps["bevy"].pinned == "0.19.1"
|
||||||
|
assert deps["bevy"].constraint == "=0.19.1"
|
||||||
|
assert deps["exact_table"].pinned == "1.2.3"
|
||||||
|
|
||||||
|
|
||||||
def test_fvmrc_channel_is_not_a_version(tmp_path):
|
def test_fvmrc_channel_is_not_a_version(tmp_path):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user