246 lines
7.8 KiB
Python
Executable File
246 lines
7.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Per-package hooks for version management.
|
|
|
|
Each hook is a callable registered by package name (the relative path under
|
|
packages/, e.g. 'raspberrypi/linux-rpi') and source component name.
|
|
|
|
A hook can override:
|
|
- fetch_candidates(comp, merged_vars) -> Candidates
|
|
- prefetch_source(comp, merged_vars) -> Optional[str] (not yet needed)
|
|
|
|
Hooks are invoked by both the CLI updater and the TUI.
|
|
|
|
Adding a new hook:
|
|
1. Define a function or class with the required signature.
|
|
2. Register it via register_candidates_hook(pkg_name, src_name, fn) at module
|
|
level below.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Callable, Dict, Optional, Tuple
|
|
|
|
from lib import (
|
|
Candidates,
|
|
Json,
|
|
gh_head_commit,
|
|
gh_list_tags,
|
|
gh_ref_date,
|
|
gh_release_date,
|
|
http_get_text,
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Hook registry
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# (pkg_name, src_name) -> fn(comp, merged_vars) -> Candidates
|
|
_CANDIDATES_HOOKS: Dict[Tuple[str, str], Callable] = {}
|
|
|
|
|
|
def register_candidates_hook(pkg: str, src: str, fn: Callable) -> None:
|
|
_CANDIDATES_HOOKS[(pkg, src)] = fn
|
|
|
|
|
|
def get_candidates_hook(pkg: str, src: str) -> Optional[Callable]:
|
|
return _CANDIDATES_HOOKS.get((pkg, src))
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Raspberry Pi linux — stable_YYYYMMDD tag selection
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _rpi_linux_stable_candidates(comp: Json, merged_vars: Json) -> Candidates:
|
|
from lib import render, gh_latest_release, gh_latest_tag
|
|
|
|
c = Candidates()
|
|
owner = comp.get("owner", "raspberrypi")
|
|
repo = comp.get("repo", "linux")
|
|
branch: Optional[str] = comp.get("branch") or None
|
|
|
|
tags_all = gh_list_tags(owner, repo)
|
|
|
|
rendered = render(comp, merged_vars)
|
|
cur_tag = str(rendered.get("tag") or "")
|
|
|
|
if cur_tag.startswith("stable_") or not branch:
|
|
# Pick the most recent stable_YYYYMMDD tag
|
|
stable_tags = sorted(
|
|
[t for t in tags_all if re.match(r"^stable_\d{8}$", t)],
|
|
reverse=True,
|
|
)
|
|
if stable_tags:
|
|
c.tag = stable_tags[0]
|
|
c.tag_date = gh_ref_date(owner, repo, c.tag)
|
|
else:
|
|
# Series-based tracking: pick latest rpi-X.Y.* tag
|
|
mm = str(merged_vars.get("modDirVersion") or "")
|
|
m = re.match(r"^(\d+)\.(\d+)", mm)
|
|
if m:
|
|
base = f"rpi-{m.group(1)}.{m.group(2)}"
|
|
series = [
|
|
t
|
|
for t in tags_all
|
|
if t == f"{base}.y"
|
|
or t.startswith(f"{base}.y")
|
|
or t.startswith(f"{base}.")
|
|
]
|
|
series.sort(reverse=True)
|
|
if series:
|
|
c.tag = series[0]
|
|
c.tag_date = gh_ref_date(owner, repo, c.tag)
|
|
|
|
if branch:
|
|
commit = gh_head_commit(owner, repo, branch)
|
|
if commit:
|
|
c.commit = commit
|
|
c.commit_date = gh_ref_date(owner, repo, commit)
|
|
|
|
return c
|
|
|
|
|
|
register_candidates_hook(
|
|
"raspberrypi/linux-rpi", "stable", _rpi_linux_stable_candidates
|
|
)
|
|
register_candidates_hook(
|
|
"raspberrypi/linux-rpi", "unstable", _rpi_linux_stable_candidates
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# CachyOS linux — version from upstream PKGBUILD / .SRCINFO
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _parse_cachyos_linux_version(text: str, is_srcinfo: bool) -> Optional[str]:
|
|
if is_srcinfo:
|
|
m = re.search(r"^\s*pkgver\s*=\s*([^\s#]+)\s*$", text, re.MULTILINE)
|
|
if m:
|
|
v = m.group(1).strip().replace(".rc", "-rc")
|
|
return v
|
|
return None
|
|
|
|
# PKGBUILD
|
|
env: Dict[str, str] = {}
|
|
for line in text.splitlines():
|
|
line = line.strip()
|
|
if not line or line.startswith("#"):
|
|
continue
|
|
ma = re.match(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.+)$", line)
|
|
if ma:
|
|
key, val = ma.group(1), ma.group(2).strip()
|
|
val = re.sub(r"\s+#.*$", "", val).strip()
|
|
if (val.startswith('"') and val.endswith('"')) or (
|
|
val.startswith("'") and val.endswith("'")
|
|
):
|
|
val = val[1:-1]
|
|
env[key] = val
|
|
|
|
m2 = re.search(r"^\s*pkgver\s*=\s*(.+)$", text, re.MULTILINE)
|
|
if not m2:
|
|
return None
|
|
raw = m2.group(1).strip().strip("\"'")
|
|
|
|
def expand(s: str) -> str:
|
|
s = re.sub(r"\$\{([^}]+)\}", lambda mb: env.get(mb.group(1), mb.group(0)), s)
|
|
s = re.sub(
|
|
r"\$([A-Za-z_][A-Za-z0-9_]*)",
|
|
lambda mu: env.get(mu.group(1), mu.group(0)),
|
|
s,
|
|
)
|
|
return s
|
|
|
|
return expand(raw).strip().replace(".rc", "-rc")
|
|
|
|
|
|
def _cachyos_linux_suffix(variant_name: Optional[str]) -> str:
|
|
if not variant_name:
|
|
return ""
|
|
return {"rc": "-rc", "hardened": "-hardened", "lts": "-lts"}.get(variant_name, "")
|
|
|
|
|
|
def fetch_cachyos_linux_version(suffix: str) -> Optional[str]:
|
|
bases = [
|
|
"https://raw.githubusercontent.com/CachyOS/linux-cachyos/master",
|
|
"https://raw.githubusercontent.com/cachyos/linux-cachyos/master",
|
|
]
|
|
for base in bases:
|
|
text = http_get_text(f"{base}/linux-cachyos{suffix}/.SRCINFO")
|
|
if text:
|
|
v = _parse_cachyos_linux_version(text, is_srcinfo=True)
|
|
if v:
|
|
return v
|
|
text = http_get_text(f"{base}/linux-cachyos{suffix}/PKGBUILD")
|
|
if text:
|
|
v = _parse_cachyos_linux_version(text, is_srcinfo=False)
|
|
if v:
|
|
return v
|
|
return None
|
|
|
|
|
|
def linux_tarball_url(version: str) -> str:
|
|
if "-rc" in version:
|
|
return f"https://git.kernel.org/torvalds/t/linux-{version}.tar.gz"
|
|
parts = version.split(".")
|
|
major = parts[0] if parts else "6"
|
|
ver_for_tar = ".".join(parts[:2]) if version.endswith(".0") else version
|
|
return (
|
|
f"https://cdn.kernel.org/pub/linux/kernel/v{major}.x/linux-{ver_for_tar}.tar.xz"
|
|
)
|
|
|
|
|
|
# Note: linux-cachyos is not yet in the repo, but the hook is defined here
|
|
# so it can be activated when that package is added.
|
|
def _cachyos_linux_candidates(comp: Json, merged_vars: Json) -> Candidates:
|
|
c = Candidates()
|
|
# The variant name is not available here; the TUI/CLI must pass it via merged_vars
|
|
suffix = str(merged_vars.get("_cachyos_suffix") or "")
|
|
latest = fetch_cachyos_linux_version(suffix)
|
|
if latest:
|
|
c.tag = latest # use tag slot for display consistency
|
|
return c
|
|
|
|
|
|
register_candidates_hook("linux-cachyos", "linux", _cachyos_linux_candidates)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# CachyOS ZFS — commit pinned in PKGBUILD
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def fetch_cachyos_zfs_commit(suffix: str) -> Optional[str]:
|
|
bases = [
|
|
"https://raw.githubusercontent.com/CachyOS/linux-cachyos/master",
|
|
"https://raw.githubusercontent.com/cachyos/linux-cachyos/master",
|
|
]
|
|
for base in bases:
|
|
text = http_get_text(f"{base}/linux-cachyos{suffix}/PKGBUILD")
|
|
if not text:
|
|
continue
|
|
m = re.search(
|
|
r"git\+https://github\.com/cachyos/zfs\.git#commit=([0-9a-f]+)", text
|
|
)
|
|
if m:
|
|
return m.group(1)
|
|
return None
|
|
|
|
|
|
def _cachyos_zfs_candidates(comp: Json, merged_vars: Json) -> Candidates:
|
|
c = Candidates()
|
|
suffix = str(merged_vars.get("_cachyos_suffix") or "")
|
|
sha = fetch_cachyos_zfs_commit(suffix)
|
|
if sha:
|
|
c.commit = sha
|
|
url = comp.get("url") or ""
|
|
c.commit_date = (
|
|
gh_ref_date("cachyos", "zfs", sha) if "github.com" in url else ""
|
|
)
|
|
return c
|
|
|
|
|
|
register_candidates_hook("linux-cachyos", "zfs", _cachyos_zfs_candidates)
|