OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Script that reads omahaproxy and gsutil to determine version of SDK to put | 6 """Script that reads omahaproxy and gsutil to determine version of SDK to put |
7 in manifest. | 7 in manifest. |
8 """ | 8 """ |
9 | 9 |
10 # pylint is convinced the email module is missing attributes | 10 # pylint is convinced the email module is missing attributes |
11 # pylint: disable=E1101 | 11 # pylint: disable=E1101 |
12 | 12 |
13 import buildbot_common | 13 import buildbot_common |
14 import csv | 14 import csv |
15 import cStringIO | 15 import cStringIO |
16 import difflib | 16 import difflib |
17 import email | 17 import email |
18 import json | |
19 import logging | 18 import logging |
20 import logging.handlers | 19 import logging.handlers |
21 import manifest_util | 20 import manifest_util |
22 import optparse | 21 import optparse |
23 import os | 22 import os |
24 import posixpath | 23 import posixpath |
25 import re | 24 import re |
26 import smtplib | 25 import smtplib |
27 import subprocess | 26 import subprocess |
28 import sys | 27 import sys |
(...skipping 14 matching lines...) Expand all Loading... |
43 CANARY = 'canary' | 42 CANARY = 'canary' |
44 NACLPORTS_ARCHIVE_NAME = 'naclports.tar.bz2' | 43 NACLPORTS_ARCHIVE_NAME = 'naclports.tar.bz2' |
45 | 44 |
46 | 45 |
47 logger = logging.getLogger(__name__) | 46 logger = logging.getLogger(__name__) |
48 | 47 |
49 | 48 |
50 def SplitVersion(version_string): | 49 def SplitVersion(version_string): |
51 """Split a version string (e.g. "18.0.1025.163") into its components. | 50 """Split a version string (e.g. "18.0.1025.163") into its components. |
52 | 51 |
53 Note that this function doesn't handle versions in the form "trunk.###". | 52 e.g. |
| 53 SplitVersion("trunk.123456") => ("trunk", "123456") |
| 54 SplitVersion("18.0.1025.163") => (18, 0, 1025, 163) |
54 """ | 55 """ |
55 return tuple(map(int, version_string.split('.'))) | 56 parts = version_string.split('.') |
| 57 if parts[0] == 'trunk': |
| 58 return (parts[0], int(parts[1])) |
| 59 return tuple([int(p) for p in parts]) |
| 60 |
| 61 |
| 62 def GetMajorVersion(version_string): |
| 63 """Get the major version number from a version string (e.g. "18.0.1025.163"). |
| 64 |
| 65 e.g. |
| 66 GetMajorVersion("trunk.123456") => "trunk" |
| 67 GetMajorVersion("18.0.1025.163") => 18 |
| 68 """ |
| 69 return SplitVersion(version_string)[0] |
| 70 |
| 71 |
| 72 def CompareVersions(version1, version2): |
| 73 """Compare two version strings and return -1, 0, 1 (similar to cmp). |
| 74 |
| 75 Versions can only be compared if they are both trunk versions, or neither is. |
| 76 |
| 77 e.g. |
| 78 CompareVersions("trunk.123", "trunk.456") => -1 |
| 79 CompareVersions("18.0.1025.163", "37.0.2054.3") => -1 |
| 80 CompareVersions("trunk.123", "18.0.1025.163") => Error |
| 81 |
| 82 """ |
| 83 split1 = SplitVersion(version1) |
| 84 split2 = SplitVersion(version2) |
| 85 if split1[0] == split2[0]: |
| 86 return cmp(split1[1:], split2[1:]) |
| 87 |
| 88 if split1 == 'trunk' or split2 == 'trunk': |
| 89 raise Exception("Unable to compare versions %s and %s" % ( |
| 90 version1, version2)) |
| 91 |
| 92 return cmp(split1, split2) |
56 | 93 |
57 | 94 |
58 def JoinVersion(version_tuple): | 95 def JoinVersion(version_tuple): |
59 """Create a string from a version tuple. | 96 """Create a string from a version tuple. |
60 | 97 |
61 The tuple should be of the form (18, 0, 1025, 163). | 98 The tuple should be of the form (18, 0, 1025, 163). |
62 """ | 99 """ |
| 100 assert len(version_tuple) == 4 |
| 101 assert version_tuple[0] != 'trunk' |
63 return '.'.join(map(str, version_tuple)) | 102 return '.'.join(map(str, version_tuple)) |
64 | 103 |
65 | 104 |
66 def GetTimestampManifestName(): | 105 def GetTimestampManifestName(): |
67 """Create a manifest name with a timestamp. | 106 """Create a manifest name with a timestamp. |
68 | 107 |
69 Returns: | 108 Returns: |
70 A manifest name with an embedded date. This should make it easier to roll | 109 A manifest name with an embedded date. This should make it easier to roll |
71 back if necessary. | 110 back if necessary. |
72 """ | 111 """ |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
139 mac,stable,18.0.1025.168,2012-04-30 20:34:55.231141\n | 178 mac,stable,18.0.1025.168,2012-04-30 20:34:55.231141\n |
140 ... | 179 ... |
141 Where each line has comma separated values in the following format: | 180 Where each line has comma separated values in the following format: |
142 platform, channel, version, date/time\n | 181 platform, channel, version, date/time\n |
143 | 182 |
144 Returns: | 183 Returns: |
145 A list where each element is a line from the document, represented as a | 184 A list where each element is a line from the document, represented as a |
146 tuple.""" | 185 tuple.""" |
147 raise NotImplementedError() | 186 raise NotImplementedError() |
148 | 187 |
149 def GetTrunkRevision(self, version): | |
150 """Given a Chrome version, get its trunk revision. | |
151 | |
152 Args: | |
153 version: A version string of the form '18.0.1025.64' | |
154 Returns: | |
155 The revision number for that version, as a string.""" | |
156 raise NotImplementedError() | |
157 | |
158 def GsUtil_ls(self, url): | 188 def GsUtil_ls(self, url): |
159 """Runs gsutil ls |url| | 189 """Runs gsutil ls |url| |
160 | 190 |
161 Args: | 191 Args: |
162 url: The cloud storage url to list. | 192 url: The cloud storage url to list. |
163 Returns: | 193 Returns: |
164 A list of URLs, all with the gs:// schema.""" | 194 A list of URLs, all with the gs:// schema.""" |
165 raise NotImplementedError() | 195 raise NotImplementedError() |
166 | 196 |
167 def GsUtil_cat(self, url): | 197 def GsUtil_cat(self, url): |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
216 def GetHistory(self): | 246 def GetHistory(self): |
217 """See Delegate.GetHistory""" | 247 """See Delegate.GetHistory""" |
218 url_stream = urllib2.urlopen('https://omahaproxy.appspot.com/history') | 248 url_stream = urllib2.urlopen('https://omahaproxy.appspot.com/history') |
219 history = [(platform, channel, version, date) | 249 history = [(platform, channel, version, date) |
220 for platform, channel, version, date in csv.reader(url_stream)] | 250 for platform, channel, version, date in csv.reader(url_stream)] |
221 | 251 |
222 # The first line of this URL is the header: | 252 # The first line of this URL is the header: |
223 # os,channel,version,timestamp | 253 # os,channel,version,timestamp |
224 return history[1:] | 254 return history[1:] |
225 | 255 |
226 def GetTrunkRevision(self, version): | |
227 """See Delegate.GetTrunkRevision""" | |
228 url = 'http://omahaproxy.appspot.com/revision.json?version=%s' % (version,) | |
229 data = json.loads(urllib2.urlopen(url).read()) | |
230 return 'trunk.%s' % int(data['chromium_revision']) | |
231 | |
232 def GsUtil_ls(self, url): | 256 def GsUtil_ls(self, url): |
233 """See Delegate.GsUtil_ls""" | 257 """See Delegate.GsUtil_ls""" |
234 try: | 258 try: |
235 stdout = self._RunGsUtil(None, False, 'ls', url) | 259 stdout = self._RunGsUtil(None, False, 'ls', url) |
236 except subprocess.CalledProcessError: | 260 except subprocess.CalledProcessError: |
237 return [] | 261 return [] |
238 | 262 |
239 # filter out empty lines | 263 # filter out empty lines |
240 return filter(None, stdout.split('\n')) | 264 return filter(None, stdout.split('\n')) |
241 | 265 |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 major_version: The major version of the pepper bundle, e.g. 19. | 383 major_version: The major version of the pepper bundle, e.g. 19. |
360 Returns: | 384 Returns: |
361 A tuple (version, channel, archives). The version is a string such as | 385 A tuple (version, channel, archives). The version is a string such as |
362 "19.0.1084.41". The channel is one of ('stable', 'beta', or 'dev'). | 386 "19.0.1084.41". The channel is one of ('stable', 'beta', or 'dev'). |
363 |archives| is a list of archive URLs.""" | 387 |archives| is a list of archive URLs.""" |
364 def GetPlatformHistory(platform): | 388 def GetPlatformHistory(platform): |
365 return self._GetPlatformMajorVersionHistory(major_version, platform) | 389 return self._GetPlatformMajorVersionHistory(major_version, platform) |
366 | 390 |
367 shared_version_generator = self._FindNextSharedVersion(self.platforms, | 391 shared_version_generator = self._FindNextSharedVersion(self.platforms, |
368 GetPlatformHistory) | 392 GetPlatformHistory) |
369 return self._DoGetMostRecentSharedVersion(shared_version_generator, | 393 return self._DoGetMostRecentSharedVersion(shared_version_generator) |
370 allow_trunk_revisions=False) | |
371 | 394 |
372 def GetMostRecentSharedCanary(self): | 395 def GetMostRecentSharedCanary(self): |
373 """Returns the most recent version of a canary pepper bundle that exists on | 396 """Returns the most recent version of a canary pepper bundle that exists on |
374 all given platforms. | 397 all given platforms. |
375 | 398 |
376 Canary is special-cased because we don't care about its major version; we | 399 Canary is special-cased because we don't care about its major version; we |
377 always use the most recent canary, regardless of major version. | 400 always use the most recent canary, regardless of major version. |
378 | 401 |
379 Returns: | 402 Returns: |
380 A tuple (version, channel, archives). The version is a string such as | 403 A tuple (version, channel, archives). The version is a string such as |
381 "19.0.1084.41". The channel is always 'canary'. |archives| is a list of | 404 "trunk.123456". The channel is always 'canary'. |archives| is a list of |
382 archive URLs.""" | 405 archive URLs.""" |
383 # Canary versions that differ in the last digit shouldn't be considered | 406 version_generator = self._FindNextTrunkVersion() |
384 # different; this number is typically used to represent an experiment, e.g. | 407 return self._DoGetMostRecentSharedVersion(version_generator) |
385 # using ASAN or aura. | |
386 def CanaryKey(version): | |
387 return version[:-1] | |
388 | 408 |
389 # We don't ship canary on Linux, so it won't appear in self.history. | 409 def GetAvailablePlatformArchivesFor(self, version): |
390 # Instead, we can use the matching Linux trunk build for that version. | |
391 shared_version_generator = self._FindNextSharedVersion( | |
392 set(self.platforms) - set(('linux',)), | |
393 self._GetPlatformCanaryHistory, CanaryKey) | |
394 return self._DoGetMostRecentSharedVersion(shared_version_generator, | |
395 allow_trunk_revisions=True) | |
396 | |
397 def GetAvailablePlatformArchivesFor(self, version, allow_trunk_revisions): | |
398 """Returns a sequence of archives that exist for a given version, on the | 410 """Returns a sequence of archives that exist for a given version, on the |
399 given platforms. | 411 given platforms. |
400 | 412 |
401 The second element of the returned tuple is a list of all platforms that do | 413 The second element of the returned tuple is a list of all platforms that do |
402 not have an archive for the given version. | 414 not have an archive for the given version. |
403 | 415 |
404 Args: | 416 Args: |
405 version: The version to find archives for. (e.g. "18.0.1025.164") | 417 version: The version to find archives for. (e.g. "18.0.1025.164") |
406 allow_trunk_revisions: If True, will search for archives using the | |
407 trunk revision that matches the branch version. | |
408 Returns: | 418 Returns: |
409 A tuple (archives, missing_archives). |archives| is a list of archive | 419 A tuple (archives, missing_archives). |archives| is a list of archive |
410 URLs, |missing_archives| is a list of archive names. | 420 URLs, |missing_archives| is a list of archive names. |
411 """ | 421 """ |
412 archive_urls = self._GetAvailableArchivesFor(version) | 422 archive_urls = self._GetAvailableArchivesFor(version) |
413 | 423 |
414 if self.is_bionic: | 424 if self.is_bionic: |
415 # Bionic currently is Linux-only. | 425 # Bionic currently is Linux-only. |
416 expected_archives = set([GetBionicArchiveName()]) | 426 expected_archives = set([GetBionicArchiveName()]) |
417 else: | 427 else: |
418 expected_archives = set(GetPlatformArchiveName(p) for p in self.platforms) | 428 expected_archives = set(GetPlatformArchiveName(p) for p in self.platforms) |
419 | 429 |
420 if self.extra_archives: | 430 if self.extra_archives: |
421 for extra_archive, extra_archive_min_version in self.extra_archives: | 431 for extra_archive, extra_archive_min_version in self.extra_archives: |
422 if SplitVersion(version) >= SplitVersion(extra_archive_min_version): | 432 if CompareVersions(version, extra_archive_min_version) >= 0: |
423 expected_archives.add(extra_archive) | 433 expected_archives.add(extra_archive) |
424 found_archives = set(GetCanonicalArchiveName(a) for a in archive_urls) | 434 found_archives = set(GetCanonicalArchiveName(a) for a in archive_urls) |
425 missing_archives = expected_archives - found_archives | 435 missing_archives = expected_archives - found_archives |
426 | 436 |
427 if allow_trunk_revisions and missing_archives: | |
428 # Try to find trunk versions of any missing archives. | |
429 trunk_version = self.delegate.GetTrunkRevision(version) | |
430 trunk_archives = self._GetAvailableArchivesFor(trunk_version) | |
431 for trunk_archive_url in trunk_archives: | |
432 trunk_archive = GetCanonicalArchiveName(trunk_archive_url) | |
433 if trunk_archive in missing_archives: | |
434 archive_urls.append(trunk_archive_url) | |
435 missing_archives.discard(trunk_archive) | |
436 | |
437 # Only return archives that are "expected". | 437 # Only return archives that are "expected". |
438 def IsExpected(url): | 438 def IsExpected(url): |
439 return GetCanonicalArchiveName(url) in expected_archives | 439 return GetCanonicalArchiveName(url) in expected_archives |
440 | 440 |
441 expected_archive_urls = [u for u in archive_urls if IsExpected(u)] | 441 expected_archive_urls = [u for u in archive_urls if IsExpected(u)] |
442 return expected_archive_urls, missing_archives | 442 return expected_archive_urls, missing_archives |
443 | 443 |
444 def _DoGetMostRecentSharedVersion(self, shared_version_generator, | 444 def _DoGetMostRecentSharedVersion(self, shared_version_generator): |
445 allow_trunk_revisions): | |
446 """Returns the most recent version of a pepper bundle that exists on all | 445 """Returns the most recent version of a pepper bundle that exists on all |
447 given platforms. | 446 given platforms. |
448 | 447 |
449 This function does the real work for the public GetMostRecentShared* above. | 448 This function does the real work for the public GetMostRecentShared* above. |
450 | 449 |
451 Args: | 450 Args: |
452 shared_version_generator: A generator that will yield (version, channel) | 451 shared_version_generator: A generator that will yield (version, channel) |
453 tuples in order of most recent to least recent. | 452 tuples in order of most recent to least recent. |
454 allow_trunk_revisions: If True, will search for archives using the | |
455 trunk revision that matches the branch version. | |
456 Returns: | 453 Returns: |
457 A tuple (version, channel, archives). The version is a string such as | 454 A tuple (version, channel, archives). The version is a string such as |
458 "19.0.1084.41". The channel is one of ('stable', 'beta', 'dev', | 455 "19.0.1084.41". The channel is one of ('stable', 'beta', 'dev', |
459 'canary'). |archives| is a list of archive URLs.""" | 456 'canary'). |archives| is a list of archive URLs.""" |
460 version = None | 457 version = None |
461 skipped_versions = [] | 458 skipped_versions = [] |
462 channel = '' | 459 channel = '' |
463 while True: | 460 while True: |
464 try: | 461 try: |
465 version, channel = shared_version_generator.next() | 462 version, channel = shared_version_generator.next() |
466 except StopIteration: | 463 except StopIteration: |
467 msg = 'No shared version for platforms: %s\n' % ( | 464 msg = 'No shared version for platforms: %s\n' % ( |
468 ', '.join(self.platforms)) | 465 ', '.join(self.platforms)) |
469 msg += 'Last version checked = %s.\n' % (version,) | 466 msg += 'Last version checked = %s.\n' % (version,) |
470 if skipped_versions: | 467 if skipped_versions: |
471 msg += 'Versions skipped due to missing archives:\n' | 468 msg += 'Versions skipped due to missing archives:\n' |
472 for version, channel, missing_archives in skipped_versions: | 469 for version, channel, missing_archives in skipped_versions: |
473 archive_msg = '(missing %s)' % (', '.join(missing_archives)) | 470 archive_msg = '(missing %s)' % (', '.join(missing_archives)) |
474 msg += ' %s (%s) %s\n' % (version, channel, archive_msg) | 471 msg += ' %s (%s) %s\n' % (version, channel, archive_msg) |
475 raise NoSharedVersionException(msg) | 472 raise NoSharedVersionException(msg) |
476 | 473 |
477 logger.info('Found shared version: %s, channel: %s' % ( | 474 logger.info('Found shared version: %s, channel: %s' % ( |
478 version, channel)) | 475 version, channel)) |
479 | 476 |
480 archives, missing_archives = self.GetAvailablePlatformArchivesFor( | 477 archives, missing_archives = self.GetAvailablePlatformArchivesFor(version) |
481 version, allow_trunk_revisions) | |
482 | 478 |
483 if not missing_archives: | 479 if not missing_archives: |
484 return version, channel, archives | 480 return version, channel, archives |
485 | 481 |
486 logger.info(' skipping. Missing archives: %s' % ( | 482 logger.info(' skipping. Missing archives: %s' % ( |
487 ', '.join(missing_archives))) | 483 ', '.join(missing_archives))) |
488 | 484 |
489 skipped_versions.append((version, channel, missing_archives)) | 485 skipped_versions.append((version, channel, missing_archives)) |
490 | 486 |
491 def _GetPlatformMajorVersionHistory(self, with_major_version, with_platform): | 487 def _GetPlatformMajorVersionHistory(self, with_major_version, with_platform): |
492 """Yields Chrome history for a given platform and major version. | 488 """Yields Chrome history for a given platform and major version. |
493 | 489 |
494 Args: | 490 Args: |
495 with_major_version: The major version to filter for. If 0, match all | 491 with_major_version: The major version to filter for. If 0, match all |
496 versions. | 492 versions. |
497 with_platform: The name of the platform to filter for. | 493 with_platform: The name of the platform to filter for. |
498 Returns: | 494 Returns: |
499 A generator that yields a tuple (channel, version) for each version that | 495 A generator that yields a tuple (channel, version) for each version that |
500 matches the platform and major version. The version returned is a tuple as | 496 matches the platform and major version. The version returned is a tuple as |
501 returned from SplitVersion. | 497 returned from SplitVersion. |
502 """ | 498 """ |
503 for platform, channel, version, _ in self.history: | 499 for platform, channel, version, _ in self.history: |
504 version = SplitVersion(version) | 500 version = SplitVersion(version) |
505 if (with_platform == platform and | 501 if (with_platform == platform and |
506 (with_major_version == 0 or with_major_version == version[0])): | 502 (with_major_version == 0 or with_major_version == version[0])): |
507 yield channel, version | 503 yield channel, version |
508 | 504 |
509 def _GetPlatformCanaryHistory(self, with_platform): | 505 def _FindNextSharedVersion(self, platforms, generator_func): |
510 """Yields Chrome history for a given platform, but only for canary | |
511 versions. | |
512 | |
513 Args: | |
514 with_platform: The name of the platform to filter for. | |
515 Returns: | |
516 A generator that yields a tuple (channel, version) for each version that | |
517 matches the platform and uses the canary channel. The version returned is | |
518 a tuple as returned from SplitVersion. | |
519 """ | |
520 for platform, channel, version, _ in self.history: | |
521 version = SplitVersion(version) | |
522 if with_platform == platform and channel == CANARY: | |
523 yield channel, version | |
524 | |
525 | |
526 def _FindNextSharedVersion(self, platforms, generator_func, key_func=None): | |
527 """Yields versions of Chrome that exist on all given platforms, in order of | 506 """Yields versions of Chrome that exist on all given platforms, in order of |
528 newest to oldest. | 507 newest to oldest. |
529 | 508 |
530 Versions are compared in reverse order of release. That is, the most | 509 Versions are compared in reverse order of release. That is, the most |
531 recently updated version will be tested first. | 510 recently updated version will be tested first. |
532 | 511 |
533 Args: | 512 Args: |
534 platforms: A sequence of platforms to consider, e.g. | 513 platforms: A sequence of platforms to consider, e.g. |
535 ('mac', 'linux', 'win') | 514 ('mac', 'linux', 'win') |
536 generator_func: A function which takes a platform and returns a | 515 generator_func: A function which takes a platform and returns a |
537 generator that yields (channel, version) tuples. | 516 generator that yields (channel, version) tuples. |
538 key_func: A function to convert the version into a value that should be | |
539 used for comparison. See python built-in sorted() or min(), for | |
540 an example. | |
541 Returns: | 517 Returns: |
542 A generator that yields a tuple (version, channel) for each version that | 518 A generator that yields a tuple (version, channel) for each version that |
543 matches all platforms and the major version. The version returned is a | 519 matches all platforms and the major version. The version returned is a |
544 string (e.g. "18.0.1025.164"). | 520 string (e.g. "18.0.1025.164"). |
545 """ | 521 """ |
546 if not key_func: | |
547 key_func = lambda x: x | |
548 | |
549 platform_generators = [] | 522 platform_generators = [] |
550 for platform in platforms: | 523 for platform in platforms: |
551 platform_generators.append(generator_func(platform)) | 524 platform_generators.append(generator_func(platform)) |
552 | 525 |
553 shared_version = None | 526 shared_version = None |
554 platform_versions = [] | 527 platform_versions = [] |
555 for platform_gen in platform_generators: | 528 for platform_gen in platform_generators: |
556 platform_versions.append(platform_gen.next()) | 529 platform_versions.append(platform_gen.next()) |
557 | 530 |
558 while True: | 531 while True: |
559 if logger.isEnabledFor(logging.INFO): | 532 if logger.isEnabledFor(logging.INFO): |
560 msg_info = [] | 533 msg_info = [] |
561 for i, platform in enumerate(platforms): | 534 for i, platform in enumerate(platforms): |
562 msg_info.append('%s: %s' % ( | 535 msg_info.append('%s: %s' % ( |
563 platform, JoinVersion(platform_versions[i][1]))) | 536 platform, JoinVersion(platform_versions[i][1]))) |
564 logger.info('Checking versions: %s' % ', '.join(msg_info)) | 537 logger.info('Checking versions: %s' % ', '.join(msg_info)) |
565 | 538 |
566 shared_version = min((v for c, v in platform_versions), key=key_func) | 539 shared_version = min((v for c, v in platform_versions)) |
567 | 540 |
568 if all(key_func(v) == key_func(shared_version) | 541 if all(v == shared_version for c, v in platform_versions): |
569 for c, v in platform_versions): | |
570 # The real shared_version should be the real minimum version. This will | |
571 # be different from shared_version above only if key_func compares two | |
572 # versions with different values as equal. | |
573 min_version = min((v for c, v in platform_versions)) | |
574 | |
575 # grab the channel from an arbitrary platform | 542 # grab the channel from an arbitrary platform |
576 first_platform = platform_versions[0] | 543 first_platform = platform_versions[0] |
577 channel = first_platform[0] | 544 channel = first_platform[0] |
578 yield JoinVersion(min_version), channel | 545 yield JoinVersion(shared_version), channel |
579 | 546 |
580 # force increment to next version for all platforms | 547 # force increment to next version for all platforms |
581 shared_version = None | 548 shared_version = None |
582 | 549 |
583 # Find the next version for any platform that isn't at the shared version. | 550 # Find the next version for any platform that isn't at the shared version. |
584 try: | 551 try: |
585 for i, platform_gen in enumerate(platform_generators): | 552 for i, platform_gen in enumerate(platform_generators): |
586 if platform_versions[i][1] != shared_version: | 553 if platform_versions[i][1] != shared_version: |
587 platform_versions[i] = platform_gen.next() | 554 platform_versions[i] = platform_gen.next() |
588 except StopIteration: | 555 except StopIteration: |
589 return | 556 return |
590 | 557 |
591 | 558 |
| 559 def _FindNextTrunkVersion(self): |
| 560 """Yields all trunk versions that exist in the cloud storage bucket, newest |
| 561 to oldest. |
| 562 |
| 563 Returns: |
| 564 A generator that yields a tuple (version, channel) for each version that |
| 565 matches all platforms and the major version. The version returned is a |
| 566 string (e.g. "trunk.123456"). |
| 567 """ |
| 568 files = self.delegate.GsUtil_ls(GS_BUCKET_PATH) |
| 569 assert all(f.startswith('gs://') for f in files) |
| 570 |
| 571 trunk_versions = [] |
| 572 for f in files: |
| 573 match = re.search(r'(trunk\.\d+)', f) |
| 574 if match: |
| 575 trunk_versions.append(match.group(1)) |
| 576 |
| 577 trunk_versions.sort(reverse=True) |
| 578 |
| 579 for version in trunk_versions: |
| 580 yield version, 'canary' |
| 581 |
| 582 |
592 def _GetAvailableArchivesFor(self, version_string): | 583 def _GetAvailableArchivesFor(self, version_string): |
593 """Downloads a list of all available archives for a given version. | 584 """Downloads a list of all available archives for a given version. |
594 | 585 |
595 Args: | 586 Args: |
596 version_string: The version to find archives for. (e.g. "18.0.1025.164") | 587 version_string: The version to find archives for. (e.g. "18.0.1025.164") |
597 Returns: | 588 Returns: |
598 A list of strings, each of which is a platform-specific archive URL. (e.g. | 589 A list of strings, each of which is a platform-specific archive URL. (e.g. |
599 "gs://nativeclient_mirror/nacl/nacl_sdk/18.0.1025.164/" | 590 "gs://nativeclient_mirror/nacl/nacl_sdk/18.0.1025.164/" |
600 "naclsdk_linux.tar.bz2"). | 591 "naclsdk_linux.tar.bz2"). |
601 | 592 |
602 All returned URLs will use the gs:// schema.""" | 593 All returned URLs will use the gs:// schema.""" |
603 files = self.delegate.GsUtil_ls(GS_BUCKET_PATH + version_string) | 594 files = self.delegate.GsUtil_ls(GS_BUCKET_PATH + version_string) |
604 | 595 |
605 assert all(file.startswith('gs://') for file in files) | 596 assert all(f.startswith('gs://') for f in files) |
606 | 597 |
607 archives = [f for f in files if not f.endswith('.json')] | 598 archives = [f for f in files if not f.endswith('.json')] |
608 manifests = [f for f in files if f.endswith('.json')] | 599 manifests = [f for f in files if f.endswith('.json')] |
609 | 600 |
610 # don't include any archives that don't have an associated manifest. | 601 # don't include any archives that don't have an associated manifest. |
611 return filter(lambda a: a + '.json' in manifests, archives) | 602 return filter(lambda a: a + '.json' in manifests, archives) |
612 | 603 |
613 | 604 |
614 class UnknownLockedBundleException(Exception): | 605 class UnknownLockedBundleException(Exception): |
615 pass | 606 pass |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
650 | 641 |
651 Note that bundles will not be updated if the current version is newer. | 642 Note that bundles will not be updated if the current version is newer. |
652 That is, the updater will never automatically update to an older version of | 643 That is, the updater will never automatically update to an older version of |
653 a bundle. | 644 a bundle. |
654 | 645 |
655 Args: | 646 Args: |
656 manifest: The manifest used as a template for updating. Only pepper | 647 manifest: The manifest used as a template for updating. Only pepper |
657 bundles that contain no archives will be considered for auto-updating.""" | 648 bundles that contain no archives will be considered for auto-updating.""" |
658 # Make sure there is only one stable branch: the one with the max version. | 649 # Make sure there is only one stable branch: the one with the max version. |
659 # All others are post-stable. | 650 # All others are post-stable. |
660 stable_major_versions = [SplitVersion(version)[0] for _, version, channel, _ | 651 stable_major_versions = [GetMajorVersion(version) for _, version, channel, _ |
661 in self.versions_to_update if channel == 'stable'] | 652 in self.versions_to_update if channel == 'stable'] |
662 # Add 0 in case there are no stable versions. | 653 # Add 0 in case there are no stable versions. |
663 max_stable_version = max([0] + stable_major_versions) | 654 max_stable_version = max([0] + stable_major_versions) |
664 | 655 |
665 # Ensure that all locked bundles exist in the online manifest. | 656 # Ensure that all locked bundles exist in the online manifest. |
666 for bundle_name in self.locked_bundles: | 657 for bundle_name in self.locked_bundles: |
667 online_bundle = self.online_manifest.GetBundle(bundle_name) | 658 online_bundle = self.online_manifest.GetBundle(bundle_name) |
668 if online_bundle: | 659 if online_bundle: |
669 manifest.SetBundle(online_bundle) | 660 manifest.SetBundle(online_bundle) |
670 else: | 661 else: |
(...skipping 19 matching lines...) Expand all Loading... |
690 bundle = manifest.GetBundle(bundle_name) | 681 bundle = manifest.GetBundle(bundle_name) |
691 for archive in archives: | 682 for archive in archives: |
692 platform_bundle = self._GetPlatformArchiveBundle(archive) | 683 platform_bundle = self._GetPlatformArchiveBundle(archive) |
693 # Normally the manifest snippet's bundle name matches our bundle name. | 684 # Normally the manifest snippet's bundle name matches our bundle name. |
694 # pepper_canary, however is called "pepper_###" in the manifest | 685 # pepper_canary, however is called "pepper_###" in the manifest |
695 # snippet. | 686 # snippet. |
696 platform_bundle.name = bundle_name | 687 platform_bundle.name = bundle_name |
697 bundle.MergeWithBundle(platform_bundle) | 688 bundle.MergeWithBundle(platform_bundle) |
698 | 689 |
699 # Fix the stability and recommended values | 690 # Fix the stability and recommended values |
700 major_version = SplitVersion(version)[0] | 691 major_version = GetMajorVersion(version) |
701 if major_version < max_stable_version: | 692 if major_version < max_stable_version: |
702 bundle.stability = 'post_stable' | 693 bundle.stability = 'post_stable' |
703 else: | 694 else: |
704 bundle.stability = channel | 695 bundle.stability = channel |
705 # We always recommend the stable version. | 696 # We always recommend the stable version. |
706 if bundle.stability == 'stable': | 697 if bundle.stability == 'stable': |
707 bundle.recommended = 'yes' | 698 bundle.recommended = 'yes' |
708 else: | 699 else: |
709 bundle.recommended = 'no' | 700 bundle.recommended = 'no' |
710 | 701 |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
825 if not auto_update_bundles: | 816 if not auto_update_bundles: |
826 logger.info('No versions need auto-updating.') | 817 logger.info('No versions need auto-updating.') |
827 return | 818 return |
828 | 819 |
829 updater = Updater(delegate) | 820 updater = Updater(delegate) |
830 | 821 |
831 for bundle in auto_update_bundles: | 822 for bundle in auto_update_bundles: |
832 try: | 823 try: |
833 if bundle.name == BIONIC_CANARY_BUNDLE_NAME: | 824 if bundle.name == BIONIC_CANARY_BUNDLE_NAME: |
834 logger.info('>>> Looking for most recent bionic_canary...') | 825 logger.info('>>> Looking for most recent bionic_canary...') |
835 version_finder = VersionFinder(delegate, platforms, extra_archives, | 826 # Ignore extra_archives on bionic; There is no naclports bundle yet. |
| 827 version_finder = VersionFinder(delegate, platforms, None, |
836 is_bionic=True) | 828 is_bionic=True) |
837 version, channel, archives = version_finder.GetMostRecentSharedCanary() | 829 version, channel, archives = version_finder.GetMostRecentSharedCanary() |
838 elif bundle.name == CANARY_BUNDLE_NAME: | 830 elif bundle.name == CANARY_BUNDLE_NAME: |
839 logger.info('>>> Looking for most recent pepper_canary...') | 831 logger.info('>>> Looking for most recent pepper_canary...') |
840 version_finder = VersionFinder(delegate, platforms, extra_archives) | 832 version_finder = VersionFinder(delegate, platforms, extra_archives) |
841 version, channel, archives = version_finder.GetMostRecentSharedCanary() | 833 version, channel, archives = version_finder.GetMostRecentSharedCanary() |
842 else: | 834 else: |
843 logger.info('>>> Looking for most recent pepper_%s...' % | 835 logger.info('>>> Looking for most recent pepper_%s...' % |
844 bundle.version) | 836 bundle.version) |
845 version_finder = VersionFinder(delegate, platforms, extra_archives) | 837 version_finder = VersionFinder(delegate, platforms, extra_archives) |
846 version, channel, archives = version_finder.GetMostRecentSharedVersion( | 838 version, channel, archives = version_finder.GetMostRecentSharedVersion( |
847 bundle.version) | 839 bundle.version) |
848 except NoSharedVersionException: | 840 except NoSharedVersionException: |
849 # If we can't find a shared version, make sure that there is an uploaded | 841 # If we can't find a shared version, make sure that there is an uploaded |
850 # bundle with that name already. | 842 # bundle with that name already. |
851 updater.AddLockedBundle(bundle.name) | 843 updater.AddLockedBundle(bundle.name) |
852 continue | 844 continue |
853 | 845 |
854 if bundle.name in fixed_bundle_versions: | 846 if bundle.name in fixed_bundle_versions: |
855 # Ensure this version is valid for all platforms. | 847 # Ensure this version is valid for all platforms. |
856 # If it is, use the channel found above (because the channel for this | 848 # If it is, use the channel found above (because the channel for this |
857 # version may not be in the history.) | 849 # version may not be in the history.) |
858 version = fixed_bundle_versions[bundle.name] | 850 version = fixed_bundle_versions[bundle.name] |
859 logger.info('Fixed bundle version: %s, %s' % (bundle.name, version)) | 851 logger.info('Fixed bundle version: %s, %s' % (bundle.name, version)) |
860 allow_trunk_revisions = bundle.name == CANARY_BUNDLE_NAME | 852 archives, missing = \ |
861 archives, missing = version_finder.GetAvailablePlatformArchivesFor( | 853 version_finder.GetAvailablePlatformArchivesFor(version) |
862 version, allow_trunk_revisions) | |
863 if missing: | 854 if missing: |
864 logger.warn( | 855 logger.warn( |
865 'Some archives for version %s of bundle %s don\'t exist: ' | 856 'Some archives for version %s of bundle %s don\'t exist: ' |
866 'Missing %s' % (version, bundle.name, ', '.join(missing))) | 857 'Missing %s' % (version, bundle.name, ', '.join(missing))) |
867 return | 858 return |
868 | 859 |
869 updater.AddVersionToUpdate(bundle.name, version, channel, archives) | 860 updater.AddVersionToUpdate(bundle.name, version, channel, archives) |
870 | 861 |
871 updater.Update(manifest) | 862 updater.Update(manifest) |
872 | 863 |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
962 gsutil_logging_handler.upload() | 953 gsutil_logging_handler.upload() |
963 except manifest_util.Error as e: | 954 except manifest_util.Error as e: |
964 if options.debug: | 955 if options.debug: |
965 raise | 956 raise |
966 sys.stderr.write(str(e) + '\n') | 957 sys.stderr.write(str(e) + '\n') |
967 return 1 | 958 return 1 |
968 | 959 |
969 | 960 |
970 if __name__ == '__main__': | 961 if __name__ == '__main__': |
971 sys.exit(main(sys.argv)) | 962 sys.exit(main(sys.argv)) |
OLD | NEW |