mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-12 15:51:00 +01:00
dep_freshness: answer JitPack artifacts from the source repo's tags
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
Since ~2026-09-14 https://jitpack.io/.../maven-metadata.xml answers HTTP 401 "No access token" (artifact POM/AAR downloads still 200), which turned every com.github.* coordinate into "undeterminable" (exit 3) in --strict CI. JitPack is dropped from MAVEN_REPOS. A com.github.<owner>:<repo> name that no HTTP repository knows is now answered from the GitHub repo's tags via `git ls-remote --tags --refs` -- JitPack's version IS the tag name, so the tags are compared as they are (v prefix included). The tag list goes through the same stable-first selector as maven-metadata, so a newer pre-release tag does not win. Not an allowlist entry, not a token. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016P4KZw1E2sXJjYqfcZWf1x
This commit is contained in:
parent
ba7ea0926f
commit
580d6e9727
@ -54,13 +54,15 @@ NODE_RELEASES: Final = "https://nodejs.org/dist/index.json"
|
||||
GRADLE_RELEASES: Final = "https://services.gradle.org/versions/current"
|
||||
# Asked in order; a 404 moves on. Google first because AndroidX and the
|
||||
# Android Gradle plugin exist nowhere else, and Central answers 404 fast.
|
||||
# JitPack last: it serves tag metadata for `com.github.*` artifacts that
|
||||
# were never published anywhere else (PhotoView, DirectionalViewPager, ...).
|
||||
# JitPack is not on the list: since 2026-09 its maven-metadata answers
|
||||
# HTTP 401 "No access token" (artifact downloads still work), so `com.github.*`
|
||||
# artifacts published nowhere else (PhotoView, DirectionalViewPager, ...)
|
||||
# are answered from the source repo's tags instead -- which is all JitPack
|
||||
# ever built.
|
||||
MAVEN_REPOS: Final = (
|
||||
"https://dl.google.com/dl/android/maven2/",
|
||||
"https://repo1.maven.org/maven2/",
|
||||
"https://plugins.gradle.org/m2/",
|
||||
"https://jitpack.io/",
|
||||
)
|
||||
UTILS_TAG_REMOTE: Final = "https://github.com/kuhyx/utils"
|
||||
# JitPack coordinates: `com.github.<owner>:<repo>` or
|
||||
|
||||
@ -1,12 +1,18 @@
|
||||
"""Maven repositories: newest stable of `group:artifact`, from maven-metadata.
|
||||
|
||||
Four repositories, asked in order until one knows the artifact: Google
|
||||
Three repositories, asked in order until one knows the artifact: Google
|
||||
Maven (every `androidx.*` and `com.android.*` artifact lives ONLY there),
|
||||
Maven Central, the Gradle plugin portal (plugin marker artifacts
|
||||
`<id>:<id>.gradle.plugin`) and JitPack (tag metadata for `com.github.*`
|
||||
artifacts published nowhere else). A 404 means "not here", not "not
|
||||
anywhere", so it moves on; anything else surfaces as `Offline` from the HTTP
|
||||
layer.
|
||||
Maven Central and the Gradle plugin portal (plugin marker artifacts
|
||||
`<id>:<id>.gradle.plugin`). A 404 means "not here", not "not anywhere", so
|
||||
it moves on; anything else surfaces as `Offline` from the HTTP layer.
|
||||
|
||||
JitPack used to be the fourth: it served tag metadata for `com.github.*`
|
||||
artifacts published nowhere else (PhotoView, DirectionalViewPager). Since
|
||||
2026-09 that endpoint answers HTTP 401 "No access token" while the artifacts
|
||||
themselves still download, so a `com.github.<owner>:<repo>` name that no
|
||||
repository knows is answered from the GitHub repo's tags over `git
|
||||
ls-remote` (unmetered, no token). JitPack's version IS the tag name, `v`
|
||||
prefix included, so the tags are compared as they are.
|
||||
|
||||
`<latest>` and `<release>` in the metadata are not trusted: AndroidX writes
|
||||
`1.2.0-alpha03` into `<latest>` routinely. The full `<versions>` list goes
|
||||
@ -22,7 +28,9 @@ from __future__ import annotations
|
||||
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from dep_freshness._tables import MAVEN_REPOS
|
||||
from dep_freshness._tables import GITHUB_REMOTE, MAVEN_REPOS
|
||||
from dep_freshness.registries._git import ls_remote
|
||||
from dep_freshness.registries.gitcommit import source_repo
|
||||
from dep_freshness.registries.http import get_text
|
||||
from dep_freshness.versions import newest_per_generation, newest_stable, reference
|
||||
|
||||
@ -48,6 +56,15 @@ def versions_in(xml: str) -> list[str]:
|
||||
]
|
||||
|
||||
|
||||
def github_tags(name: str) -> list[str]:
|
||||
"""Every tag of the GitHub repo behind a JitPack coordinate; [] otherwise."""
|
||||
repo = source_repo(name)
|
||||
if repo is None:
|
||||
return []
|
||||
lines = ls_remote(GITHUB_REMOTE.format(repo=repo), "--tags", "--refs")
|
||||
return [line.rpartition("refs/tags/")[2] for line in lines if "refs/tags/" in line]
|
||||
|
||||
|
||||
def latest(name: str) -> str | None:
|
||||
seen: list[str] = []
|
||||
for repo in MAVEN_REPOS:
|
||||
@ -62,4 +79,6 @@ def latest(name: str) -> str | None:
|
||||
if stable is not None:
|
||||
return stable
|
||||
seen.extend(versions)
|
||||
if not seen:
|
||||
seen.extend(github_tags(name))
|
||||
return reference(seen) or newest_per_generation(seen)
|
||||
|
||||
@ -13,7 +13,7 @@ from dep_freshness.registries.http import Offline
|
||||
from dep_freshness.resolve import _toolchain_latest
|
||||
from dep_freshness.tests.conftest import write
|
||||
|
||||
GOOGLE, CENTRAL, PORTAL, JITPACK = maven.MAVEN_REPOS
|
||||
GOOGLE, CENTRAL, PORTAL = maven.MAVEN_REPOS
|
||||
|
||||
|
||||
def metadata(*versions: str, latest: str | None = None) -> str:
|
||||
@ -68,21 +68,74 @@ def test_a_repo_with_only_prereleases_does_not_stop_the_search(repos):
|
||||
assert maven.latest("g:a") == "0.9.0"
|
||||
|
||||
|
||||
def test_jitpack_is_asked_last_and_reads_tag_metadata(repos):
|
||||
"""PhotoView was never published anywhere but JitPack; `v`-tags and bare
|
||||
tags both appear and the newest wins."""
|
||||
def tags(monkeypatch, remote_tags: dict[str, list[str]]):
|
||||
"""Serve `git ls-remote --tags --refs` for the GitHub remotes in `remote_tags`."""
|
||||
calls: list[tuple[str, ...]] = []
|
||||
|
||||
def fake(remote, *args):
|
||||
calls.append((remote, *args))
|
||||
repo = remote.removeprefix("https://github.com/")
|
||||
if repo not in remote_tags:
|
||||
raise Offline(remote)
|
||||
return [f"{'0' * 40}\trefs/tags/{tag}" for tag in remote_tags[repo]]
|
||||
|
||||
monkeypatch.setattr(maven, "ls_remote", fake)
|
||||
return calls
|
||||
|
||||
|
||||
def test_jitpack_artifact_is_answered_from_the_repo_tags(repos, monkeypatch):
|
||||
"""PhotoView was never published anywhere but JitPack, whose metadata
|
||||
endpoint now answers 401; the repo's tags are the same list. `v`-tags and
|
||||
bare tags both appear and the newest STABLE wins: a newer pre-release
|
||||
tag is skipped exactly as it would be in maven-metadata."""
|
||||
name = "com.github.chrisbanes:PhotoView"
|
||||
repos[maven.metadata_url(JITPACK, name)] = metadata("v1.3.1", "2.2.0", "2.3.0")
|
||||
calls = tags(
|
||||
monkeypatch,
|
||||
{"chrisbanes/PhotoView": ["v1.3.1", "2.2.0", "2.3.0", "v3.0.0-beta1"]},
|
||||
)
|
||||
assert maven.latest(name) == "2.3.0"
|
||||
assert calls == [("https://github.com/chrisbanes/PhotoView", "--tags", "--refs")]
|
||||
|
||||
|
||||
def test_a_project_named_tag_is_read_as_its_version(repos):
|
||||
def test_a_project_named_tag_is_read_as_its_version(repos, monkeypatch):
|
||||
"""`java-nat-sort` is tagged `natural-comparator-1.1`, not `1.1`."""
|
||||
name = "com.github.gpanther:java-nat-sort"
|
||||
repos[maven.metadata_url(JITPACK, name)] = metadata("natural-comparator-1.1")
|
||||
tags(monkeypatch, {"gpanther/java-nat-sort": ["natural-comparator-1.0", "natural-comparator-1.1"]})
|
||||
assert maven.latest(name) == "natural-comparator-1.1"
|
||||
|
||||
|
||||
def test_a_repository_answer_wins_over_the_tags(repos, monkeypatch):
|
||||
"""A `com.github.*` artifact that Central knows is not a JitPack build."""
|
||||
name = "com.github.ben-manes.caffeine:caffeine"
|
||||
repos[maven.metadata_url(CENTRAL, name)] = metadata("3.1.8", "3.2.0")
|
||||
calls = tags(monkeypatch, {})
|
||||
assert maven.latest(name) == "3.2.0"
|
||||
assert calls == []
|
||||
|
||||
|
||||
def test_prereleases_from_a_repository_skip_the_tag_lookup(repos, monkeypatch):
|
||||
"""Something was found, just nothing stable: the repo answered, so the
|
||||
pre-release fallback applies and the tags are never consulted."""
|
||||
name = "com.github.x:y"
|
||||
repos[maven.metadata_url(CENTRAL, name)] = metadata("1.0.0-beta1")
|
||||
calls = tags(monkeypatch, {})
|
||||
assert maven.latest(name) == "1.0.0-beta1"
|
||||
assert calls == []
|
||||
|
||||
|
||||
def test_a_tagless_repo_and_a_non_jitpack_name_are_none(repos, monkeypatch):
|
||||
calls = tags(monkeypatch, {"o/r": [], "o/s": ["HEAD-not-a-tag"]})
|
||||
assert maven.latest("com.github.o:r") is None
|
||||
assert maven.github_tags("g:a") == []
|
||||
assert calls == [("https://github.com/o/r", "--tags", "--refs")]
|
||||
|
||||
|
||||
def test_an_unreachable_repo_is_offline(repos, monkeypatch):
|
||||
tags(monkeypatch, {})
|
||||
with pytest.raises(Offline):
|
||||
maven.latest("com.github.o:gone")
|
||||
|
||||
|
||||
def test_a_package_with_no_stable_anywhere_falls_back_to_newest_prerelease(repos):
|
||||
"""biometric-ktx has been alpha-only since 2021: the newest alpha is the
|
||||
reference, and a stable release in ANY repo would still win over it."""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user