diff options
Diffstat (limited to 'pkgs/development/compilers/flutter/update/update.py')
-rwxr-xr-x | pkgs/development/compilers/flutter/update/update.py | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/pkgs/development/compilers/flutter/update/update.py b/pkgs/development/compilers/flutter/update/update.py new file mode 100755 index 000000000000..c622487cf9dc --- /dev/null +++ b/pkgs/development/compilers/flutter/update/update.py @@ -0,0 +1,339 @@ +#! /usr/bin/env nix-shell +#! nix-shell -i python3 -p python3Packages.pyyaml + +import shutil +import json +import urllib.request +import tempfile +from sys import exit +import os +import subprocess +import re +import json +import argparse +import yaml +import json + + +NIXPKGS_ROOT = subprocess.Popen(['git', + 'rev-parse', + '--show-toplevel'], + stdout=subprocess.PIPE, + text=True).communicate()[0].strip() + + +def load_code(name, **kwargs): + with open(f"{NIXPKGS_ROOT}/pkgs/development/compilers/flutter/update/{name}", 'r') as f: + code = f.read() + + for (key, value) in kwargs.items(): + code = code.replace(f"@{key}@", value) + + return code + + +# Return out paths +def nix_build(code): + temp = tempfile.NamedTemporaryFile(mode='w') + temp.write(code) + temp.flush() + os.fsync(temp.fileno()) + + process = subprocess.Popen( + [ + "nix-build", + "--impure", + "--no-out-link", + "--expr", + f"with import {NIXPKGS_ROOT} {{}}; callPackage {temp.name} {{}}"], + stdout=subprocess.PIPE, + text=True) + + process.wait() + temp.close() + return process.stdout.read().strip().splitlines()[0] + + +# Return errors +def nix_build_to_fail(code): + temp = tempfile.NamedTemporaryFile(mode='w') + temp.write(code) + temp.flush() + os.fsync(temp.fileno()) + + process = subprocess.Popen( + [ + "nix-build", + "--impure", + "--keep-going", + "--no-link", + "--expr", + f"with import {NIXPKGS_ROOT} {{}}; callPackage {temp.name} {{}}"], + stderr=subprocess.PIPE, + text=True) + + stderr = "" + while True: + line = process.stderr.readline() + if not line: + break + stderr += line + print(line.strip()) + + process.wait() + temp.close() + return stderr + + +def get_artifact_hashes(flutter_compact_version): + code = load_code("get-artifact-hashes.nix", + nixpkgs_root=NIXPKGS_ROOT, + flutter_compact_version=flutter_compact_version) + + stderr = nix_build_to_fail(code) + + pattern = re.compile( + r"/nix/store/.*-flutter-artifacts-(.+?)-(.+?).drv':\n\s+specified: .*\n\s+got:\s+(.+?)\n") + matches = pattern.findall(stderr) + result_dict = {} + + for match in matches: + flutter_platform, architecture, got = match + result_dict.setdefault(flutter_platform, {})[architecture] = got + + def sort_dict_recursive(d): + return { + k: sort_dict_recursive(v) if isinstance( + v, dict) else v for k, v in sorted( + d.items())} + result_dict = sort_dict_recursive(result_dict) + + return result_dict + + +def get_dart_hashes(dart_version): + platforms = [ + "x86_64-linux", + "aarch64-linux", + "x86_64-darwin", + "aarch64-darwin"] + result_dict = {} + for platform in platforms: + code = load_code( + "get-dart-hashes.nix", + dart_version=dart_version, + platform=platform) + stderr = nix_build_to_fail(code) + + pattern = re.compile(r"got:\s+(.+?)\n") + result_dict[platform] = pattern.findall(stderr)[0] + + return result_dict + + +def get_flutter_hash_and_src(flutter_version): + code = load_code( + "get-flutter.nix", + flutter_version=flutter_version, + hash="") + + stderr = nix_build_to_fail(code) + pattern = re.compile(r"got:\s+(.+?)\n") + hash = pattern.findall(stderr)[0] + + code = load_code( + "get-flutter.nix", + flutter_version=flutter_version, + hash=hash) + + return (hash, nix_build(code)) + + +def get_pubspec_lock(flutter_compact_version, flutter_src): + code = load_code( + "get-pubspec-lock.nix", + flutter_compact_version=flutter_compact_version, + flutter_src=flutter_src, + hash="") + + stderr = nix_build_to_fail(code) + pattern = re.compile(r"got:\s+(.+?)\n") + hash = pattern.findall(stderr)[0] + + code = load_code( + "get-pubspec-lock.nix", + flutter_compact_version=flutter_compact_version, + flutter_src=flutter_src, + hash=hash) + + pubspec_lock_file = nix_build(code) + + with open(pubspec_lock_file, 'r') as f: + pubspec_lock_yaml = f.read() + + return yaml.safe_load(pubspec_lock_yaml) + + +def write_data( + nixpkgs_flutter_version_directory, + flutter_version, + engine_hash, + dart_version, + dart_hash, + flutter_hash, + artifact_hashes, + pubspec_lock): + with open(f"{nixpkgs_flutter_version_directory}/data.json", "w") as f: + f.write(json.dumps({ + "version": flutter_version, + "engineVersion": engine_hash, + "dartVersion": dart_version, + "dartHash": dart_hash, + "flutterHash": flutter_hash, + "artifactHashes": artifact_hashes, + "pubspecLock": pubspec_lock, + }, indent=2).strip() + "\n") + + +def update_all_packages(): + versions_directory = f"{NIXPKGS_ROOT}/pkgs/development/compilers/flutter/versions" + versions = [directory for directory in os.listdir(versions_directory)] + versions = sorted(versions, key=lambda x: ( + int(x.split('_')[0]), int(x.split('_')[1])), reverse=True) + + new_content = [ + "flutterPackages = recurseIntoAttrs (callPackage ../development/compilers/flutter { });", + "flutter = flutterPackages.stable;", + ] + [f"flutter{version.replace('_', '')} = flutterPackages.v{version};" for version in versions] + + with open(f"{NIXPKGS_ROOT}/pkgs/top-level/all-packages.nix", 'r') as file: + lines = file.read().splitlines(keepends=True) + + start = -1 + end = -1 + for i, line in enumerate(lines): + if "flutterPackages = recurseIntoAttrs (callPackage ../development/compilers/flutter { });" in line: + start = i + if start != -1 and len(line.strip()) == 0: + end = i + break + + if start != -1 and end != -1: + del lines[start:end] + lines[start:start] = [f" {l}\n" for l in new_content] + + with open(f"{NIXPKGS_ROOT}/pkgs/top-level/all-packages.nix", 'w') as file: + file.write("".join(lines)) + + +# Finds Flutter version, Dart version, and Engine hash. +# If the Flutter version is given, it uses that. Otherwise finds the +# latest stable Flutter version. +def find_versions(flutter_version=None): + engine_hash = None + dart_version = None + + releases = json.load(urllib.request.urlopen( + "https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json")) + + if not flutter_version: + stable_hash = releases['current_release']['stable'] + release = next( + filter( + lambda release: release['hash'] == stable_hash, + releases['releases'])) + flutter_version = release['version'] + + tags = subprocess.Popen(['git', + 'ls-remote', + '--tags', + 'https://github.com/flutter/engine.git'], + stdout=subprocess.PIPE, + text=True).communicate()[0].strip() + + try: + engine_hash = next( + filter( + lambda line: line.endswith(f'refs/tags/{flutter_version}'), + tags.splitlines())).split('refs')[0].strip() + except StopIteration: + exit( + f"Couldn't find Engine hash for Flutter version: {flutter_version}") + + try: + dart_version = next( + filter( + lambda release: release['version'] == flutter_version, + releases['releases']))['dart_sdk_version'] + except StopIteration: + exit( + f"Couldn't find Dart version for Flutter version: {flutter_version}") + + return (flutter_version, engine_hash, dart_version) + + +def main(): + parser = argparse.ArgumentParser(description='Update Flutter in Nixpkgs') + parser.add_argument('--version', type=str, help='Specify Flutter version') + parser.add_argument('--artifact-hashes', action='store_true', + help='Whether to get artifact hashes') + args = parser.parse_args() + + (flutter_version, engine_hash, dart_version) = find_versions(args.version) + + flutter_compact_version = '_'.join(flutter_version.split('.')[:2]) + + if args.artifact_hashes: + print( + json.dumps( + get_artifact_hashes(flutter_compact_version), + indent=2).strip() + + "\n") + return + + print(f"Flutter version: {flutter_version} ({flutter_compact_version})") + print(f"Engine hash: {engine_hash}") + print(f"Dart version: {dart_version}") + + dart_hash = get_dart_hashes(dart_version) + (flutter_hash, flutter_src) = get_flutter_hash_and_src(flutter_version) + + nixpkgs_flutter_version_directory = f"{NIXPKGS_ROOT}/pkgs/development/compilers/flutter/versions/{flutter_compact_version}" + + if os.path.exists(f"{nixpkgs_flutter_version_directory}/data.json"): + os.remove(f"{nixpkgs_flutter_version_directory}/data.json") + os.makedirs(nixpkgs_flutter_version_directory, exist_ok=True) + + update_all_packages() + + common_data_args = { + "nixpkgs_flutter_version_directory": nixpkgs_flutter_version_directory, + "flutter_version": flutter_version, + "dart_version": dart_version, + "engine_hash": engine_hash, + "flutter_hash": flutter_hash, + "dart_hash": dart_hash, + } + + write_data( + pubspec_lock={}, + artifact_hashes={}, + **common_data_args) + + pubspec_lock = get_pubspec_lock(flutter_compact_version, flutter_src) + + write_data( + pubspec_lock=pubspec_lock, + artifact_hashes={}, + **common_data_args) + + artifact_hashes = get_artifact_hashes(flutter_compact_version) + + write_data( + pubspec_lock=pubspec_lock, + artifact_hashes=artifact_hashes, + **common_data_args) + + +if __name__ == "__main__": + main() |