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