mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-14 16:11:00 +01:00
dep_freshness: gittag looks up tags in the dependency's own repo
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
repo-contract / repo contract (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
repo-contract / repo contract (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
A `pkg @ git+https://github.com/<owner>/<repo>@pkg-vX.Y.Z` pin outside kuhyx/utils was asked of utils, which has no such tag, so steam-backlog-enforcer's leisure-watch pin was undeterminable (exit 3). Foreign pins are now named `<host>/<owner>/<repo>:<pkg>`; utils pins keep their bare names (allowlist labels and cache keys unchanged). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dsh4Znwh2KnC7q6vbGpX1N
This commit is contained in:
parent
35f6f95956
commit
00085a7de6
@ -16,6 +16,7 @@ import tomllib
|
||||
from dep_freshness._tables import GITTAG, PYPI, TOOLCHAIN
|
||||
from dep_freshness.models import Dep
|
||||
from dep_freshness.parsers._lines import index
|
||||
from dep_freshness.registries import gittag
|
||||
from dep_freshness.versions import exact_pin
|
||||
|
||||
_REQ = re.compile(
|
||||
@ -35,7 +36,7 @@ _SKIP_PREFIXES = ("-", "#", "git+", "http://", "https://", ".", "/")
|
||||
_DIRECT = re.compile(
|
||||
r"^\s*(?P<name>[A-Za-z0-9][A-Za-z0-9._-]*)"
|
||||
r"(?:\[[^\]]*\])?"
|
||||
r"\s*@\s*git\+\S+?@(?P<ref>[A-Za-z0-9_.-]+-v[0-9][^\s#]*)"
|
||||
r"\s*@\s*git\+(?P<url>\S+?)@(?P<ref>[A-Za-z0-9_.-]+-v[0-9][^\s#]*)"
|
||||
)
|
||||
|
||||
|
||||
@ -46,8 +47,9 @@ def _direct_reference(requirement: str, path: Path, line: int) -> Dep | None:
|
||||
return None
|
||||
ref = match.group("ref")
|
||||
package, _, version = ref.rpartition("-v")
|
||||
name = gittag.qualified_name(match.group("url"), package or match.group("name"))
|
||||
return Dep(
|
||||
ecosystem=GITTAG, name=package or match.group("name"), constraint=ref,
|
||||
ecosystem=GITTAG, name=name, constraint=ref,
|
||||
path=path, line=line or _ref_line(path, ref), pinned=version or None,
|
||||
)
|
||||
|
||||
|
||||
@ -1,22 +1,53 @@
|
||||
"""Shared libraries consumed by git tag out of `kuhyx/utils`.
|
||||
"""Libraries consumed by git tag: `kuhyx/utils` by default, or their own repo.
|
||||
|
||||
"Newest" here is the newest tag matching `<package>-vX.Y.Z`, not a registry
|
||||
answer. The transport is `git ls-remote` (see `_git.py`), never the REST API.
|
||||
|
||||
A dep pinned out of kuhyx/utils keeps its bare package name -- that is the
|
||||
allowlist label and cache key every consumer already has. Any other repo is
|
||||
named `<host>/<owner>/<repo>:<package>`, so the resolver (which only ever
|
||||
sees the name) can still find the remote: asking utils for leisure-watch's
|
||||
tags answered "undeterminable" (exit 3) in steam-backlog-enforcer.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
from dep_freshness._tables import UTILS_TAG_REMOTE
|
||||
from dep_freshness.registries._git import ls_remote
|
||||
from dep_freshness.versions import newest_stable
|
||||
|
||||
_TAG = re.compile(r"refs/tags/(?P<name>[A-Za-z0-9_.-]+)-v(?P<version>[0-9][^\s]*)$")
|
||||
_UTILS_SOURCE = urlsplit(UTILS_TAG_REMOTE).netloc + urlsplit(UTILS_TAG_REMOTE).path
|
||||
|
||||
|
||||
def latest(package: str, remote: str = UTILS_TAG_REMOTE) -> str | None:
|
||||
def _source(url: str) -> str | None:
|
||||
"""`host/owner/repo` behind a git URL, or None when it cannot be read."""
|
||||
parts = urlsplit(url.strip())
|
||||
path = parts.path.strip("/").removesuffix(".git")
|
||||
if not parts.hostname or path.count("/") < 1:
|
||||
return None
|
||||
return f"{parts.hostname}/{path}"
|
||||
|
||||
|
||||
def qualified_name(url: str, package: str) -> str:
|
||||
"""The dep name for `package` pinned out of `url` (see module docstring).
|
||||
|
||||
An unreadable URL falls back to the bare name -- the pre-existing
|
||||
behaviour -- rather than a guessed remote.
|
||||
"""
|
||||
source = _source(url)
|
||||
if source is None or source.casefold() == _UTILS_SOURCE.casefold():
|
||||
return package
|
||||
return f"{source}:{package}"
|
||||
|
||||
|
||||
def latest(name: str) -> str | None:
|
||||
"""Highest semver tag prefixed with `<package>-v`, or None if there is none."""
|
||||
source, _, package = name.rpartition(":")
|
||||
remote = f"https://{source}" if source else UTILS_TAG_REMOTE
|
||||
found = []
|
||||
for line in ls_remote(remote, "--tags", "--refs"):
|
||||
match = _TAG.search(line)
|
||||
|
||||
84
dep_freshness/tests/test_gittag_source.py
Normal file
84
dep_freshness/tests/test_gittag_source.py
Normal file
@ -0,0 +1,84 @@
|
||||
"""Git-tag deps outside kuhyx/utils are looked up in THEIR repo, not utils.
|
||||
|
||||
`leisure-watch @ git+https://github.com/kuhyx/leisure-watch@leisure-watch-v0.1.0`
|
||||
was asked of kuhyx/utils, which has no `leisure-watch-v*` tag, so the answer
|
||||
came back undeterminable (exit 3) in steam-backlog-enforcer. The shared libs
|
||||
in utils must keep their bare names: those are the allowlist labels and the
|
||||
cache keys every consumer already has.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from dep_freshness._tables import UTILS_TAG_REMOTE
|
||||
from dep_freshness.parsers import python
|
||||
from dep_freshness.registries import gittag
|
||||
|
||||
LW_URL = "https://github.com/kuhyx/leisure-watch"
|
||||
REQS = f"""\
|
||||
leisure-watch @ git+{LW_URL}@leisure-watch-v0.1.0
|
||||
earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.14.0#subdirectory=earned_time
|
||||
gatelock @ git+https://github.com/kuhyx/utils.git@gatelock-v0.11.0#subdirectory=gatelock
|
||||
"""
|
||||
|
||||
|
||||
def _names(tmp_path: Path) -> dict[str, str]:
|
||||
path = tmp_path / "requirements.txt"
|
||||
path.write_text(REQS, encoding="utf-8")
|
||||
return {d.constraint: d.name for d in python.parse_requirements(path)}
|
||||
|
||||
|
||||
def test_a_foreign_repo_pin_is_qualified_by_its_source(tmp_path):
|
||||
names = _names(tmp_path)
|
||||
assert (
|
||||
names["leisure-watch-v0.1.0"] == "github.com/kuhyx/leisure-watch:leisure-watch"
|
||||
)
|
||||
|
||||
|
||||
def test_utils_pins_keep_their_bare_names(tmp_path):
|
||||
names = _names(tmp_path)
|
||||
assert names["earned-time-v0.14.0"] == "earned-time"
|
||||
assert names["gatelock-v0.11.0"] == "gatelock"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("url", "expected"),
|
||||
[
|
||||
("https://github.com/kuhyx/utils", "pkg"),
|
||||
("https://GitHub.com/Kuhyx/Utils.git/", "pkg"),
|
||||
("ssh://git@github.com/kuhyx/utils.git", "pkg"),
|
||||
("https://gitlab.com/someone/thing.git", "gitlab.com/someone/thing:pkg"),
|
||||
("not a url", "pkg"),
|
||||
("https://github.com/lonely", "pkg"),
|
||||
],
|
||||
)
|
||||
def test_qualified_name(url, expected):
|
||||
assert gittag.qualified_name(url, "pkg") == expected
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def remotes(monkeypatch):
|
||||
seen: list[str] = []
|
||||
|
||||
def fake_ls_remote(remote, *_args):
|
||||
seen.append(remote)
|
||||
return [
|
||||
"aaa\trefs/tags/leisure-watch-v0.1.0",
|
||||
"bbb\trefs/tags/leisure-watch-v0.1.1",
|
||||
]
|
||||
|
||||
monkeypatch.setattr(gittag, "ls_remote", fake_ls_remote)
|
||||
return seen
|
||||
|
||||
|
||||
def test_a_qualified_name_asks_its_own_repo(remotes):
|
||||
assert gittag.latest("github.com/kuhyx/leisure-watch:leisure-watch") == "0.1.1"
|
||||
assert remotes == [LW_URL]
|
||||
|
||||
|
||||
def test_a_bare_name_still_asks_utils(remotes):
|
||||
assert gittag.latest("leisure-watch") == "0.1.1"
|
||||
assert remotes == [UTILS_TAG_REMOTE]
|
||||
Loading…
Reference in New Issue
Block a user