at master 4.8 kB view raw
1#!/usr/bin/env nix-shell 2#!nix-shell -i python3 -p "python3.withPackages(ps: [ ps.beautifulsoup4 ps.click ps.httpx ps.jinja2 ps.packaging ps.pyyaml ])" nix-update 3import base64 4import binascii 5import hashlib 6import json 7import pathlib 8import subprocess 9from urllib.parse import urljoin, urlparse 10 11import bs4 12import click 13import httpx 14import jinja2 15import packaging.version as v 16 17import utils 18 19 20LEAF_TEMPLATE = jinja2.Template(''' 21{ mkKdeDerivation }: 22mkKdeDerivation { 23 pname = "{{ pname }}"; 24} 25'''.strip()) 26 27ROOT_TEMPLATE = jinja2.Template(''' 28{ callPackage }: 29{ 30 {%- for p in packages %} 31 {{ p }} = callPackage ./{{ p }} { }; 32 {%- endfor %} 33} 34'''.strip()) 35 36PROJECTS_WITH_RUST = { 37 "akonadi-search", 38 "angelfish", 39 "kdepim-addons", 40} 41 42def to_sri(hash): 43 raw = binascii.unhexlify(hash) 44 b64 = base64.b64encode(raw).decode() 45 return f"sha256-{b64}" 46 47 48@click.command 49@click.argument( 50 "pkgset", 51 type=click.Choice(["frameworks", "gear", "plasma"]), 52 required=True 53) 54@click.argument( 55 "version", 56 type=str, 57 required=True 58) 59@click.option( 60 "--nixpkgs", 61 type=click.Path( 62 exists=True, 63 file_okay=False, 64 resolve_path=True, 65 writable=True, 66 path_type=pathlib.Path, 67 ), 68 default=pathlib.Path(__file__).parent.parent.parent.parent 69) 70@click.option( 71 "--sources-url", 72 type=str, 73 default=None, 74) 75def main(pkgset: str, version: str, nixpkgs: pathlib.Path, sources_url: str | None): 76 root_dir = nixpkgs / "pkgs/kde" 77 set_dir = root_dir / pkgset 78 generated_dir = root_dir / "generated" 79 metadata = utils.KDERepoMetadata.from_json(generated_dir) 80 81 if sources_url is None: 82 set_url = { 83 "frameworks": f"frameworks/{version}/", 84 "gear": f"release-service/{version}/src/", 85 "plasma": f"plasma/{version}/", 86 }[pkgset] 87 sources_url = f"https://download.kde.org/stable/{set_url}" 88 89 client = httpx.Client() 90 sources = client.get(sources_url) 91 sources.raise_for_status() 92 bs = bs4.BeautifulSoup(sources.text, features="html.parser") 93 94 results = {} 95 projects_to_update_rust = set() 96 for item in bs.select("tr")[3:]: 97 link = item.select_one("td:nth-child(2) a") 98 if not link: 99 continue 100 101 project_name, version_and_ext = link.text.rsplit("-", maxsplit=1) 102 103 if project_name not in metadata.projects_by_name: 104 print(f"Warning: unknown tarball: {project_name}") 105 106 if project_name in PROJECTS_WITH_RUST: 107 projects_to_update_rust.add(project_name) 108 109 if version_and_ext.endswith(".sig"): 110 continue 111 112 version = version_and_ext.removesuffix(".tar.xz") 113 114 url = urljoin(sources_url, link.attrs["href"]) 115 116 hash = client.get(url + ".sha256").text.strip() 117 118 if hash == "Hash type not supported": 119 print(f"{url} missing hash on CDN, downloading...") 120 hasher = hashlib.sha256() 121 with client.stream("GET", url, follow_redirects=True) as r: 122 for data in r.iter_bytes(): 123 hasher.update(data) 124 hash = hasher.hexdigest() 125 else: 126 hash = hash.split(" ", maxsplit=1)[0] 127 128 if existing := results.get(project_name): 129 old_version = existing["version"] 130 if v.parse(old_version) > v.parse(version): 131 print(f"{project_name} {old_version} is newer than {version}, skipping...") 132 continue 133 134 results[project_name] = { 135 "version": version, 136 "url": "mirror://kde" + urlparse(url).path, 137 "hash": to_sri(hash) 138 } 139 140 pkg_dir = set_dir / project_name 141 pkg_file = pkg_dir / "default.nix" 142 143 if not pkg_file.exists(): 144 print(f"Generated new package: {pkgset}/{project_name}") 145 pkg_dir.mkdir(parents=True, exist_ok=True) 146 with pkg_file.open("w") as fd: 147 fd.write(LEAF_TEMPLATE.render(pname=project_name) + "\n") 148 149 set_dir.mkdir(parents=True, exist_ok=True) 150 with (set_dir / "default.nix").open("w") as fd: 151 fd.write(ROOT_TEMPLATE.render(packages=sorted(results.keys())) + "\n") 152 153 sources_dir = generated_dir / "sources" 154 sources_dir.mkdir(parents=True, exist_ok=True) 155 with (sources_dir / f"{pkgset}.json").open("w") as fd: 156 json.dump(results, fd, indent=2) 157 158 for project_name in projects_to_update_rust: 159 print(f"Updating cargoDeps hash for {pkgset}/{project_name}...") 160 subprocess.run([ 161 "nix-update", 162 f"kdePackages.{project_name}", 163 "--version", 164 "skip", 165 "--override-filename", 166 pkg_file 167 ]) 168 169 170if __name__ == "__main__": 171 main() # type: ignore