Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(150)

Side by Side Diff: third_party/mozdownload/mozdownload/scraper.py

Issue 108313011: Adding mozilla libraries required by Firefox interop test. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/deps/
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
Added: svn:executable
+ *
OLDNEW
(Empty)
1 #!/usr/bin/env python
2
3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 """Module to handle downloads for different types of Firefox and Thunderbird bui lds."""
8
9
10 from datetime import datetime
11 from optparse import OptionParser, OptionGroup
12 import os
13 import re
14 import sys
15 import time
16 import urllib
17 import urllib2
18
19 import mozinfo
20
21 from parser import DirectoryParser
22 from timezones import PacificTimezone
23
24
25 APPLICATIONS = ['b2g', 'firefox', 'thunderbird']
26
27 # Base URL for the path to all builds
28 BASE_URL = 'https://ftp.mozilla.org/pub/mozilla.org'
29
30 PLATFORM_FRAGMENTS = {'linux': 'linux-i686',
31 'linux64': 'linux-x86_64',
32 'mac': 'mac',
33 'mac64': 'mac64',
34 'win32': 'win32',
35 'win64': 'win64-x86_64'}
36
37 DEFAULT_FILE_EXTENSIONS = {'linux': 'tar.bz2',
38 'linux64': 'tar.bz2',
39 'mac': 'dmg',
40 'mac64': 'dmg',
41 'win32': 'exe',
42 'win64': 'exe'}
43
44 class NotFoundException(Exception):
45 """Exception for a resource not being found (e.g. no logs)"""
46 def __init__(self, message, location):
47 self.location = location
48 Exception.__init__(self, ': '.join([message, location]))
49
50
51 class Scraper(object):
52 """Generic class to download an application from the Mozilla server"""
53
54 def __init__(self, directory, version, platform=None,
55 application='firefox', locale='en-US', extension=None,
56 authentication=None, retry_attempts=3, retry_delay=10):
57
58 # Private properties for caching
59 self._target = None
60 self._binary = None
61
62 self.directory = directory
63 self.locale = locale
64 self.platform = platform or self.detect_platform()
65 self.version = version
66 self.extension = extension or DEFAULT_FILE_EXTENSIONS[self.platform]
67 self.authentication = authentication
68 self.retry_attempts = retry_attempts
69 self.retry_delay = retry_delay
70
71 # build the base URL
72 self.application = application
73 self.base_url = '/'.join([BASE_URL, self.application])
74
75
76 @property
77 def binary(self):
78 """Return the name of the build"""
79
80 if self._binary is None:
81 # Retrieve all entries from the remote virtual folder
82 parser = DirectoryParser(self.path)
83 if not parser.entries:
84 raise NotFoundException('No entries found', self.path)
85
86 # Download the first matched directory entry
87 pattern = re.compile(self.binary_regex, re.IGNORECASE)
88 for entry in parser.entries:
89 try:
90 self._binary = pattern.match(entry).group()
91 break
92 except:
93 # No match, continue with next entry
94 continue
95
96 if self._binary is None:
97 raise NotFoundException("Binary not found in folder", self.path)
98 else:
99 return self._binary
100
101
102 @property
103 def binary_regex(self):
104 """Return the regex for the binary filename"""
105
106 raise NotImplementedError(sys._getframe(0).f_code.co_name)
107
108
109 @property
110 def final_url(self):
111 """Return the final URL of the build"""
112
113 return '/'.join([self.path, self.binary])
114
115
116 @property
117 def path(self):
118 """Return the path to the build"""
119
120 return '/'.join([self.base_url, self.path_regex])
121
122
123 @property
124 def path_regex(self):
125 """Return the regex for the path to the build"""
126
127 raise NotImplementedError(sys._getframe(0).f_code.co_name)
128
129
130 @property
131 def platform_regex(self):
132 """Return the platform fragment of the URL"""
133
134 return PLATFORM_FRAGMENTS[self.platform];
135
136
137 @property
138 def target(self):
139 """Return the target file name of the build"""
140
141 if self._target is None:
142 self._target = os.path.join(self.directory,
143 self.build_filename(self.binary))
144 return self._target
145
146
147 def build_filename(self, binary):
148 """Return the proposed filename with extension for the binary"""
149
150 raise NotImplementedError(sys._getframe(0).f_code.co_name)
151
152
153 def detect_platform(self):
154 """Detect the current platform"""
155
156 # For Mac and Linux 32bit we do not need the bits appended
157 if mozinfo.os == 'mac' or (mozinfo.os == 'linux' and mozinfo.bits == 32) :
158 return mozinfo.os
159 else:
160 return "%s%d" % (mozinfo.os, mozinfo.bits)
161
162
163 def download(self):
164 """Download the specified file"""
165
166 attempts = 0
167
168 if not os.path.isdir(self.directory):
169 os.makedirs(self.directory)
170
171 # Don't re-download the file
172 if os.path.isfile(os.path.abspath(self.target)):
173 print "File has already been downloaded: %s" % (self.target)
174 return
175
176 print 'Downloading from: %s' % (urllib.unquote(self.final_url))
177 tmp_file = self.target + ".part"
178
179 if self.authentication \
180 and self.authentication['username'] \
181 and self.authentication['password']:
182 password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
183 password_mgr.add_password(None,
184 self.final_url,
185 self.authentication['username'],
186 self.authentication['password'])
187 handler = urllib2.HTTPBasicAuthHandler(password_mgr)
188 opener = urllib2.build_opener(urllib2.HTTPHandler, handler)
189 urllib2.install_opener(opener)
190
191 while True:
192 attempts += 1
193 try:
194 r = urllib2.urlopen(self.final_url)
195 CHUNK = 16 * 1024
196 with open(tmp_file, 'wb') as f:
197 for chunk in iter(lambda: r.read(CHUNK), ''):
198 f.write(chunk)
199 break
200 except (urllib2.HTTPError, urllib2.URLError):
201 if tmp_file and os.path.isfile(tmp_file):
202 os.remove(tmp_file)
203 print 'Download failed! Retrying... (attempt %s)' % attempts
204 if attempts >= self.retry_attempts:
205 raise
206 time.sleep(self.retry_delay)
207
208 os.rename(tmp_file, self.target)
209
210
211 class DailyScraper(Scraper):
212 """Class to download a daily build from the Mozilla server"""
213
214 def __init__(self, branch='mozilla-central', build_id=None, date=None,
215 build_number=None, *args, **kwargs):
216
217 Scraper.__init__(self, *args, **kwargs)
218 self.branch = branch
219
220 # Internally we access builds via index
221 if build_number is not None:
222 self.build_index = int(build_number) - 1
223 else:
224 self.build_index = None
225
226 if build_id:
227 # A build id has been specified. Split up its components so the date
228 # and time can be extracted: '20111212042025' -> '2011-12-12 04:20:2 5'
229 self.date = datetime.strptime(build_id, '%Y%m%d%H%M%S')
230 self.builds, self.build_index = self.get_build_info_for_date(self.da te,
231 has_tim e=True)
232
233 elif date:
234 # A date (without time) has been specified. Use its value and the
235 # build index to find the requested build for that day.
236 self.date = datetime.strptime(date, '%Y-%m-%d')
237 self.builds, self.build_index = self.get_build_info_for_date(self.da te,
238 build_i ndex=self.build_index)
239
240 else:
241 # If no build id nor date have been specified the lastest available
242 # build of the given branch has to be identified. We also have to
243 # retrieve the date of the build via its build id.
244 url = '%s/nightly/latest-%s/' % (self.base_url, self.branch)
245
246 print 'Retrieving the build status file from %s' % url
247 parser = DirectoryParser(url)
248 parser.entries = parser.filter(r'.*%s\.txt' % self.platform_regex)
249 if not parser.entries:
250 message = 'Status file for %s build cannot be found' % self.plat form_regex
251 raise NotFoundException(message, url)
252
253 # Read status file for the platform, retrieve build id, and convert to a date
254 status_file = url + parser.entries[-1]
255 f = urllib.urlopen(status_file)
256 self.date = datetime.strptime(f.readline().strip(), '%Y%m%d%H%M%S')
257 self.builds, self.build_index = self.get_build_info_for_date(self.da te,
258 has_tim e=True)
259
260
261 def get_build_info_for_date(self, date, has_time=False, build_index=None):
262 url = '/'.join([self.base_url, self.monthly_build_list_regex])
263
264 print 'Retrieving list of builds from %s' % url
265 parser = DirectoryParser(url)
266 regex = r'%(DATE)s-(\d+-)+%(BRANCH)s%(L10N)s$' % {
267 'DATE': date.strftime('%Y-%m-%d'),
268 'BRANCH': self.branch,
269 'L10N': '' if self.locale == 'en-US' else '-l10n'}
270 parser.entries = parser.filter(regex)
271 if not parser.entries:
272 message = 'Folder for builds on %s has not been found' % self.date.s trftime('%Y-%m-%d')
273 raise NotFoundException(message, url)
274
275 if has_time:
276 # If a time is included in the date, use it to determine the build's index
277 regex = r'.*%s.*' % date.strftime('%H-%M-%S')
278 build_index = parser.entries.index(parser.filter(regex)[0])
279 else:
280 # If no index has been given, set it to the last build of the day.
281 if build_index is None:
282 build_index = len(parser.entries) - 1
283
284 return (parser.entries, build_index)
285
286
287 @property
288 def binary_regex(self):
289 """Return the regex for the binary"""
290
291 regex_base_name = r'^%(APP)s-.*\.%(LOCALE)s\.%(PLATFORM)s'
292 regex_suffix = {'linux': r'\.%(EXT)s$',
293 'linux64': r'\.%(EXT)s$',
294 'mac': r'\.%(EXT)s$',
295 'mac64': r'\.%(EXT)s$',
296 'win32': r'(\.installer)\.%(EXT)s$',
297 'win64': r'(\.installer)\.%(EXT)s$'}
298 regex = regex_base_name + regex_suffix[self.platform]
299
300 return regex % {'APP': self.application,
301 'LOCALE': self.locale,
302 'PLATFORM': self.platform_regex,
303 'EXT': self.extension}
304
305
306 def build_filename(self, binary):
307 """Return the proposed filename with extension for the binary"""
308
309 try:
310 # Get exact timestamp of the build to build the local file name
311 folder = self.builds[self.build_index]
312 timestamp = re.search('([\d\-]+)-\D.*', folder).group(1)
313 except:
314 # If it's not available use the build's date
315 timestamp = self.date.strftime('%Y-%m-%d')
316
317 return '%(TIMESTAMP)s-%(BRANCH)s-%(NAME)s' % {
318 'TIMESTAMP': timestamp,
319 'BRANCH': self.branch,
320 'NAME': binary}
321
322
323 @property
324 def monthly_build_list_regex(self):
325 """Return the regex for the folder which contains the builds of a month. """
326
327 # Regex for possible builds for the given date
328 return r'nightly/%(YEAR)s/%(MONTH)s/' % {
329 'YEAR': self.date.year,
330 'MONTH': str(self.date.month).zfill(2) }
331
332
333 @property
334 def path_regex(self):
335 """Return the regex for the path"""
336
337 try:
338 return self.monthly_build_list_regex + self.builds[self.build_index]
339 except:
340 raise NotFoundException("Specified sub folder cannot be found",
341 self.base_url + self.monthly_build_list_rege x)
342
343
344 class DirectScraper(Scraper):
345 """Class to download a file from a specified URL"""
346
347 def __init__(self, url, *args, **kwargs):
348 Scraper.__init__(self, *args, **kwargs)
349
350 self.url = url
351
352 @property
353 def target(self):
354 return urllib.splitquery(self.final_url)[0].rpartition('/')[-1]
355
356 @property
357 def final_url(self):
358 return self.url
359
360
361 class ReleaseScraper(Scraper):
362 """Class to download a release build from the Mozilla server"""
363
364 def __init__(self, *args, **kwargs):
365 Scraper.__init__(self, *args, **kwargs)
366
367 @property
368 def binary_regex(self):
369 """Return the regex for the binary"""
370
371 regex = {'linux': r'^%(APP)s-.*\.%(EXT)s$',
372 'linux64': r'^%(APP)s-.*\.%(EXT)s$',
373 'mac': r'^%(APP)s.*\.%(EXT)s$',
374 'mac64': r'^%(APP)s.*\.%(EXT)s$',
375 'win32': r'^%(APP)s.*\.%(EXT)s$',
376 'win64': r'^%(APP)s.*\.%(EXT)s$'}
377 return regex[self.platform] % {'APP': self.application,
378 'EXT': self.extension}
379
380
381 @property
382 def path_regex(self):
383 """Return the regex for the path"""
384
385 regex = r'releases/%(VERSION)s/%(PLATFORM)s/%(LOCALE)s'
386 return regex % {'LOCALE': self.locale,
387 'PLATFORM': self.platform_regex,
388 'VERSION': self.version}
389
390
391 def build_filename(self, binary):
392 """Return the proposed filename with extension for the binary"""
393
394 template = '%(APP)s-%(VERSION)s.%(LOCALE)s.%(PLATFORM)s.%(EXT)s'
395 return template % {'APP': self.application,
396 'VERSION': self.version,
397 'LOCALE': self.locale,
398 'PLATFORM': self.platform,
399 'EXT': self.extension}
400
401
402 class ReleaseCandidateScraper(ReleaseScraper):
403 """Class to download a release candidate build from the Mozilla server"""
404
405 def __init__(self, build_number=None, no_unsigned=False, *args, **kwargs):
406 Scraper.__init__(self, *args, **kwargs)
407
408 # Internally we access builds via index
409 if build_number is not None:
410 self.build_index = int(build_number) - 1
411 else:
412 self.build_index = None
413
414 self.builds, self.build_index = self.get_build_info_for_version(self.ver sion, self.build_index)
415
416 self.no_unsigned = no_unsigned
417 self.unsigned = False
418
419
420 def get_build_info_for_version(self, version, build_index=None):
421 url = '/'.join([self.base_url, self.candidate_build_list_regex])
422
423 print 'Retrieving list of candidate builds from %s' % url
424 parser = DirectoryParser(url)
425 if not parser.entries:
426 message = 'Folder for specific candidate builds at has not been foun d'
427 raise NotFoundException(message, url)
428
429 # If no index has been given, set it to the last build of the given vers ion.
430 if build_index is None:
431 build_index = len(parser.entries) - 1
432
433 return (parser.entries, build_index)
434
435
436 @property
437 def candidate_build_list_regex(self):
438 """Return the regex for the folder which contains the builds of
439 a candidate build."""
440
441 # Regex for possible builds for the given date
442 return r'nightly/%(VERSION)s-candidates/' % {
443 'VERSION': self.version }
444
445
446 @property
447 def path_regex(self):
448 """Return the regex for the path"""
449
450 regex = r'%(PREFIX)s%(BUILD)s/%(UNSIGNED)s%(PLATFORM)s/%(LOCALE)s'
451 return regex % {'PREFIX': self.candidate_build_list_regex,
452 'BUILD': self.builds[self.build_index],
453 'LOCALE': self.locale,
454 'PLATFORM': self.platform_regex,
455 'UNSIGNED': "unsigned/" if self.unsigned else ""}
456
457
458 def build_filename(self, binary):
459 """Return the proposed filename with extension for the binary"""
460
461 template = '%(APP)s-%(VERSION)s-build%(BUILD)s.%(LOCALE)s.%(PLATFORM)s.% (EXT)s'
462 return template % {'APP': self.application,
463 'VERSION': self.version,
464 'BUILD': self.builds[self.build_index],
465 'LOCALE': self.locale,
466 'PLATFORM': self.platform,
467 'EXT': self.extension}
468
469
470 def download(self):
471 """Download the specified file"""
472
473 try:
474 # Try to download the signed candidate build
475 Scraper.download(self)
476 except NotFoundException, e:
477 print str(e)
478
479 # If the signed build cannot be downloaded and unsigned builds are
480 # allowed, try to download the unsigned build instead
481 if self.no_unsigned:
482 raise
483 else:
484 print "Signed build has not been found. Falling back to unsigned build."
485 self.unsigned = True
486 Scraper.download(self)
487
488
489 class TinderboxScraper(Scraper):
490 """Class to download a tinderbox build from the Mozilla server.
491
492 There are two ways to specify a unique build:
493 1. If the date (%Y-%m-%d) is given and build_number is given where
494 the build_number is the index of the build on the date
495 2. If the build timestamp (UNIX) is given, and matches a specific build.
496 """
497
498 def __init__(self, branch='mozilla-central', build_number=None, date=None,
499 debug_build=False, *args, **kwargs):
500 Scraper.__init__(self, *args, **kwargs)
501
502 self.branch = branch
503 self.debug_build = debug_build
504 self.locale_build = self.locale != 'en-US'
505 self.timestamp = None
506
507 # Currently any time in RelEng is based on the Pacific time zone.
508 self.timezone = PacificTimezone();
509
510 # Internally we access builds via index
511 if build_number is not None:
512 self.build_index = int(build_number) - 1
513 else:
514 self.build_index = None
515
516 if date is not None:
517 try:
518 self.date = datetime.fromtimestamp(float(date), self.timezone)
519 self.timestamp = date
520 except:
521 self.date = datetime.strptime(date, '%Y-%m-%d')
522 else:
523 self.date = None
524
525 # For localized builds we do not have to retrieve the list of builds
526 # because only the last build is available
527 if not self.locale_build:
528 self.builds, self.build_index = self.get_build_info(self.build_index )
529
530 try:
531 self.timestamp = self.builds[self.build_index]
532 except:
533 raise NotFoundException("Specified sub folder cannot be found",
534 self.base_url + self.monthly_build_list_ regex)
535
536
537 @property
538 def binary_regex(self):
539 """Return the regex for the binary"""
540
541 regex_base_name = r'^%(APP)s-.*\.%(LOCALE)s\.'
542 regex_suffix = {'linux': r'.*\.%(EXT)s$',
543 'linux64': r'.*\.%(EXT)s$',
544 'mac': r'.*\.%(EXT)s$',
545 'mac64': r'.*\.%(EXT)s$',
546 'win32': r'.*(\.installer)\.%(EXT)s$',
547 'win64': r'.*(\.installer)\.%(EXT)s$'}
548
549 regex = regex_base_name + regex_suffix[self.platform]
550
551 return regex % {'APP': self.application,
552 'LOCALE': self.locale,
553 'EXT': self.extension}
554
555
556 def build_filename(self, binary):
557 """Return the proposed filename with extension for the binary"""
558
559 return '%(TIMESTAMP)s%(BRANCH)s%(DEBUG)s-%(NAME)s' % {
560 'TIMESTAMP': self.timestamp + '-' if self.timestamp else '',
561 'BRANCH': self.branch,
562 'DEBUG': '-debug' if self.debug_build else '',
563 'NAME': binary}
564
565
566 @property
567 def build_list_regex(self):
568 """Return the regex for the folder which contains the list of builds"""
569
570 regex = 'tinderbox-builds/%(BRANCH)s-%(PLATFORM)s%(L10N)s%(DEBUG)s'
571
572 return regex % {'BRANCH': self.branch,
573 'PLATFORM': '' if self.locale_build else self.platform_r egex,
574 'L10N': 'l10n' if self.locale_build else '',
575 'DEBUG': '-debug' if self.debug_build else ''}
576
577
578 def date_matches(self, timestamp):
579 """Determines whether the timestamp date is equal to the argument date"" "
580
581 if self.date is None:
582 return False
583
584 timestamp = datetime.fromtimestamp(float(timestamp), self.timezone)
585 if self.date.date() == timestamp.date():
586 return True
587
588 return False
589
590
591 @property
592 def date_validation_regex(self):
593 """Return the regex for a valid date argument value"""
594
595 return r'^\d{4}-\d{1,2}-\d{1,2}$|^\d+$'
596
597
598 def detect_platform(self):
599 """Detect the current platform"""
600
601 platform = Scraper.detect_platform(self)
602
603 # On OS X we have to special case the platform detection code and fallba ck
604 # to 64 bit builds for the en-US locale
605 if mozinfo.os == 'mac' and self.locale == 'en-US' and mozinfo.bits == 64 :
606 platform = "%s%d" % (mozinfo.os, mozinfo.bits)
607
608 return platform
609
610
611 def get_build_info(self, build_index=None):
612 url = '/'.join([self.base_url, self.build_list_regex])
613
614 print 'Retrieving list of builds from %s' % url
615
616 # If a timestamp is given, retrieve just that build
617 regex = '^' + self.timestamp + '$' if self.timestamp else r'^\d+$'
618
619 parser = DirectoryParser(url)
620 parser.entries = parser.filter(regex)
621
622 # If date is given, retrieve the subset of builds on that date
623 if self.date is not None:
624 parser.entries = filter(self.date_matches, parser.entries)
625
626 if not parser.entries:
627 message = 'No builds have been found'
628 raise NotFoundException(message, url)
629
630 # If no index has been given, set it to the last build of the day.
631 if build_index is None:
632 build_index = len(parser.entries) - 1
633
634 return (parser.entries, build_index)
635
636
637 @property
638 def path_regex(self):
639 """Return the regex for the path"""
640
641 if self.locale_build:
642 return self.build_list_regex
643
644 return '/'.join([self.build_list_regex, self.builds[self.build_index]])
645
646
647 @property
648 def platform_regex(self):
649 """Return the platform fragment of the URL"""
650
651 PLATFORM_FRAGMENTS = {'linux': 'linux',
652 'linux64': 'linux64',
653 'mac': 'macosx',
654 'mac64': 'macosx64',
655 'win32': 'win32',
656 'win64': 'win64'}
657
658 return PLATFORM_FRAGMENTS[self.platform]
659
660
661 def cli():
662 """Main function for the downloader"""
663
664 BUILD_TYPES = {'release': ReleaseScraper,
665 'candidate': ReleaseCandidateScraper,
666 'daily': DailyScraper,
667 'tinderbox': TinderboxScraper }
668
669 usage = 'usage: %prog [options]'
670 parser = OptionParser(usage=usage, description=__doc__)
671 parser.add_option('--application', '-a',
672 dest='application',
673 choices=APPLICATIONS,
674 default='firefox',
675 metavar='APPLICATION',
676 help='The name of the application to download, '
677 'default: "%default"')
678 parser.add_option('--directory', '-d',
679 dest='directory',
680 default=os.getcwd(),
681 metavar='DIRECTORY',
682 help='Target directory for the download, default: '
683 'current working directory')
684 parser.add_option('--build-number',
685 dest='build_number',
686 default=None,
687 type="int",
688 metavar='BUILD_NUMBER',
689 help='Number of the build (for candidate, daily, '
690 'and tinderbox builds)')
691 parser.add_option('--locale', '-l',
692 dest='locale',
693 default='en-US',
694 metavar='LOCALE',
695 help='Locale of the application, default: "%default"')
696 parser.add_option('--platform', '-p',
697 dest='platform',
698 choices=PLATFORM_FRAGMENTS.keys(),
699 metavar='PLATFORM',
700 help='Platform of the application')
701 parser.add_option('--type', '-t',
702 dest='type',
703 choices=BUILD_TYPES.keys(),
704 default='release',
705 metavar='BUILD_TYPE',
706 help='Type of build to download, default: "%default"')
707 parser.add_option('--url',
708 dest='url',
709 default=None,
710 metavar='URL',
711 help='URL to download.')
712 parser.add_option('--version', '-v',
713 dest='version',
714 metavar='VERSION',
715 help='Version of the application to be used by release and \
716 candidate builds, i.e. "3.6"')
717 parser.add_option('--extension',
718 dest='extension',
719 default=None,
720 metavar='EXTENSION',
721 help='File extension of the build (e.g. "zip"), default:\
722 the standard build extension on the platform.')
723 parser.add_option('--username',
724 dest='username',
725 default=None,
726 metavar='USERNAME',
727 help='Username for basic HTTP authentication.')
728 parser.add_option('--password',
729 dest='password',
730 default=None,
731 metavar='PASSWORD',
732 help='Password for basic HTTP authentication.')
733 parser.add_option('--retry-attempts',
734 dest='retry_attempts',
735 default=3,
736 type=int,
737 metavar='RETRY_ATTEMPTS',
738 help='Number of times the download will be attempted in '
739 'the event of a failure, default: %default')
740 parser.add_option('--retry-delay',
741 dest='retry_delay',
742 default=10,
743 type=int,
744 metavar='RETRY_DELAY',
745 help='Amount of time (in seconds) to wait between retry '
746 'attempts, default: %default')
747
748 # Option group for candidate builds
749 group = OptionGroup(parser, "Candidate builds",
750 "Extra options for candidate builds.")
751 group.add_option('--no-unsigned',
752 dest='no_unsigned',
753 action="store_true",
754 help="Don't allow to download unsigned builds if signed\
755 builds are not available")
756 parser.add_option_group(group)
757
758 # Option group for daily builds
759 group = OptionGroup(parser, "Daily builds",
760 "Extra options for daily builds.")
761 group.add_option('--branch',
762 dest='branch',
763 default='mozilla-central',
764 metavar='BRANCH',
765 help='Name of the branch, default: "%default"')
766 group.add_option('--build-id',
767 dest='build_id',
768 default=None,
769 metavar='BUILD_ID',
770 help='ID of the build to download')
771 group.add_option('--date',
772 dest='date',
773 default=None,
774 metavar='DATE',
775 help='Date of the build, default: latest build')
776 parser.add_option_group(group)
777
778 # Option group for tinderbox builds
779 group = OptionGroup(parser, "Tinderbox builds",
780 "Extra options for tinderbox builds.")
781 group.add_option('--debug-build',
782 dest='debug_build',
783 action="store_true",
784 help="Download a debug build")
785 parser.add_option_group(group)
786
787 # TODO: option group for nightly builds
788 (options, args) = parser.parse_args()
789
790 # Check for required options and arguments
791 # Note: Will be optional when ini file support has been landed
792 if not options.url \
793 and not options.type in ['daily', 'tinderbox'] \
794 and not options.version:
795 parser.error('The version of the application to download has not been sp ecified.')
796
797 # Instantiate scraper and download the build
798 scraper_keywords = {'application': options.application,
799 'locale': options.locale,
800 'platform': options.platform,
801 'version': options.version,
802 'directory': options.directory,
803 'extension': options.extension,
804 'authentication': {
805 'username': options.username,
806 'password': options.password},
807 'retry_attempts': options.retry_attempts,
808 'retry_delay': options.retry_delay}
809 scraper_options = {'candidate': {
810 'build_number': options.build_number,
811 'no_unsigned': options.no_unsigned},
812 'daily': {
813 'branch': options.branch,
814 'build_number': options.build_number,
815 'build_id': options.build_id,
816 'date': options.date},
817 'tinderbox': {
818 'branch': options.branch,
819 'build_number': options.build_number,
820 'date': options.date,
821 'debug_build': options.debug_build}
822 }
823
824 kwargs = scraper_keywords.copy()
825 kwargs.update(scraper_options.get(options.type, {}))
826
827 if options.url:
828 build = DirectScraper(options.url, **kwargs)
829 else:
830 build = BUILD_TYPES[options.type](**kwargs)
831
832 build.download()
833
834 if __name__ == "__main__":
835 cli()
OLDNEW
« no previous file with comments | « third_party/mozdownload/mozdownload/parser.py ('k') | third_party/mozdownload/mozdownload/timezones.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698