diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml index 01c6e59..89a9bb2 100644 --- a/.github/workflows/auto-update.yml +++ b/.github/workflows/auto-update.yml @@ -60,11 +60,12 @@ jobs: sudo systemctl daemon-reload sudo systemctl restart nix-daemon - - name: Build nix packages + - name: Update sources env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | export TMPDIR=/nix/tmpdir + nix run .#update-cachyos-kernel-sources nix flake update nix run .#update-zfs-cachyos diff --git a/flake.nix b/flake.nix index f22a76c..5195ccd 100644 --- a/flake.nix +++ b/flake.nix @@ -99,6 +99,25 @@ lib.getExe script; }; + apps.update-cachyos-kernel-sources = { + type = "app"; + program = + let + python = pkgs.python3.withPackages (ps: [ ps.requests ]); + script = pkgs.writeShellApplication { + name = "update-cachyos-kernel-sources"; + runtimeInputs = [ + python + pkgs.nix + ]; + text = '' + python3 ${./kernel-cachyos/update.py} + ''; + }; + in + lib.getExe script; + }; + # Allow build unfree modules such as nvidia_x11 _module.args.pkgs = lib.mkForce ( import inputs.nixpkgs { diff --git a/kernel-cachyos/default.nix b/kernel-cachyos/default.nix index db4921a..696e4eb 100644 --- a/kernel-cachyos/default.nix +++ b/kernel-cachyos/default.nix @@ -2,76 +2,89 @@ inputs, callPackage, lib, - linux_latest, - linux_testing, - linux, + fetchurl, ... }: let mkCachyKernel = callPackage ./mkCachyKernel.nix { inherit inputs; }; + + sources = lib.importJSON ./sources.json; + cachySrc = track: let + s = sources.${track}; + tag = "cachyos-${s.version}-${toString s.tagrel}"; + in { + inherit (s) version; + src = fetchurl { + url = "https://github.com/CachyOS/linux/releases/download/${tag}/${tag}.tar.gz"; + inherit (s) hash; + }; + }; + latest = cachySrc "latest"; + lts = cachySrc "lts"; + rc = cachySrc "rc"; in builtins.listToAttrs ( builtins.map (v: lib.nameValuePair v.pname v) [ # Latest kernel, provide all LTO/CPU arch variants (mkCachyKernel { pname = "linux-cachyos-latest"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos"; }) (mkCachyKernel { pname = "linux-cachyos-latest-x86_64-v2"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos"; processorOpt = "x86_64-v2"; }) (mkCachyKernel { pname = "linux-cachyos-latest-x86_64-v3"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos"; processorOpt = "x86_64-v3"; }) (mkCachyKernel { pname = "linux-cachyos-latest-x86_64-v4"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos"; processorOpt = "x86_64-v4"; }) (mkCachyKernel { pname = "linux-cachyos-latest-zen4"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos"; processorOpt = "zen4"; }) (mkCachyKernel { pname = "linux-cachyos-latest-lto"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos"; lto = "thin"; }) (mkCachyKernel { pname = "linux-cachyos-latest-lto-x86_64-v2"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos"; lto = "thin"; processorOpt = "x86_64-v2"; }) (mkCachyKernel { pname = "linux-cachyos-latest-lto-x86_64-v3"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos"; lto = "thin"; processorOpt = "x86_64-v3"; }) (mkCachyKernel { pname = "linux-cachyos-latest-lto-x86_64-v4"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos"; lto = "thin"; processorOpt = "x86_64-v4"; }) (mkCachyKernel { pname = "linux-cachyos-latest-lto-zen4"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos"; lto = "thin"; processorOpt = "zen4"; @@ -80,63 +93,63 @@ builtins.listToAttrs ( # LTS kernel (mkCachyKernel { pname = "linux-cachyos-lts"; - inherit (linux) version src; + inherit (lts) version src; configVariant = "linux-cachyos-lts"; }) (mkCachyKernel { pname = "linux-cachyos-lts-x86_64-v2"; - inherit (linux) version src; + inherit (lts) version src; configVariant = "linux-cachyos-lts"; processorOpt = "x86_64-v2"; }) (mkCachyKernel { pname = "linux-cachyos-lts-x86_64-v3"; - inherit (linux) version src; + inherit (lts) version src; configVariant = "linux-cachyos-lts"; processorOpt = "x86_64-v3"; }) (mkCachyKernel { pname = "linux-cachyos-lts-x86_64-v4"; - inherit (linux) version src; + inherit (lts) version src; configVariant = "linux-cachyos-lts"; processorOpt = "x86_64-v4"; }) (mkCachyKernel { pname = "linux-cachyos-lts-zen4"; - inherit (linux) version src; + inherit (lts) version src; configVariant = "linux-cachyos-lts"; processorOpt = "zen4"; }) (mkCachyKernel { pname = "linux-cachyos-lts-lto"; - inherit (linux) version src; + inherit (lts) version src; configVariant = "linux-cachyos-lts"; lto = "thin"; }) (mkCachyKernel { pname = "linux-cachyos-lts-lto-x86_64-v2"; - inherit (linux) version src; + inherit (lts) version src; configVariant = "linux-cachyos-lts"; lto = "thin"; processorOpt = "x86_64-v2"; }) (mkCachyKernel { pname = "linux-cachyos-lts-lto-x86_64-v3"; - inherit (linux) version src; + inherit (lts) version src; configVariant = "linux-cachyos-lts"; lto = "thin"; processorOpt = "x86_64-v3"; }) (mkCachyKernel { pname = "linux-cachyos-lts-lto-x86_64-v4"; - inherit (linux) version src; + inherit (lts) version src; configVariant = "linux-cachyos-lts"; lto = "thin"; processorOpt = "x86_64-v4"; }) (mkCachyKernel { pname = "linux-cachyos-lts-lto-zen4"; - inherit (linux) version src; + inherit (lts) version src; configVariant = "linux-cachyos-lts"; lto = "thin"; processorOpt = "zen4"; @@ -145,40 +158,40 @@ builtins.listToAttrs ( # Additional CachyOS provided variants (mkCachyKernel { pname = "linux-cachyos-bmq"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-bmq"; cpusched = "bmq"; }) (mkCachyKernel { pname = "linux-cachyos-bmq-lto"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-bmq"; lto = "thin"; cpusched = "bmq"; }) (mkCachyKernel { pname = "linux-cachyos-bore"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-bore"; cpusched = "bore"; }) (mkCachyKernel { pname = "linux-cachyos-bore-lto"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-bore"; lto = "thin"; cpusched = "bore"; }) (mkCachyKernel { pname = "linux-cachyos-deckify"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-deckify"; acpiCall = true; handheld = true; }) (mkCachyKernel { pname = "linux-cachyos-deckify-lto"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-deckify"; lto = "thin"; acpiCall = true; @@ -186,51 +199,51 @@ builtins.listToAttrs ( }) (mkCachyKernel { pname = "linux-cachyos-eevdf"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-eevdf"; cpusched = "eevdf"; }) (mkCachyKernel { pname = "linux-cachyos-eevdf-lto"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-eevdf"; cpusched = "eevdf"; lto = "thin"; }) (mkCachyKernel { pname = "linux-cachyos-hardened"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-hardened"; hardened = true; }) (mkCachyKernel { pname = "linux-cachyos-hardened-lto"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-hardened"; hardened = true; lto = "thin"; }) (mkCachyKernel { pname = "linux-cachyos-rc"; - inherit (linux_testing) version src; + inherit (rc) version src; configVariant = "linux-cachyos-rc"; }) (mkCachyKernel { pname = "linux-cachyos-rc-lto"; - inherit (linux_testing) version src; + inherit (rc) version src; configVariant = "linux-cachyos-rc"; lto = "thin"; }) (mkCachyKernel { pname = "linux-cachyos-rt-bore"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-rt-bore"; rt = true; cpusched = "bore"; }) (mkCachyKernel { pname = "linux-cachyos-rt-bore-lto"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-rt-bore"; rt = true; cpusched = "bore"; @@ -238,7 +251,7 @@ builtins.listToAttrs ( }) (mkCachyKernel { pname = "linux-cachyos-server"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-server"; cpusched = "eevdf"; hzTicks = "300"; @@ -246,7 +259,7 @@ builtins.listToAttrs ( }) (mkCachyKernel { pname = "linux-cachyos-server-lto"; - inherit (linux_latest) version src; + inherit (latest) version src; configVariant = "linux-cachyos-server"; cpusched = "eevdf"; hzTicks = "300"; diff --git a/kernel-cachyos/mkCachyKernel.nix b/kernel-cachyos/mkCachyKernel.nix index c401850..5989b2c 100644 --- a/kernel-cachyos/mkCachyKernel.nix +++ b/kernel-cachyos/mkCachyKernel.nix @@ -83,8 +83,7 @@ lib.makeOverridable ( cachyosConfigFile = "${inputs.cachyos-kernel.outPath}/${configVariant}/config"; cachyosPatches = builtins.map (p: "${inputs.cachyos-kernel-patches.outPath}/${patchVersion}/${p}") ( - [ "all/0001-cachyos-base-all.patch" ] - ++ (lib.optional (cpusched == "bore") "sched/0001-bore-cachy.patch") + (lib.optional (cpusched == "bore") "sched/0001-bore-cachy.patch") ++ (lib.optional (cpusched == "bmq") "sched/0001-prjc-cachy.patch") ++ (lib.optional hardened "misc/0001-hardened.patch") ++ (lib.optional rt "misc/0001-rt-i915.patch") diff --git a/kernel-cachyos/sources.json b/kernel-cachyos/sources.json new file mode 100644 index 0000000..efa7527 --- /dev/null +++ b/kernel-cachyos/sources.json @@ -0,0 +1,17 @@ +{ + "latest": { + "version": "6.19.5", + "tagrel": 2, + "hash": "sha256-KgOZl6MTmH4IvuopKArhK2fMzM1E+4zOeq9HeWUPmYY=" + }, + "lts": { + "version": "6.18.15", + "tagrel": 2, + "hash": "sha256-WpGH+tHh6vipOwqg1ZupEL40KF9FliYVwuOiykEj0Gc=" + }, + "rc": { + "version": "7.0-rc1", + "tagrel": 2, + "hash": "sha256-BxeMq0M0C98Wcc9d+GxYVzPNdw6hK4kmj+xW9Ck3mgg=" + } +} diff --git a/kernel-cachyos/update.py b/kernel-cachyos/update.py new file mode 100644 index 0000000..1732b0a --- /dev/null +++ b/kernel-cachyos/update.py @@ -0,0 +1,233 @@ +#!/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\d+\.\d+(?:\.\d+)?(?:-rc\d+)?)-(?P\d+)$" +) + +# For sorting: extract (major, minor, patch) from version string +VERSION_PATTERN = re.compile( + r"^(?P\d+)\.(?P\d+)(?:\.(?P\d+))?(?:-rc(?P\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())