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 |