at 24.11-pre 160 lines 4.4 kB view raw
1#!/usr/bin/env nix-shell 2#!nix-shell -i python3 -p "python3.withPackages (ps: [ ps.beautifulsoup4 ps.lxml ps.packaging ])" 3import json 4import os 5import pathlib 6import subprocess 7import sys 8import urllib.request 9from dataclasses import dataclass 10from enum import Enum 11 12from bs4 import BeautifulSoup, NavigableString, Tag 13from packaging.version import parse as parse_version, Version 14from typing import List 15 16HERE = pathlib.Path(__file__).parent 17ROOT = HERE.parent.parent.parent.parent 18VERSIONS_FILE = HERE / "kernels-org.json" 19 20 21class KernelNature(Enum): 22 MAINLINE = 1 23 STABLE = 2 24 LONGTERM = 3 25 26 27@dataclass 28class KernelRelease: 29 nature: KernelNature 30 version: str 31 branch: str 32 date: str 33 link: str 34 eol: bool = False 35 36 37def parse_release(release: Tag) -> KernelRelease | None: 38 columns: list[Tag] = list(release.find_all("td")) 39 try: 40 nature = KernelNature[columns[0].get_text().rstrip(":").upper()] 41 except KeyError: 42 return None 43 44 version = columns[1].get_text().rstrip(" [EOL]") 45 date = columns[2].get_text() 46 link = columns[3].find("a") 47 if link is not None and isinstance(link, Tag): 48 link = link.attrs.get("href") 49 assert link is not None, f"link for kernel {version} is non-existent" 50 eol = bool(release.find(class_="eolkernel")) 51 52 return KernelRelease( 53 nature=nature, 54 branch=get_branch(version), 55 version=version, 56 date=date, 57 link=link, 58 eol=eol, 59 ) 60 61 62def get_branch(version: str): 63 # This is a testing kernel. 64 if "rc" in version: 65 return "testing" 66 else: 67 major, minor, *_ = version.split(".") 68 return f"{major}.{minor}" 69 70 71def get_hash(kernel: KernelRelease): 72 if kernel.branch == "testing": 73 args = ["--unpack"] 74 else: 75 args = [] 76 77 hash = ( 78 subprocess.check_output(["nix-prefetch-url", kernel.link] + args) 79 .decode() 80 .strip() 81 ) 82 return f"sha256:{hash}" 83 84 85def get_oldest_branch() -> Version: 86 with open(VERSIONS_FILE) as f: 87 return parse_version(sorted(json.load(f).keys())[0]) 88 89 90def predates_oldest_branch(oldest: Version, to_compare: str) -> bool: 91 if to_compare == "testing": 92 return False 93 94 return parse_version(to_compare) < oldest 95 96 97def commit(message): 98 return subprocess.check_call(["git", "commit", "-m", message, VERSIONS_FILE]) 99 100 101def main(): 102 kernel_org = urllib.request.urlopen("https://kernel.org/") 103 soup = BeautifulSoup(kernel_org.read().decode(), "lxml") 104 release_table = soup.find(id="releases") 105 if not release_table or isinstance(release_table, NavigableString): 106 print(release_table, file=sys.stderr) 107 print("Failed to find the release table on https://kernel.org", file=sys.stderr) 108 sys.exit(1) 109 110 releases = release_table.find_all("tr") 111 parsed_releases = filter(None, [parse_release(release) for release in releases]) 112 all_kernels = json.load(VERSIONS_FILE.open()) 113 114 oldest_branch = get_oldest_branch() 115 116 for kernel in parsed_releases: 117 branch = get_branch(kernel.version) 118 nixpkgs_branch = branch.replace(".", "_") 119 120 old_version = all_kernels.get(branch, {}).get("version") 121 if old_version == kernel.version: 122 print(f"linux_{nixpkgs_branch}: {kernel.version} is latest, skipping...") 123 continue 124 125 if predates_oldest_branch(oldest_branch, kernel.branch): 126 print( 127 f"{kernel.branch} is too old and not supported anymore, skipping...", 128 file=sys.stderr 129 ) 130 continue 131 132 if old_version is None: 133 if kernel.eol: 134 print( 135 f"{kernel.branch} is EOL, not adding...", 136 file=sys.stderr 137 ) 138 continue 139 140 message = f"linux_{nixpkgs_branch}: init at {kernel.version}" 141 else: 142 message = f"linux_{nixpkgs_branch}: {old_version} -> {kernel.version}" 143 144 print(message, file=sys.stderr) 145 146 all_kernels[branch] = { 147 "version": kernel.version, 148 "hash": get_hash(kernel), 149 } 150 151 with VERSIONS_FILE.open("w") as fd: 152 json.dump(all_kernels, fd, indent=4) 153 fd.write("\n") # makes editorconfig happy 154 155 if os.environ.get("COMMIT") == "1": 156 commit(message) 157 158 159if __name__ == "__main__": 160 main()