at 25.11-pre 434 lines 14 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 17FAKE_HASH = 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' 18 19NIXPKGS_ROOT = subprocess.Popen(['git', 20 'rev-parse', 21 '--show-toplevel'], 22 stdout=subprocess.PIPE, 23 text=True).communicate()[0].strip() 24 25 26def load_code(name, **kwargs): 27 with open(f"{NIXPKGS_ROOT}/pkgs/development/compilers/flutter/update/{name}.in", 'r') as f: 28 code = f.read() 29 30 for (key, value) in kwargs.items(): 31 code = code.replace(f"@{key}@", value) 32 33 return code 34 35 36# Return out paths 37def nix_build(code): 38 temp = tempfile.NamedTemporaryFile(mode='w') 39 temp.write(code) 40 temp.flush() 41 os.fsync(temp.fileno()) 42 43 process = subprocess.Popen( 44 [ 45 "nix-build", 46 "--impure", 47 "--no-out-link", 48 "--expr", 49 f"with import {NIXPKGS_ROOT} {{}}; callPackage {temp.name} {{}}"], 50 stdout=subprocess.PIPE, 51 text=True) 52 53 process.wait() 54 temp.close() 55 return process.stdout.read().strip().splitlines()[0] 56 57 58# Return errors 59def nix_build_to_fail(code): 60 temp = tempfile.NamedTemporaryFile(mode='w') 61 temp.write(code) 62 temp.flush() 63 os.fsync(temp.fileno()) 64 65 process = subprocess.Popen( 66 [ 67 "nix-build", 68 "--impure", 69 "--keep-going", 70 "--no-link", 71 "--expr", 72 f"with import {NIXPKGS_ROOT} {{}}; callPackage {temp.name} {{}}"], 73 stderr=subprocess.PIPE, 74 text=True) 75 76 stderr = "" 77 while True: 78 line = process.stderr.readline() 79 if not line: 80 break 81 stderr += line 82 print(line.strip()) 83 84 process.wait() 85 temp.close() 86 return stderr 87 88 89def get_engine_hashes(engine_version, flutter_version): 90 code = load_code("get-engine-hashes.nix", 91 nixpkgs_root=NIXPKGS_ROOT, 92 flutter_version=flutter_version, 93 engine_version=engine_version) 94 95 stderr = nix_build_to_fail(code) 96 97 pattern = re.compile( 98 rf"/nix/store/.*-flutter-engine-source-{engine_version}-(.+?-.+?)-(.+?-.+?).drv':\n\s+specified: .*\n\s+got:\s+(.+?)\n") 99 matches = pattern.findall(stderr) 100 result_dict = {} 101 102 for match in matches: 103 flutter_platform, architecture, got = match 104 result_dict.setdefault(flutter_platform, {})[architecture] = got 105 106 def sort_dict_recursive(d): 107 return { 108 k: sort_dict_recursive(v) if isinstance( 109 v, dict) else v for k, v in sorted( 110 d.items())} 111 result_dict = sort_dict_recursive(result_dict) 112 113 return result_dict 114 115 116def get_artifact_hashes(flutter_compact_version): 117 code = load_code("get-artifact-hashes.nix", 118 nixpkgs_root=NIXPKGS_ROOT, 119 flutter_compact_version=flutter_compact_version) 120 121 stderr = nix_build_to_fail(code) 122 123 pattern = re.compile( 124 r"/nix/store/.*-flutter-artifacts-(.+?)-(.+?).drv':\n\s+specified: .*\n\s+got:\s+(.+?)\n") 125 matches = pattern.findall(stderr) 126 result_dict = {} 127 128 for match in matches: 129 flutter_platform, architecture, got = match 130 result_dict.setdefault(flutter_platform, {})[architecture] = got 131 132 def sort_dict_recursive(d): 133 return { 134 k: sort_dict_recursive(v) if isinstance( 135 v, dict) else v for k, v in sorted( 136 d.items())} 137 result_dict = sort_dict_recursive(result_dict) 138 139 return result_dict 140 141 142def get_dart_hashes(dart_version, channel): 143 platforms = [ 144 "x86_64-linux", 145 "aarch64-linux", 146 "x86_64-darwin", 147 "aarch64-darwin"] 148 result_dict = {} 149 for platform in platforms: 150 code = load_code( 151 "get-dart-hashes.nix", 152 dart_version=dart_version, 153 channel=channel, 154 platform=platform) 155 stderr = nix_build_to_fail(code) 156 157 pattern = re.compile(r"got:\s+(.+?)\n") 158 result_dict[platform] = pattern.findall(stderr)[0] 159 160 return result_dict 161 162 163def get_flutter_hash_and_src(flutter_version): 164 code = load_code( 165 "get-flutter.nix", 166 flutter_version=flutter_version, 167 hash="") 168 169 stderr = nix_build_to_fail(code) 170 pattern = re.compile(r"got:\s+(.+?)\n") 171 hash = pattern.findall(stderr)[0] 172 173 code = load_code( 174 "get-flutter.nix", 175 flutter_version=flutter_version, 176 hash=hash) 177 178 return (hash, nix_build(code)) 179 180 181def get_pubspec_lock(flutter_compact_version, flutter_src): 182 code = load_code( 183 "get-pubspec-lock.nix", 184 flutter_compact_version=flutter_compact_version, 185 flutter_src=flutter_src, 186 hash="") 187 188 stderr = nix_build_to_fail(code) 189 pattern = re.compile(r"got:\s+(.+?)\n") 190 hash = pattern.findall(stderr)[0] 191 192 code = load_code( 193 "get-pubspec-lock.nix", 194 flutter_compact_version=flutter_compact_version, 195 flutter_src=flutter_src, 196 hash=hash) 197 198 pubspec_lock_file = nix_build(code) 199 200 with open(pubspec_lock_file, 'r') as f: 201 pubspec_lock_yaml = f.read() 202 203 return yaml.safe_load(pubspec_lock_yaml) 204 205def get_engine_swiftshader_rev(engine_version): 206 with urllib.request.urlopen(f"https://github.com/flutter/flutter/raw/{engine_version}/DEPS") as f: 207 deps = f.read().decode('utf-8') 208 pattern = re.compile(r"Var\('swiftshader_git'\) \+ '\/SwiftShader\.git' \+ '@' \+ \'([0-9a-fA-F]{40})\'\,") 209 rev = pattern.findall(deps)[0] 210 return rev 211 212def get_engine_swiftshader_hash(engine_swiftshader_rev): 213 code = load_code( 214 "get-engine-swiftshader.nix", 215 engine_swiftshader_rev=engine_swiftshader_rev, 216 hash="") 217 218 stderr = nix_build_to_fail(code) 219 pattern = re.compile(r"got:\s+(.+?)\n") 220 return pattern.findall(stderr)[0] 221 222def write_data( 223 nixpkgs_flutter_version_directory, 224 flutter_version, 225 channel, 226 engine_hash, 227 engine_hashes, 228 engine_swiftshader_hash, 229 engine_swiftshader_rev, 230 dart_version, 231 dart_hash, 232 flutter_hash, 233 artifact_hashes, 234 pubspec_lock): 235 with open(f"{nixpkgs_flutter_version_directory}/data.json", "w") as f: 236 f.write(json.dumps({ 237 "version": flutter_version, 238 "engineVersion": engine_hash, 239 "engineSwiftShaderHash": engine_swiftshader_hash, 240 "engineSwiftShaderRev": engine_swiftshader_rev, 241 "channel": channel, 242 "engineHashes": engine_hashes, 243 "dartVersion": dart_version, 244 "dartHash": dart_hash, 245 "flutterHash": flutter_hash, 246 "artifactHashes": artifact_hashes, 247 "pubspecLock": pubspec_lock, 248 }, indent=2).strip() + "\n") 249 250 251def update_all_packages(): 252 versions_directory = f"{NIXPKGS_ROOT}/pkgs/development/compilers/flutter/versions" 253 versions = [directory for directory in os.listdir(versions_directory)] 254 versions = sorted(versions, key=lambda x: ( 255 int(x.split('_')[0]), int(x.split('_')[1])), reverse=True) 256 257 new_content = [ 258 "flutterPackages-bin = recurseIntoAttrs (callPackage ../development/compilers/flutter { });", 259 "flutterPackages-source = recurseIntoAttrs (callPackage ../development/compilers/flutter { useNixpkgsEngine = true; });", 260 "flutterPackages = flutterPackages-bin;", 261 "flutter = flutterPackages.stable;", 262 ] + [f"flutter{version.replace('_', '')} = flutterPackages.v{version};" for version in versions] 263 264 with open(f"{NIXPKGS_ROOT}/pkgs/top-level/all-packages.nix", 'r') as file: 265 lines = file.read().splitlines(keepends=True) 266 267 start = -1 268 end = -1 269 for i, line in enumerate(lines): 270 if "flutterPackages-bin = recurseIntoAttrs (callPackage ../development/compilers/flutter { });" in line: 271 start = i 272 if start != -1 and len(line.strip()) == 0: 273 end = i 274 break 275 276 if start != -1 and end != -1: 277 del lines[start:end] 278 lines[start:start] = [f" {l}\n" for l in new_content] 279 280 with open(f"{NIXPKGS_ROOT}/pkgs/top-level/all-packages.nix", 'w') as file: 281 file.write("".join(lines)) 282 283 284# Finds Flutter version, Dart version, and Engine hash. 285# If the Flutter version is given, it uses that. Otherwise finds the 286# latest stable Flutter version. 287def find_versions(flutter_version=None, channel=None): 288 engine_hash = None 289 dart_version = None 290 291 releases = json.load(urllib.request.urlopen( 292 "https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json")) 293 294 if not channel: 295 channel = 'stable' 296 297 if not flutter_version: 298 hash = releases['current_release'][channel] 299 release = next( 300 filter( 301 lambda release: release['hash'] == hash, 302 releases['releases'])) 303 flutter_version = release['version'] 304 305 tags = subprocess.Popen(['git', 306 'ls-remote', 307 '--tags', 308 'https://github.com/flutter/flutter.git'], 309 stdout=subprocess.PIPE, 310 text=True).communicate()[0].strip() 311 312 try: 313 flutter_hash = next( 314 filter( 315 lambda line: line.endswith(f'refs/tags/{flutter_version}'), 316 tags.splitlines())).split('refs')[0].strip() 317 318 engine_hash = urllib.request.urlopen(f'https://github.com/flutter/flutter/raw/{flutter_hash}/bin/internal/engine.version').read().decode('utf-8').strip() 319 except StopIteration: 320 exit( 321 f"Couldn't find Engine hash for Flutter version: {flutter_version}") 322 323 try: 324 dart_version = next( 325 filter( 326 lambda release: release['version'] == flutter_version, 327 releases['releases']))['dart_sdk_version'] 328 329 if " " in dart_version: 330 dart_version = dart_version.split(' ')[2][:-1] 331 except StopIteration: 332 exit( 333 f"Couldn't find Dart version for Flutter version: {flutter_version}") 334 335 return (flutter_version, engine_hash, dart_version, channel) 336 337 338def main(): 339 parser = argparse.ArgumentParser(description='Update Flutter in Nixpkgs') 340 parser.add_argument('--version', type=str, help='Specify Flutter version') 341 parser.add_argument('--channel', type=str, help='Specify Flutter release channel') 342 parser.add_argument('--artifact-hashes', action='store_true', 343 help='Whether to get artifact hashes') 344 args = parser.parse_args() 345 346 (flutter_version, engine_hash, dart_version, channel) = find_versions(args.version, args.channel) 347 348 flutter_compact_version = '_'.join(flutter_version.split('.')[:2]) 349 350 if args.artifact_hashes: 351 print( 352 json.dumps( 353 get_artifact_hashes(flutter_compact_version), 354 indent=2).strip() + 355 "\n") 356 return 357 358 print(f"Flutter version: {flutter_version} ({flutter_compact_version}) on ({channel})") 359 print(f"Engine hash: {engine_hash}") 360 print(f"Dart version: {dart_version}") 361 362 dart_hash = get_dart_hashes(dart_version, channel) 363 (flutter_hash, flutter_src) = get_flutter_hash_and_src(flutter_version) 364 365 nixpkgs_flutter_version_directory = f"{NIXPKGS_ROOT}/pkgs/development/compilers/flutter/versions/{flutter_compact_version}" 366 367 if os.path.exists(f"{nixpkgs_flutter_version_directory}/data.json"): 368 os.remove(f"{nixpkgs_flutter_version_directory}/data.json") 369 os.makedirs(nixpkgs_flutter_version_directory, exist_ok=True) 370 371 update_all_packages() 372 373 common_data_args = { 374 "nixpkgs_flutter_version_directory": nixpkgs_flutter_version_directory, 375 "flutter_version": flutter_version, 376 "channel": channel, 377 "dart_version": dart_version, 378 "engine_hash": engine_hash, 379 "flutter_hash": flutter_hash, 380 "dart_hash": dart_hash, 381 } 382 383 write_data( 384 pubspec_lock={}, 385 artifact_hashes={}, 386 engine_hashes={}, 387 engine_swiftshader_hash=FAKE_HASH, 388 engine_swiftshader_rev='0', 389 **common_data_args) 390 391 pubspec_lock = get_pubspec_lock(flutter_compact_version, flutter_src) 392 393 write_data( 394 pubspec_lock=pubspec_lock, 395 artifact_hashes={}, 396 engine_hashes={}, 397 engine_swiftshader_hash=FAKE_HASH, 398 engine_swiftshader_rev='0', 399 **common_data_args) 400 401 artifact_hashes = get_artifact_hashes(flutter_compact_version) 402 403 write_data( 404 pubspec_lock=pubspec_lock, 405 artifact_hashes=artifact_hashes, 406 engine_hashes={}, 407 engine_swiftshader_hash=FAKE_HASH, 408 engine_swiftshader_rev='0', 409 **common_data_args) 410 411 engine_hashes = get_engine_hashes(engine_hash, flutter_version) 412 413 write_data( 414 pubspec_lock=pubspec_lock, 415 artifact_hashes=artifact_hashes, 416 engine_hashes=engine_hashes, 417 engine_swiftshader_hash=FAKE_HASH, 418 engine_swiftshader_rev='0', 419 **common_data_args) 420 421 engine_swiftshader_rev = get_engine_swiftshader_rev(engine_hash) 422 engine_swiftshader_hash = get_engine_swiftshader_hash(engine_swiftshader_rev) 423 424 write_data( 425 pubspec_lock=pubspec_lock, 426 artifact_hashes=artifact_hashes, 427 engine_hashes=engine_hashes, 428 engine_swiftshader_hash=engine_swiftshader_hash, 429 engine_swiftshader_rev=engine_swiftshader_rev, 430 **common_data_args) 431 432 433if __name__ == "__main__": 434 main()