nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at python-updates 181 lines 5.4 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", "sonarr") 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 = 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 Sonarr 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/Sonarr/Sonarr/blob/070919a7e6a96ca7e26524996417c6f8d1b5fcaa/src/NzbDrone.Core/Update/UpdatePackageProvider.cs 75version_update = requests.get( 76 f"https://services.sonarr.tv/v1/update/main?version={old_version}", 77).json() 78if version_update["available"]: 79 new_version = version_update["updatePackage"]["version"] 80 81if new_version == old_version: 82 sys.exit() 83 84source_nix_hash, source_store_path = subprocess.run( 85 [ 86 "nix-prefetch-url", 87 "--name", "source", 88 "--unpack", 89 "--print-path", 90 f"https://github.com/Sonarr/Sonarr/archive/v{new_version}.tar.gz", 91 ], 92 stdout=subprocess.PIPE, 93 text=True, 94 check=True, 95).stdout.rstrip().split("\n") 96 97old_source_hash = package_attrs["sourceHash"] 98new_source_hash = nix_hash_to_sri(source_nix_hash) 99 100package_dir = package_attrs["dir"] 101package_file_name = "package.nix" 102deps_file_name = "deps.json" 103 104# To update deps.nix, we copy the package to a temporary directory and run 105# passthru.fetch-deps script there. 106with tempfile.TemporaryDirectory() as work_dir: 107 package_file = os.path.join(work_dir, package_file_name) 108 deps_file = os.path.join(work_dir, deps_file_name) 109 110 shutil.copytree(package_dir, work_dir, dirs_exist_ok=True) 111 112 replace_in_file(package_file, { 113 # NB unlike hashes, versions are likely to be used in code or comments. 114 # Try to be more specific to avoid false positive matches. 115 f"version = \"{old_version}\"": f"version = \"{new_version}\"", 116 old_source_hash: new_source_hash, 117 }) 118 119 # We need access to the patched and updated src to get the patched 120 # `yarn.lock`. 121 patched_src = os.path.join(work_dir, "patched-src") 122 subprocess.run( 123 [ 124 "nix", 125 "--extra-experimental-features", "nix-command", 126 "build", 127 "--impure", 128 "--nix-path", "", 129 "--include", f"nixpkgs={nixpkgs_path}", 130 "--include", f"package={package_file}", 131 "--expr", "(import <nixpkgs> { }).callPackage <package> { }", 132 "--out-link", patched_src, 133 "src", 134 ], 135 check=True, 136 ) 137 old_yarn_hash = package_attrs["yarnHash"] 138 new_yarn_hash = nix_hash_to_sri(subprocess.run( 139 [ 140 "prefetch-yarn-deps", 141 # does not support "--" separator :( 142 # Also --verbose writes to stdout, yikes. 143 os.path.join(patched_src, "yarn.lock"), 144 ], 145 stdout=subprocess.PIPE, 146 text=True, 147 check=True, 148 ).stdout.rstrip()) 149 150 replace_in_file(package_file, { 151 old_yarn_hash: new_yarn_hash, 152 }) 153 154 # Generate nuget-to-json dependency lock file. 155 fetch_deps = os.path.join(work_dir, "fetch-deps") 156 subprocess.run( 157 [ 158 "nix", 159 "--extra-experimental-features", "nix-command", 160 "build", 161 "--impure", 162 "--nix-path", "", 163 "--include", f"nixpkgs={nixpkgs_path}", 164 "--include", f"package={package_file}", 165 "--expr", "(import <nixpkgs> { }).callPackage <package> { }", 166 "--out-link", fetch_deps, 167 "passthru.fetch-deps", 168 ], 169 check=True, 170 ) 171 subprocess.run( 172 [ 173 fetch_deps, 174 deps_file, 175 ], 176 stdout=subprocess.DEVNULL, 177 check=True, 178 ) 179 180 shutil.copy(deps_file, os.path.join(package_dir, deps_file_name)) 181 shutil.copy(package_file, os.path.join(package_dir, package_file_name))