utils/dep_freshness/registries/toolchain.py
Krzysztof kuhy Rudnicki a4ea305164 dep_freshness: Gradle ecosystem -- version catalogs, wrapper, Maven registries
The gate had no way to read a Gradle build, which left the two Kotlin
forks (tachiyomisy, syncyomi-src) outside the fleet-wide rule.

- parsers/gradle.py reads gradle/libs.versions.toml ([libraries] and
  [plugins] in every shape: inline string, module + version.ref,
  group/name/version, rich {strictly|require|prefer} tables) into
  `maven:<group>:<artifact>` deps, pointing the fix at the [versions]
  line when a ref is used; BOM-managed entries without a version are
  skipped. gradle-wrapper.properties becomes `toolchain:gradle`.
- registries/maven.py asks Google Maven, Central and the plugin portal
  in that order, reading the full <versions> list -- AndroidX writes
  alphas into <latest>. Plugins resolve through their marker artifact.
- registries/toolchain.gradle_latest reads services.gradle.org.
- http.get_text shares the transport with get_json (XML bodies).
- Templates: the pre-commit `files:` and the CI cache key know the
  catalog. 100% branch on every new file; fixtures only.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Yc4TiPjhbQLfH35QBPfrbP
2026-09-12 19:10:42 +02:00

61 lines
2.0 KiB
Python

"""Latest stable Flutter/Dart, Node and Python, for the toolchain checks (Q3).
Python is deliberately compared against the INSTALLED interpreter rather than
python.org: on Arch it is pacman-managed, and a gate that can only be satisfied
by fighting the distro gets switched off.
"""
from __future__ import annotations
import platform
from dep_freshness._tables import (
FLUTTER_RELEASES,
GRADLE_RELEASES,
NODE_CHANNEL,
NODE_RELEASES,
)
from dep_freshness.registries.http import get_json
from dep_freshness.versions import newest_stable
def flutter_latest() -> tuple[str | None, str | None]:
"""`(flutter, dart)` on the stable channel."""
payload = get_json(FLUTTER_RELEASES)
if not payload:
return (None, None)
stable_hash = (payload.get("current_release") or {}).get("stable")
for release in payload.get("releases") or []:
if release.get("hash") == stable_hash and release.get("channel") == "stable":
return (release.get("version"), release.get("dart_sdk_version"))
stable = [
r.get("version") for r in payload.get("releases") or []
if r.get("channel") == "stable"
]
return (newest_stable(stable), None)
def node_latest(channel: str = NODE_CHANNEL) -> str | None:
"""Newest Node release on the configured channel (`lts` or `current`)."""
payload = get_json(NODE_RELEASES)
if not payload:
return None
if channel == "lts":
candidates = [r.get("version") for r in payload if r.get("lts")]
else:
candidates = [r.get("version") for r in payload]
return newest_stable(candidates)
def python_installed() -> str:
"""The interpreter this gate is running under — the Python target."""
return platform.python_version()
def gradle_latest() -> str | None:
"""Newest Gradle release, from services.gradle.org (never a nightly/RC)."""
payload = get_json(GRADLE_RELEASES)
if not payload:
return None
return newest_stable([payload.get("version")])