#!/usr/bin/env python # Copyright 2015 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # See https://github.com/domokit/sky_engine/wiki/Release-process import argparse import os import subprocess import sys import distutils.util import tempfile import zipfile DRY_RUN = False def run(cwd, args): print 'RUNNING:', ' '.join(args), 'IN:', cwd if DRY_RUN: return subprocess.check_call(args, cwd=cwd) def confirm(prompt): user_input = raw_input("%s (y/N) " % prompt) try: return distutils.util.strtobool(user_input) == 1 except ValueError: return False def git_revision(cwd): return subprocess.check_output([ 'git', 'rev-parse', 'HEAD', ], cwd=cwd).strip() GS_URL = 'gs://mojo/flutter/%(commit_hash)s/%(config)s/%(name)s' # Paths of the artifacts that will be packaged into a zip file. ZIP_ARTIFACTS = { 'android-arm': [ 'chromium-debug.keystore', 'icudtl.dat', 'dist/shell/SkyShell.apk', 'dist/shell/flutter.mojo', 'gen/sky/shell/shell/classes.dex.jar', 'gen/sky/shell/shell/shell/libs/armeabi-v7a/libsky_shell.so', # TODO(mpcomplete): obsolete. Remove after updating the flutter tool. 'gen/sky/shell/shell/classes.dex', ], 'linux-x64': [ 'dist/shell/icudtl.dat', 'dist/shell/sky_shell', 'dist/shell/sky_snapshot', 'dist/shell/flutter.mojo', ], } # Paths of the artifacts that will be uploaded to GCS as individual files. FILE_ARTIFACTS = { 'android-arm': [ 'dist/shell/flutter.mojo', 'dist/shell/libflutter_library.so', ], 'linux-x64': [ 'dist/shell/flutter.mojo', 'dist/shell/libflutter_library.so', ], } def find_missing_artifacts(config, config_root): result = [] for artifact_map in [ZIP_ARTIFACTS, FILE_ARTIFACTS]: for artifact_path in artifact_map[config]: full_path = os.path.join(config_root, artifact_path) if not os.path.exists(full_path): result.append(full_path) return result # Do not try to compress file types that are already compressed. FILE_TYPE_COMPRESSION = { '.apk': zipfile.ZIP_STORED, } def upload_artifacts(dist_root, config, commit_hash): # Build and upload a zip file of artifacts zip_fd, zip_filename = tempfile.mkstemp('.zip', 'artifacts_') try: os.close(zip_fd) artifact_zip = zipfile.ZipFile(zip_filename, 'w') for artifact_path in ZIP_ARTIFACTS[config]: _, extension = os.path.splitext(artifact_path) artifact_zip.write(os.path.join(dist_root, artifact_path), os.path.basename(artifact_path), FILE_TYPE_COMPRESSION.get(extension, zipfile.ZIP_DEFLATED)) artifact_zip.close() dst = GS_URL % { 'config': config, 'commit_hash': commit_hash, 'name': 'artifacts.zip', } run(dist_root, ['gsutil', 'cp', zip_filename, dst]) finally: os.remove(zip_filename) # Upload individual file artifacts for artifact_path in FILE_ARTIFACTS[config]: dst = GS_URL % { 'config': config, 'commit_hash': commit_hash, 'name': os.path.basename(artifact_path), } z = ','.join([ 'mojo', 'so' ]) run(dist_root, ['gsutil', 'cp', '-z', z, artifact_path, dst]) def main(): parser = argparse.ArgumentParser(description='Deploy!') parser.add_argument('--dry-run', action='store_true', default=False, help='Just print commands w/o executing.') parser.add_argument('--revision', help='The git revision to publish.') args = parser.parse_args() global DRY_RUN DRY_RUN = args.dry_run engine_root = os.path.abspath('.') if not os.path.exists(os.path.join(engine_root, 'sky')): print "Cannot find //sky. Is %s the Flutter engine repository?" % engine_root return 1 commit_hash = git_revision(engine_root) if commit_hash != args.revision: print "Current revision %s does not match requested revision %s." % (commit_hash, args.revision) print "Please update the current revision to %s." % args.revision return 1 # Derived paths: dart_sdk_root = os.path.join(engine_root, 'third_party/dart-sdk/dart-sdk') pub_path = os.path.join(dart_sdk_root, 'bin/pub') android_out_root = os.path.join(engine_root, 'out/android_Release') linux_out_root = os.path.join(engine_root, 'out/Release') sky_engine_package_root = os.path.join(android_out_root, 'dist/packages/sky_engine/sky_engine') sky_services_package_root = os.path.join(android_out_root, 'dist/packages/sky_services/sky_services') sky_engine_revision_file = os.path.join(sky_engine_package_root, 'lib', 'REVISION') run(engine_root, ['sky/tools/gn', '--android', '--release']) run(engine_root, ['ninja', '-C', 'out/android_Release', ':dist']) run(engine_root, ['sky/tools/gn', '--release']) run(engine_root, ['ninja', '-C', 'out/Release', ':dist']) with open(sky_engine_revision_file, 'w') as stream: stream.write(commit_hash) configs = [('android-arm', android_out_root), ('linux-x64', linux_out_root)] # Check for missing artifact files missing_artifacts = [] for config, config_root in configs: missing_artifacts.extend(find_missing_artifacts(config, config_root)) if missing_artifacts: print ('Build is missing files:\n%s' % '\n'.join('\t%s' % path for path in missing_artifacts)) return 1 # Upload artifacts for config, config_root in configs: upload_artifacts(config_root, config, commit_hash) run(sky_engine_package_root, [pub_path, 'publish', '--force']) run(sky_services_package_root, [pub_path, 'publish', '--force']) if __name__ == '__main__': sys.exit(main())