Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at python-updates 182 lines 5.6 kB view raw
1import json 2import os 3import pathlib 4import requests 5import shutil 6import subprocess 7import sys 8import tempfile 9 10 11def replace_in_file(file_path, replacements): 12 file_contents = pathlib.Path(file_path).read_text() 13 for old, new in replacements.items(): 14 if old == new: 15 continue 16 updated_file_contents = file_contents.replace(old, new) 17 # A dumb way to check that we’ve actually replaced the string. 18 if file_contents == updated_file_contents: 19 print(f"no string to replace: {old}{new}", file=sys.stderr) 20 sys.exit(1) 21 file_contents = updated_file_contents 22 with tempfile.NamedTemporaryFile(mode="w") as t: 23 t.write(file_contents) 24 t.flush() 25 shutil.copyfile(t.name, file_path) 26 27 28def nix_hash_to_sri(hash): 29 return subprocess.run( 30 [ 31 "nix", 32 "--extra-experimental-features", "nix-command", 33 "hash", 34 "to-sri", 35 "--type", "sha256", 36 "--", 37 hash, 38 ], 39 stdout=subprocess.PIPE, 40 text=True, 41 check=True, 42 ).stdout.rstrip() 43 44 45nixpkgs_path = "." 46attr_path = os.getenv("UPDATE_NIX_ATTR_PATH", "prowlarr") 47 48package_attrs = json.loads(subprocess.run( 49 [ 50 "nix", 51 "--extra-experimental-features", "nix-command", 52 "eval", 53 "--json", 54 "--file", nixpkgs_path, 55 "--apply", """p: { 56 dir = builtins.dirOf p.meta.position; 57 version = p.version; 58 sourceHash = p.src.src.outputHash; 59 yarnHash = p.yarnOfflineCache.outputHash; 60 }""", 61 "--", 62 attr_path, 63 ], 64 stdout=subprocess.PIPE, 65 text=True, 66 check=True, 67).stdout) 68 69old_version = package_attrs["version"] 70new_version = old_version 71 72# Note that we use Prowlarr API instead of GitHub to fetch latest stable release. 73# This corresponds to the Updates tab in the web UI. See also 74# https://github.com/Prowlarr/Prowlarr/blob/7d813ef97a01af0f36a2beaec32e9cd854fc67f3/src/NzbDrone.Core/Update/UpdatePackageProvider.cs 75# https://github.com/Prowlarr/Prowlarr/blob/7d813ef97a01af0f36a2beaec32e9cd854fc67f3/src/NzbDrone.Common/Cloud/ProwlarrCloudRequestBuilder.cs 76version_update = requests.get( 77 f"https://prowlarr.servarr.com/v1/update/master?version={old_version}&includeMajorVersion=true", 78).json() 79if version_update["available"]: 80 new_version = version_update["updatePackage"]["version"] 81 82if new_version == old_version: 83 sys.exit() 84 85source_nix_hash, source_store_path = subprocess.run( 86 [ 87 "nix-prefetch-url", 88 "--name", "source", 89 "--unpack", 90 "--print-path", 91 f"https://github.com/Prowlarr/Prowlarr/archive/v{new_version}.tar.gz", 92 ], 93 stdout=subprocess.PIPE, 94 text=True, 95 check=True, 96).stdout.rstrip().split("\n") 97 98old_source_hash = package_attrs["sourceHash"] 99new_source_hash = nix_hash_to_sri(source_nix_hash) 100 101package_dir = package_attrs["dir"] 102package_file_name = "package.nix" 103deps_file_name = "deps.json" 104 105# To update deps.nix, we copy the package to a temporary directory and run 106# passthru.fetch-deps script there. 107with tempfile.TemporaryDirectory() as work_dir: 108 package_file = os.path.join(work_dir, package_file_name) 109 deps_file = os.path.join(work_dir, deps_file_name) 110 111 shutil.copytree(package_dir, work_dir, dirs_exist_ok=True) 112 113 replace_in_file(package_file, { 114 # NB unlike hashes, versions are likely to be used in code or comments. 115 # Try to be more specific to avoid false positive matches. 116 f"version = \"{old_version}\"": f"version = \"{new_version}\"", 117 old_source_hash: new_source_hash, 118 }) 119 120 # We need access to the patched and updated src to get the patched 121 # `yarn.lock`. 122 patched_src = os.path.join(work_dir, "patched-src") 123 subprocess.run( 124 [ 125 "nix", 126 "--extra-experimental-features", "nix-command", 127 "build", 128 "--impure", 129 "--nix-path", "", 130 "--include", f"nixpkgs={nixpkgs_path}", 131 "--include", f"package={package_file}", 132 "--expr", "(import <nixpkgs> { }).callPackage <package> { }", 133 "--out-link", patched_src, 134 "src", 135 ], 136 check=True, 137 ) 138 old_yarn_hash = package_attrs["yarnHash"] 139 new_yarn_hash = nix_hash_to_sri(subprocess.run( 140 [ 141 "prefetch-yarn-deps", 142 # does not support "--" separator :( 143 # Also --verbose writes to stdout, yikes. 144 os.path.join(patched_src, "yarn.lock"), 145 ], 146 stdout=subprocess.PIPE, 147 text=True, 148 check=True, 149 ).stdout.rstrip()) 150 151 replace_in_file(package_file, { 152 old_yarn_hash: new_yarn_hash, 153 }) 154 155 # Generate nuget-to-json dependency lock file. 156 fetch_deps = os.path.join(work_dir, "fetch-deps") 157 subprocess.run( 158 [ 159 "nix", 160 "--extra-experimental-features", "nix-command", 161 "build", 162 "--impure", 163 "--nix-path", "", 164 "--include", f"nixpkgs={nixpkgs_path}", 165 "--include", f"package={package_file}", 166 "--expr", "(import <nixpkgs> { }).callPackage <package> { }", 167 "--out-link", fetch_deps, 168 "passthru.fetch-deps", 169 ], 170 check=True, 171 ) 172 subprocess.run( 173 [ 174 fetch_deps, 175 deps_file, 176 ], 177 stdout=subprocess.DEVNULL, 178 check=True, 179 ) 180 181 shutil.copy(deps_file, os.path.join(package_dir, deps_file_name)) 182 shutil.copy(package_file, os.path.join(package_dir, package_file_name))