at 24.11-pre 339 lines 10 kB view raw
1#! /usr/bin/env nix-shell 2#! nix-shell -i python3 -p python3Packages.pyyaml 3 4import shutil 5import json 6import urllib.request 7import tempfile 8from sys import exit 9import os 10import subprocess 11import re 12import json 13import argparse 14import yaml 15import json 16 17 18NIXPKGS_ROOT = subprocess.Popen(['git', 19 'rev-parse', 20 '--show-toplevel'], 21 stdout=subprocess.PIPE, 22 text=True).communicate()[0].strip() 23 24 25def load_code(name, **kwargs): 26 with open(f"{NIXPKGS_ROOT}/pkgs/development/compilers/flutter/update/{name}.in", 'r') as f: 27 code = f.read() 28 29 for (key, value) in kwargs.items(): 30 code = code.replace(f"@{key}@", value) 31 32 return code 33 34 35# Return out paths 36def nix_build(code): 37 temp = tempfile.NamedTemporaryFile(mode='w') 38 temp.write(code) 39 temp.flush() 40 os.fsync(temp.fileno()) 41 42 process = subprocess.Popen( 43 [ 44 "nix-build", 45 "--impure", 46 "--no-out-link", 47 "--expr", 48 f"with import {NIXPKGS_ROOT} {{}}; callPackage {temp.name} {{}}"], 49 stdout=subprocess.PIPE, 50 text=True) 51 52 process.wait() 53 temp.close() 54 return process.stdout.read().strip().splitlines()[0] 55 56 57# Return errors 58def nix_build_to_fail(code): 59 temp = tempfile.NamedTemporaryFile(mode='w') 60 temp.write(code) 61 temp.flush() 62 os.fsync(temp.fileno()) 63 64 process = subprocess.Popen( 65 [ 66 "nix-build", 67 "--impure", 68 "--keep-going", 69 "--no-link", 70 "--expr", 71 f"with import {NIXPKGS_ROOT} {{}}; callPackage {temp.name} {{}}"], 72 stderr=subprocess.PIPE, 73 text=True) 74 75 stderr = "" 76 while True: 77 line = process.stderr.readline() 78 if not line: 79 break 80 stderr += line 81 print(line.strip()) 82 83 process.wait() 84 temp.close() 85 return stderr 86 87 88def get_artifact_hashes(flutter_compact_version): 89 code = load_code("get-artifact-hashes.nix", 90 nixpkgs_root=NIXPKGS_ROOT, 91 flutter_compact_version=flutter_compact_version) 92 93 stderr = nix_build_to_fail(code) 94 95 pattern = re.compile( 96 r"/nix/store/.*-flutter-artifacts-(.+?)-(.+?).drv':\n\s+specified: .*\n\s+got:\s+(.+?)\n") 97 matches = pattern.findall(stderr) 98 result_dict = {} 99 100 for match in matches: 101 flutter_platform, architecture, got = match 102 result_dict.setdefault(flutter_platform, {})[architecture] = got 103 104 def sort_dict_recursive(d): 105 return { 106 k: sort_dict_recursive(v) if isinstance( 107 v, dict) else v for k, v in sorted( 108 d.items())} 109 result_dict = sort_dict_recursive(result_dict) 110 111 return result_dict 112 113 114def get_dart_hashes(dart_version): 115 platforms = [ 116 "x86_64-linux", 117 "aarch64-linux", 118 "x86_64-darwin", 119 "aarch64-darwin"] 120 result_dict = {} 121 for platform in platforms: 122 code = load_code( 123 "get-dart-hashes.nix", 124 dart_version=dart_version, 125 platform=platform) 126 stderr = nix_build_to_fail(code) 127 128 pattern = re.compile(r"got:\s+(.+?)\n") 129 result_dict[platform] = pattern.findall(stderr)[0] 130 131 return result_dict 132 133 134def get_flutter_hash_and_src(flutter_version): 135 code = load_code( 136 "get-flutter.nix", 137 flutter_version=flutter_version, 138 hash="") 139 140 stderr = nix_build_to_fail(code) 141 pattern = re.compile(r"got:\s+(.+?)\n") 142 hash = pattern.findall(stderr)[0] 143 144 code = load_code( 145 "get-flutter.nix", 146 flutter_version=flutter_version, 147 hash=hash) 148 149 return (hash, nix_build(code)) 150 151 152def get_pubspec_lock(flutter_compact_version, flutter_src): 153 code = load_code( 154 "get-pubspec-lock.nix", 155 flutter_compact_version=flutter_compact_version, 156 flutter_src=flutter_src, 157 hash="") 158 159 stderr = nix_build_to_fail(code) 160 pattern = re.compile(r"got:\s+(.+?)\n") 161 hash = pattern.findall(stderr)[0] 162 163 code = load_code( 164 "get-pubspec-lock.nix", 165 flutter_compact_version=flutter_compact_version, 166 flutter_src=flutter_src, 167 hash=hash) 168 169 pubspec_lock_file = nix_build(code) 170 171 with open(pubspec_lock_file, 'r') as f: 172 pubspec_lock_yaml = f.read() 173 174 return yaml.safe_load(pubspec_lock_yaml) 175 176 177def write_data( 178 nixpkgs_flutter_version_directory, 179 flutter_version, 180 engine_hash, 181 dart_version, 182 dart_hash, 183 flutter_hash, 184 artifact_hashes, 185 pubspec_lock): 186 with open(f"{nixpkgs_flutter_version_directory}/data.json", "w") as f: 187 f.write(json.dumps({ 188 "version": flutter_version, 189 "engineVersion": engine_hash, 190 "dartVersion": dart_version, 191 "dartHash": dart_hash, 192 "flutterHash": flutter_hash, 193 "artifactHashes": artifact_hashes, 194 "pubspecLock": pubspec_lock, 195 }, indent=2).strip() + "\n") 196 197 198def update_all_packages(): 199 versions_directory = f"{NIXPKGS_ROOT}/pkgs/development/compilers/flutter/versions" 200 versions = [directory for directory in os.listdir(versions_directory)] 201 versions = sorted(versions, key=lambda x: ( 202 int(x.split('_')[0]), int(x.split('_')[1])), reverse=True) 203 204 new_content = [ 205 "flutterPackages = recurseIntoAttrs (callPackage ../development/compilers/flutter { });", 206 "flutter = flutterPackages.stable;", 207 ] + [f"flutter{version.replace('_', '')} = flutterPackages.v{version};" for version in versions] 208 209 with open(f"{NIXPKGS_ROOT}/pkgs/top-level/all-packages.nix", 'r') as file: 210 lines = file.read().splitlines(keepends=True) 211 212 start = -1 213 end = -1 214 for i, line in enumerate(lines): 215 if "flutterPackages = recurseIntoAttrs (callPackage ../development/compilers/flutter { });" in line: 216 start = i 217 if start != -1 and len(line.strip()) == 0: 218 end = i 219 break 220 221 if start != -1 and end != -1: 222 del lines[start:end] 223 lines[start:start] = [f" {l}\n" for l in new_content] 224 225 with open(f"{NIXPKGS_ROOT}/pkgs/top-level/all-packages.nix", 'w') as file: 226 file.write("".join(lines)) 227 228 229# Finds Flutter version, Dart version, and Engine hash. 230# If the Flutter version is given, it uses that. Otherwise finds the 231# latest stable Flutter version. 232def find_versions(flutter_version=None): 233 engine_hash = None 234 dart_version = None 235 236 releases = json.load(urllib.request.urlopen( 237 "https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json")) 238 239 if not flutter_version: 240 stable_hash = releases['current_release']['stable'] 241 release = next( 242 filter( 243 lambda release: release['hash'] == stable_hash, 244 releases['releases'])) 245 flutter_version = release['version'] 246 247 tags = subprocess.Popen(['git', 248 'ls-remote', 249 '--tags', 250 'https://github.com/flutter/engine.git'], 251 stdout=subprocess.PIPE, 252 text=True).communicate()[0].strip() 253 254 try: 255 engine_hash = next( 256 filter( 257 lambda line: line.endswith(f'refs/tags/{flutter_version}'), 258 tags.splitlines())).split('refs')[0].strip() 259 except StopIteration: 260 exit( 261 f"Couldn't find Engine hash for Flutter version: {flutter_version}") 262 263 try: 264 dart_version = next( 265 filter( 266 lambda release: release['version'] == flutter_version, 267 releases['releases']))['dart_sdk_version'] 268 except StopIteration: 269 exit( 270 f"Couldn't find Dart version for Flutter version: {flutter_version}") 271 272 return (flutter_version, engine_hash, dart_version) 273 274 275def main(): 276 parser = argparse.ArgumentParser(description='Update Flutter in Nixpkgs') 277 parser.add_argument('--version', type=str, help='Specify Flutter version') 278 parser.add_argument('--artifact-hashes', action='store_true', 279 help='Whether to get artifact hashes') 280 args = parser.parse_args() 281 282 (flutter_version, engine_hash, dart_version) = find_versions(args.version) 283 284 flutter_compact_version = '_'.join(flutter_version.split('.')[:2]) 285 286 if args.artifact_hashes: 287 print( 288 json.dumps( 289 get_artifact_hashes(flutter_compact_version), 290 indent=2).strip() + 291 "\n") 292 return 293 294 print(f"Flutter version: {flutter_version} ({flutter_compact_version})") 295 print(f"Engine hash: {engine_hash}") 296 print(f"Dart version: {dart_version}") 297 298 dart_hash = get_dart_hashes(dart_version) 299 (flutter_hash, flutter_src) = get_flutter_hash_and_src(flutter_version) 300 301 nixpkgs_flutter_version_directory = f"{NIXPKGS_ROOT}/pkgs/development/compilers/flutter/versions/{flutter_compact_version}" 302 303 if os.path.exists(f"{nixpkgs_flutter_version_directory}/data.json"): 304 os.remove(f"{nixpkgs_flutter_version_directory}/data.json") 305 os.makedirs(nixpkgs_flutter_version_directory, exist_ok=True) 306 307 update_all_packages() 308 309 common_data_args = { 310 "nixpkgs_flutter_version_directory": nixpkgs_flutter_version_directory, 311 "flutter_version": flutter_version, 312 "dart_version": dart_version, 313 "engine_hash": engine_hash, 314 "flutter_hash": flutter_hash, 315 "dart_hash": dart_hash, 316 } 317 318 write_data( 319 pubspec_lock={}, 320 artifact_hashes={}, 321 **common_data_args) 322 323 pubspec_lock = get_pubspec_lock(flutter_compact_version, flutter_src) 324 325 write_data( 326 pubspec_lock=pubspec_lock, 327 artifact_hashes={}, 328 **common_data_args) 329 330 artifact_hashes = get_artifact_hashes(flutter_compact_version) 331 332 write_data( 333 pubspec_lock=pubspec_lock, 334 artifact_hashes=artifact_hashes, 335 **common_data_args) 336 337 338if __name__ == "__main__": 339 main()