| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # | |
| 3 # Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 4 # for details. All rights reserved. Use of this source code is governed by a | |
| 5 # BSD-style license that can be found in the LICENSE file. | |
| 6 | |
| 7 import imp | |
| 8 import optparse | |
| 9 import os | |
| 10 import subprocess | |
| 11 import sys | |
| 12 | |
| 13 DART_DIR = os.path.dirname(os.path.dirname(__file__)) | |
| 14 GSUTIL = os.path.join(DART_DIR, 'third_party', 'gsutil', 'gsutil') | |
| 15 BOT_UTILS = os.path.join(DART_DIR, 'tools', 'bots', 'bot_utils.py') | |
| 16 BASENAME_PATTERN = 'darteditor-%(system)s-%(bits)s' | |
| 17 FILENAME_PATTERN = BASENAME_PATTERN + '.zip' | |
| 18 | |
| 19 DRY_RUN = False | |
| 20 | |
| 21 bot_utils = imp.load_source('bot_utils', BOT_UTILS) | |
| 22 | |
| 23 class ChangedWorkingDirectory(object): | |
| 24 def __init__(self, working_directory): | |
| 25 self._working_directory = working_directory | |
| 26 | |
| 27 def __enter__(self): | |
| 28 self._old_cwd = os.getcwd() | |
| 29 print "Enter directory = ", self._working_directory | |
| 30 if not DRY_RUN: | |
| 31 os.chdir(self._working_directory) | |
| 32 | |
| 33 def __exit__(self, *_): | |
| 34 print "Enter directory = ", self._old_cwd | |
| 35 os.chdir(self._old_cwd) | |
| 36 | |
| 37 def GetOptionsParser(): | |
| 38 parser = optparse.OptionParser("usage: %prog [options]") | |
| 39 parser.add_option("--scratch-dir", | |
| 40 help="Scratch directory to use.") | |
| 41 parser.add_option("--revision", type="int", | |
| 42 help="Revision we want to sign.") | |
| 43 parser.add_option("--channel", type="string", | |
| 44 default=None, | |
| 45 help="Channel we want to sign.") | |
| 46 parser.add_option("--dry-run", action="store_true", dest="dry_run", | |
| 47 default=False, | |
| 48 help="Do a dry run and do not execute any commands.") | |
| 49 parser.add_option("--prepare", action="store_true", dest="prepare", | |
| 50 default=False, | |
| 51 help="Prepare the .exe/.zip files to sign.") | |
| 52 parser.add_option("--deploy", action="store_true", dest="deploy", | |
| 53 default=False, | |
| 54 help="Pack the signed .exe/.zip files and deploy.") | |
| 55 return parser | |
| 56 | |
| 57 def die(msg, withOptions=True): | |
| 58 print msg | |
| 59 if withOptions: | |
| 60 GetOptionsParser().print_usage() | |
| 61 sys.exit(1) | |
| 62 | |
| 63 def run(command): | |
| 64 """We use run() instead of builtin python methods, because not all | |
| 65 functionality can easily be done by python and we can support --dry-run""" | |
| 66 | |
| 67 print "Running: ", command | |
| 68 if not DRY_RUN: | |
| 69 process = subprocess.Popen(command, | |
| 70 stdout=subprocess.PIPE, | |
| 71 stderr=subprocess.PIPE) | |
| 72 (stdout, stderr) = process.communicate() | |
| 73 if process.returncode != 0: | |
| 74 print "DEBUG: failed to run command '%s'" % command | |
| 75 print "DEBUG: stdout = ", stdout | |
| 76 print "DEBUG: stderr = ", stderr | |
| 77 print "DEBUG: returncode = ", process.returncode | |
| 78 raise OSError(process.returncode) | |
| 79 | |
| 80 def clean_directory(directory): | |
| 81 if os.path.exists(directory): | |
| 82 run(['rm', '-r', directory]) | |
| 83 run(['mkdir', '-p', directory]) | |
| 84 | |
| 85 def rm_tree(directory): | |
| 86 if os.path.exists(directory): | |
| 87 run(['rm', '-r', directory]) | |
| 88 | |
| 89 def copy_tree(from_dir, to_dir): | |
| 90 if os.path.exists(to_dir): | |
| 91 run(['rm', '-r', to_dir]) | |
| 92 run(['cp', '-Rp', from_dir, to_dir]) | |
| 93 | |
| 94 def copy_file(from_file, to_file): | |
| 95 if os.path.exists(to_file): | |
| 96 run(['rm', to_file]) | |
| 97 run(['cp', '-p', from_file, to_file]) | |
| 98 | |
| 99 def copy_and_zip(from_dir, to_dir): | |
| 100 rm_tree(to_dir) | |
| 101 copy_tree(from_dir, to_dir) | |
| 102 | |
| 103 dirname = os.path.basename(to_dir) | |
| 104 with ChangedWorkingDirectory(os.path.dirname(to_dir)): | |
| 105 run(['zip', '-r9', dirname + '.zip', dirname]) | |
| 106 | |
| 107 def unzip_and_copy(extracted_zipfiledir, to_dir): | |
| 108 rm_tree(extracted_zipfiledir) | |
| 109 run(['unzip', extracted_zipfiledir + '.zip', '-d', | |
| 110 os.path.dirname(extracted_zipfiledir)]) | |
| 111 rm_tree(to_dir) | |
| 112 copy_tree(extracted_zipfiledir, to_dir) | |
| 113 | |
| 114 def download_from_new_location(channel, config, destination): | |
| 115 namer = bot_utils.GCSNamer(channel, | |
| 116 bot_utils.ReleaseType.RAW) | |
| 117 bucket = namer.editor_zipfilepath(config['revision'], config['system'], | |
| 118 config['bits']) | |
| 119 run([GSUTIL, 'cp', bucket, destination]) | |
| 120 | |
| 121 def download_msi_installer_from_new_location(channel, config, destination): | |
| 122 namer = bot_utils.GCSNamer(channel, bot_utils.ReleaseType.RAW) | |
| 123 bucket = namer.editor_installer_filepath( | |
| 124 config['revision'], config['system'], config['bits'], 'msi') | |
| 125 run([GSUTIL, 'cp', bucket, destination]) | |
| 126 | |
| 127 def upload_to_new_location(channel, config, source_zip): | |
| 128 namer = bot_utils.GCSNamer(channel, | |
| 129 bot_utils.ReleaseType.SIGNED) | |
| 130 zipfilename = namer.editor_zipfilename(config['system'], config['bits']) | |
| 131 bucket = namer.editor_zipfilepath(config['revision'], config['system'], | |
| 132 config['bits']) | |
| 133 | |
| 134 if not DRY_RUN: | |
| 135 bot_utils.CreateMD5ChecksumFile(source_zip, mangled_filename=zipfilename) | |
| 136 bot_utils.CreateSha256ChecksumFile(source_zip, | |
| 137 mangled_filename=zipfilename) | |
| 138 md5_zip_file = source_zip + '.md5sum' | |
| 139 sha256_zip_file = source_zip + '.sha256sum' | |
| 140 | |
| 141 run([GSUTIL, 'cp', source_zip, bucket]) | |
| 142 run([GSUTIL, 'cp', md5_zip_file, bucket + '.md5sum']) | |
| 143 run([GSUTIL, 'cp', sha256_zip_file, bucket + '.sha256sum']) | |
| 144 run([GSUTIL, 'setacl', 'public-read', bucket]) | |
| 145 run([GSUTIL, 'setacl', 'public-read', bucket + '.md5sum']) | |
| 146 run([GSUTIL, 'setacl', 'public-read', bucket + '.sha256sum']) | |
| 147 | |
| 148 def upload_msi_installer_to_new_location(channel, config, signed_msi): | |
| 149 namer = bot_utils.GCSNamer(channel, bot_utils.ReleaseType.SIGNED) | |
| 150 bucket = namer.editor_installer_filepath( | |
| 151 config['revision'], config['system'], config['bits'], 'msi') | |
| 152 run([GSUTIL, 'cp', '-a', 'public-read', signed_msi, bucket]) | |
| 153 | |
| 154 def main(): | |
| 155 if sys.platform != 'linux2': | |
| 156 print "This script was only tested on linux. Please run it on linux!" | |
| 157 sys.exit(1) | |
| 158 | |
| 159 parser = GetOptionsParser() | |
| 160 (options, args) = parser.parse_args() | |
| 161 | |
| 162 if not options.scratch_dir: | |
| 163 die("No scratch directory given.") | |
| 164 if not options.revision: | |
| 165 die("No revision given.") | |
| 166 if not options.prepare and not options.deploy: | |
| 167 die("No prepare/deploy parameter given.") | |
| 168 if options.prepare and options.deploy: | |
| 169 die("Can't have prepare and deploy parameters at the same time.") | |
| 170 if len(args) > 0: | |
| 171 die("Invalid additional arguments: %s." % args) | |
| 172 | |
| 173 if not options.channel: | |
| 174 die("You need to specify a channel with --channel.") | |
| 175 | |
| 176 global DRY_RUN | |
| 177 DRY_RUN = options.dry_run | |
| 178 | |
| 179 downloads_dir = os.path.join(options.scratch_dir, 'downloads') | |
| 180 presign_dir = os.path.join(options.scratch_dir, 'presign') | |
| 181 postsign_dir = os.path.join(options.scratch_dir, 'postsign') | |
| 182 uploads_dir = os.path.join(options.scratch_dir, 'uploads') | |
| 183 | |
| 184 if options.prepare: | |
| 185 # Clean all directories | |
| 186 clean_directory(downloads_dir) | |
| 187 clean_directory(presign_dir) | |
| 188 clean_directory(postsign_dir) | |
| 189 clean_directory(uploads_dir) | |
| 190 elif options.deploy: | |
| 191 clean_directory(uploads_dir) | |
| 192 | |
| 193 # These are the locations where we can find the *.app folders and *.exe files | |
| 194 # and the names we use inside the scratch directory. | |
| 195 locations = { | |
| 196 'macos' : { | |
| 197 'editor' : os.path.join('dart', 'DartEditor.app'), | |
| 198 'chrome' : os.path.join('dart', 'chromium', 'Chromium.app'), | |
| 199 | |
| 200 'editor_scratch' : 'DartEditor%(bits)s.app', | |
| 201 'chrome_scratch' : 'Chromium%(bits)s.app', | |
| 202 | |
| 203 'zip' : True, | |
| 204 }, | |
| 205 'win32' : { | |
| 206 'editor' : os.path.join('dart', 'DartEditor.exe'), | |
| 207 'chrome' : os.path.join('dart', 'chromium', 'chrome.exe'), | |
| 208 | |
| 209 'msi_scratch' : 'darteditor-installer-windows-%(bits)s.msi', | |
| 210 'editor_scratch' : 'DartEditor%(bits)s.exe', | |
| 211 'chrome_scratch' : 'chromium%(bits)s.exe', | |
| 212 | |
| 213 'zip' : False, | |
| 214 }, | |
| 215 } | |
| 216 | |
| 217 # Desitination of zip files we download | |
| 218 for system in ('macos', 'win32'): | |
| 219 for bits in ('32', '64'): | |
| 220 config = { | |
| 221 'revision' : options.revision, | |
| 222 'system' : system, | |
| 223 'bits' : bits, | |
| 224 } | |
| 225 | |
| 226 destination = os.path.join(downloads_dir, FILENAME_PATTERN % config) | |
| 227 destination_dir = os.path.join(downloads_dir, BASENAME_PATTERN % config) | |
| 228 | |
| 229 deploy = os.path.join(uploads_dir, FILENAME_PATTERN % config) | |
| 230 deploy_dir = os.path.join(uploads_dir, BASENAME_PATTERN % config) | |
| 231 | |
| 232 if options.prepare: | |
| 233 # Download *.zip files from GCS buckets | |
| 234 download_from_new_location(options.channel, config, destination) | |
| 235 | |
| 236 run(['unzip', destination, '-d', destination_dir]) | |
| 237 | |
| 238 for name in ['editor', 'chrome']: | |
| 239 from_path = os.path.join(destination_dir, locations[system][name]) | |
| 240 to_path = os.path.join( | |
| 241 presign_dir, locations[system]['%s_scratch' % name] % config) | |
| 242 | |
| 243 if locations[system]['zip']: | |
| 244 # We copy a .app directory directory and zip it | |
| 245 copy_and_zip(from_path, to_path) | |
| 246 else: | |
| 247 # We copy an .exe file | |
| 248 copy_file(from_path, to_path) | |
| 249 | |
| 250 # Download .*msi installer from GCS to presign directory | |
| 251 if system == 'win32': | |
| 252 presign_msi = os.path.join( | |
| 253 presign_dir, locations[system]['msi_scratch'] % config) | |
| 254 download_msi_installer_from_new_location( | |
| 255 options.channel, config, presign_msi) | |
| 256 elif options.deploy: | |
| 257 copy_tree(destination_dir, deploy_dir) | |
| 258 | |
| 259 for name in ['editor', 'chrome']: | |
| 260 from_path = os.path.join( | |
| 261 postsign_dir, locations[system]['%s_scratch' % name] % config) | |
| 262 to_path = os.path.join(deploy_dir, locations[system][name]) | |
| 263 | |
| 264 if locations[system]['zip']: | |
| 265 # We unzip a zip file and copy the resulting signed .app directory | |
| 266 unzip_and_copy(from_path, to_path) | |
| 267 else: | |
| 268 # We copy the signed .exe file | |
| 269 copy_file(from_path, to_path) | |
| 270 | |
| 271 deploy_zip_file = os.path.abspath(deploy) | |
| 272 with ChangedWorkingDirectory(deploy_dir): | |
| 273 run(['zip', '-r9', deploy_zip_file, 'dart']) | |
| 274 | |
| 275 # Upload *.zip/*.zip.md5sum and set 'public-read' ACL | |
| 276 upload_to_new_location(options.channel, config, deploy_zip_file) | |
| 277 | |
| 278 # Upload *.msi installer and set 'public-read ACL | |
| 279 if system == 'win32': | |
| 280 postsign_msi = os.path.join( | |
| 281 postsign_dir, locations[system]['msi_scratch'] % config) | |
| 282 upload_msi_installer_to_new_location( | |
| 283 options.channel, config, postsign_msi) | |
| 284 | |
| 285 if __name__ == '__main__': | |
| 286 main() | |
| OLD | NEW |