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