mirror of
https://github.com/xddxdd/nix-cachyos-kernel.git
synced 2026-07-03 19:18:07 +02:00
234 lines
6.8 KiB
Python
234 lines
6.8 KiB
Python
#!/usr/bin/env nix-shell
|
|
#!nix-shell -i python3 -p python3 -p python3Packages.requests -p nix
|
|
|
|
import json
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any, Optional
|
|
|
|
import requests
|
|
|
|
GITHUB_API = "https://api.github.com/repos/CachyOS/linux/releases"
|
|
|
|
# Pattern: cachyos-{version}-{tagrel}
|
|
# Stable: cachyos-6.19.5-2
|
|
# RC: cachyos-7.0-rc1-2
|
|
TAG_PATTERN = re.compile(
|
|
r"^cachyos-(?P<version>\d+\.\d+(?:\.\d+)?(?:-rc\d+)?)-(?P<tagrel>\d+)$"
|
|
)
|
|
|
|
# For sorting: extract (major, minor, patch) from version string
|
|
VERSION_PATTERN = re.compile(
|
|
r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?(?:-rc(?P<rc>\d+))?$"
|
|
)
|
|
|
|
|
|
def parse_version_tuple(version: str) -> tuple:
|
|
m = VERSION_PATTERN.match(version)
|
|
if not m:
|
|
return (0, 0, 0, 0)
|
|
major = int(m.group("major"))
|
|
minor = int(m.group("minor"))
|
|
patch = int(m.group("patch") or "0")
|
|
# RC versions sort before stable (rc=0 means stable, higher is better)
|
|
rc = int(m.group("rc") or "0")
|
|
# Stable versions (rc=0) should sort higher than any RC
|
|
rc_sort = (1, 0) if rc == 0 else (0, rc)
|
|
return (major, minor, patch, *rc_sort)
|
|
|
|
|
|
def is_rc(version: str) -> bool:
|
|
return "-rc" in version
|
|
|
|
|
|
def major_minor(version: str) -> str:
|
|
m = VERSION_PATTERN.match(version)
|
|
if not m:
|
|
return version
|
|
return f"{m.group('major')}.{m.group('minor')}"
|
|
|
|
|
|
def fetch_releases() -> list[dict[str, Any]]:
|
|
headers = {}
|
|
github_token = os.environ.get("GITHUB_TOKEN")
|
|
if github_token:
|
|
headers["Authorization"] = f"token {github_token}"
|
|
|
|
all_releases = []
|
|
page = 1
|
|
per_page = 100
|
|
|
|
while True:
|
|
params = {"per_page": per_page, "page": page}
|
|
response = requests.get(GITHUB_API, params=params, headers=headers, timeout=30)
|
|
response.raise_for_status()
|
|
|
|
releases = response.json()
|
|
if not releases:
|
|
break
|
|
|
|
all_releases.extend(releases)
|
|
page += 1
|
|
|
|
if len(releases) < per_page:
|
|
break
|
|
|
|
return all_releases
|
|
|
|
|
|
def parse_releases(releases: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
parsed = []
|
|
for release in releases:
|
|
tag = release.get("tag_name", "")
|
|
m = TAG_PATTERN.match(tag)
|
|
if not m:
|
|
continue
|
|
parsed.append({
|
|
"tag": tag,
|
|
"version": m.group("version"),
|
|
"tagrel": int(m.group("tagrel")),
|
|
})
|
|
return parsed
|
|
|
|
|
|
def pick_latest_per_track(parsed: list[dict[str, Any]]) -> dict[str, dict[str, Any]]:
|
|
# Separate RC from stable releases
|
|
rc_releases = [r for r in parsed if is_rc(r["version"])]
|
|
stable_releases = [r for r in parsed if not is_rc(r["version"])]
|
|
|
|
# Group stable releases by major.minor series
|
|
series: dict[str, list[dict[str, Any]]] = {}
|
|
for r in stable_releases:
|
|
mm = major_minor(r["version"])
|
|
series.setdefault(mm, []).append(r)
|
|
|
|
# Sort each series by (version_tuple, tagrel) descending, pick the best
|
|
def sort_key(r: dict[str, Any]) -> tuple:
|
|
return (*parse_version_tuple(r["version"]), r["tagrel"])
|
|
|
|
best_per_series = {}
|
|
for mm, releases in series.items():
|
|
releases.sort(key=sort_key, reverse=True)
|
|
best_per_series[mm] = releases[0]
|
|
|
|
# Sort series by version to determine latest vs LTS
|
|
sorted_series = sorted(
|
|
best_per_series.keys(),
|
|
key=lambda mm: parse_version_tuple(best_per_series[mm]["version"]),
|
|
reverse=True,
|
|
)
|
|
|
|
result = {}
|
|
|
|
if len(sorted_series) >= 1:
|
|
result["latest"] = best_per_series[sorted_series[0]]
|
|
if len(sorted_series) >= 2:
|
|
result["lts"] = best_per_series[sorted_series[1]]
|
|
|
|
# Pick best RC release
|
|
if rc_releases:
|
|
rc_releases.sort(key=sort_key, reverse=True)
|
|
result["rc"] = rc_releases[0]
|
|
|
|
return result
|
|
|
|
|
|
def nix_prefetch_url(url: str) -> Optional[str]:
|
|
print(f" Prefetching {url}...")
|
|
cmd = ["nix-prefetch-url", "--type", "sha256", url]
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=600)
|
|
if result.returncode != 0:
|
|
print(f" nix-prefetch-url failed: {result.stderr}")
|
|
return None
|
|
nix32_hash = result.stdout.strip()
|
|
|
|
# Convert to SRI format
|
|
cmd2 = ["nix", "hash", "convert", "--hash-algo", "sha256", "--to", "sri", nix32_hash]
|
|
result2 = subprocess.run(cmd2, capture_output=True, text=True, timeout=30)
|
|
if result2.returncode != 0:
|
|
print(f" nix hash convert failed: {result2.stderr}")
|
|
return None
|
|
return result2.stdout.strip()
|
|
|
|
|
|
def build_tag(version: str, tagrel: int) -> str:
|
|
return f"cachyos-{version}-{tagrel}"
|
|
|
|
|
|
def build_url(tag: str) -> str:
|
|
return f"https://github.com/CachyOS/linux/releases/download/{tag}/{tag}.tar.gz"
|
|
|
|
|
|
def main() -> int:
|
|
print("Updating CachyOS kernel sources...")
|
|
|
|
# Find repo root
|
|
current = Path.cwd()
|
|
while not (current / "flake.lock").exists():
|
|
if current == current.parent:
|
|
print("Could not find flake.lock in any parent directory")
|
|
return 1
|
|
current = current.parent
|
|
|
|
sources_file = current / "kernel-cachyos" / "sources.json"
|
|
|
|
# Load existing sources for comparison
|
|
existing = {}
|
|
if sources_file.exists():
|
|
with open(sources_file, encoding="utf-8") as f:
|
|
existing = json.load(f)
|
|
|
|
# Fetch and parse releases
|
|
print("Fetching releases from GitHub...")
|
|
releases = fetch_releases()
|
|
parsed = parse_releases(releases)
|
|
tracks = pick_latest_per_track(parsed)
|
|
|
|
if "latest" not in tracks:
|
|
print("Could not determine latest kernel release")
|
|
return 1
|
|
|
|
# Build new sources.json
|
|
new_sources = {}
|
|
for track_name in ["latest", "lts", "rc"]:
|
|
if track_name not in tracks:
|
|
print(f"Warning: no {track_name} release found, skipping")
|
|
continue
|
|
|
|
track = tracks[track_name]
|
|
tag = build_tag(track["version"], track["tagrel"])
|
|
|
|
# Check if this version is already in sources.json with a hash
|
|
old = existing.get(track_name, {})
|
|
old_tag = build_tag(old.get("version", ""), old.get("tagrel", 0))
|
|
|
|
if old_tag == tag and old.get("hash"):
|
|
print(f" {track_name}: {tag} (unchanged)")
|
|
new_sources[track_name] = old
|
|
else:
|
|
print(f" {track_name}: {old_tag} -> {tag}")
|
|
url = build_url(tag)
|
|
sri_hash = nix_prefetch_url(url)
|
|
if not sri_hash:
|
|
print(f" Failed to prefetch {track_name} source")
|
|
return 1
|
|
new_sources[track_name] = {
|
|
"version": track["version"],
|
|
"tagrel": track["tagrel"],
|
|
"hash": sri_hash,
|
|
}
|
|
|
|
with open(sources_file, "w", encoding="utf-8") as f:
|
|
json.dump(new_sources, f, indent=2)
|
|
f.write("\n")
|
|
|
|
print(f"Sources updated: {sources_file}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|