| LEFT | RIGHT | 
|---|
| 1 # This file is part of the Adblock Plus web scripts, | 1 # This file is part of the Adblock Plus web scripts, | 
| 2 # Copyright (C) 2006-present eyeo GmbH | 2 # Copyright (C) 2006-present eyeo GmbH | 
| 3 # | 3 # | 
| 4 # Adblock Plus is free software: you can redistribute it and/or modify | 4 # Adblock Plus is free software: you can redistribute it and/or modify | 
| 5 # it under the terms of the GNU General Public License version 3 as | 5 # it under the terms of the GNU General Public License version 3 as | 
| 6 # published by the Free Software Foundation. | 6 # published by the Free Software Foundation. | 
| 7 # | 7 # | 
| 8 # Adblock Plus is distributed in the hope that it will be useful, | 8 # Adblock Plus is distributed in the hope that it will be useful, | 
| 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| (...skipping 27 matching lines...) Expand all  Loading... | 
| 38 import subprocess | 38 import subprocess | 
| 39 import sys | 39 import sys | 
| 40 import tempfile | 40 import tempfile | 
| 41 import time | 41 import time | 
| 42 import uuid | 42 import uuid | 
| 43 from urllib import urlencode | 43 from urllib import urlencode | 
| 44 import urllib2 | 44 import urllib2 | 
| 45 import urlparse | 45 import urlparse | 
| 46 import zipfile | 46 import zipfile | 
| 47 import contextlib | 47 import contextlib | 
|  | 48 from xml.dom.minidom import parse as parseXml | 
| 48 | 49 | 
| 49 from Crypto.PublicKey import RSA | 50 from Crypto.PublicKey import RSA | 
| 50 from Crypto.Signature import PKCS1_v1_5 | 51 from Crypto.Signature import PKCS1_v1_5 | 
| 51 import Crypto.Hash.SHA256 | 52 import Crypto.Hash.SHA256 | 
| 52 | 53 | 
| 53 from xml.dom.minidom import parse as parseXml |  | 
| 54 |  | 
| 55 from sitescripts.extensions.utils import ( | 54 from sitescripts.extensions.utils import ( | 
| 56     compareVersions, Configuration, | 55     compareVersions, Configuration, | 
| 57     writeAndroidUpdateManifest | 56     writeAndroidUpdateManifest, | 
| 58 ) | 57 ) | 
| 59 from sitescripts.utils import get_config, get_template | 58 from sitescripts.utils import get_config, get_template | 
| 60 | 59 | 
| 61 MAX_BUILDS = 50 | 60 MAX_BUILDS = 50 | 
| 62 | 61 | 
| 63 | 62 | 
| 64 # Google and Microsoft APIs use HTTP error codes with error message in | 63 # Google and Microsoft APIs use HTTP error codes with error message in | 
| 65 # body. So we add the response body to the HTTPError to get more | 64 # body. So we add the response body to the HTTPError to get more | 
| 66 # meaningful error messages. | 65 # meaningful error messages. | 
| 67 class HTTPErrorBodyHandler(urllib2.HTTPDefaultErrorHandler): | 66 class HTTPErrorBodyHandler(urllib2.HTTPDefaultErrorHandler): | 
| (...skipping 28 matching lines...) Expand all  Loading... | 
| 96 | 95 | 
| 97     def hasChanges(self): | 96     def hasChanges(self): | 
| 98         return self.revision != self.previousRevision | 97         return self.revision != self.previousRevision | 
| 99 | 98 | 
| 100     def getCurrentRevision(self): | 99     def getCurrentRevision(self): | 
| 101         """ | 100         """ | 
| 102             retrieves the current revision ID from the repository | 101             retrieves the current revision ID from the repository | 
| 103         """ | 102         """ | 
| 104         command = [ | 103         command = [ | 
| 105             'hg', 'id', '-i', '-r', self.config.revision, '--config', | 104             'hg', 'id', '-i', '-r', self.config.revision, '--config', | 
| 106             'defaults.id=', self.config.repository | 105             'defaults.id=', self.config.repository, | 
| 107         ] | 106         ] | 
| 108         return subprocess.check_output(command).strip() | 107         return subprocess.check_output(command).strip() | 
| 109 | 108 | 
| 110     def getCurrentBuild(self): | 109     def getCurrentBuild(self): | 
| 111         """ | 110         """ | 
| 112             calculates the (typically numerical) build ID for the current build | 111             calculates the (typically numerical) build ID for the current build | 
| 113         """ | 112         """ | 
| 114         command = ['hg', 'id', '-n', '--config', 'defaults.id=', self.tempdir] | 113         command = ['hg', 'id', '-n', '--config', 'defaults.id=', self.tempdir] | 
| 115         build = subprocess.check_output(command).strip() | 114         build = subprocess.check_output(command).strip() | 
| 116         return build | 115         return build | 
| 117 | 116 | 
| 118     def getChanges(self): | 117     def getChanges(self): | 
| 119         """ | 118         """ | 
| 120           retrieve changes between the current and previous ("first") revision | 119           retrieve changes between the current and previous ("first") revision | 
| 121         """ | 120         """ | 
| 122         command = [ | 121         command = [ | 
| 123             'hg', 'log', '-R', self.tempdir, '-r', | 122             'hg', 'log', '-R', self.tempdir, '-r', | 
| 124             'reverse(ancestors({}))'.format(self.config.revision), '-l', '50', | 123             'reverse(ancestors({}))'.format(self.config.revision), '-l', '50', | 
| 125             '--encoding', 'utf-8', '--template', | 124             '--encoding', 'utf-8', '--template', | 
| 126             '{date|isodate}\\0{author|person}\\0{rev}\\0{desc}\\0\\0', | 125             '{date|isodate}\\0{author|person}\\0{rev}\\0{desc}\\0\\0', | 
| 127             '--config', 'defaults.log=' | 126             '--config', 'defaults.log=', | 
| 128         ] | 127         ] | 
| 129         result = subprocess.check_output(command).decode('utf-8') | 128         result = subprocess.check_output(command).decode('utf-8') | 
| 130 | 129 | 
| 131         for change in result.split('\x00\x00'): | 130         for change in result.split('\x00\x00'): | 
| 132             if change: | 131             if change: | 
| 133                 date, author, revision, description = change.split('\x00') | 132                 date, author, revision, description = change.split('\x00') | 
| 134                 yield {'date': date, 'author': author, 'revision': revision, 'de
     scription': description} | 133                 yield {'date': date, 'author': author, 'revision': revision, 'de
     scription': description} | 
| 135 | 134 | 
| 136     def copyRepository(self): | 135     def copyRepository(self): | 
| 137         """ | 136         """ | 
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 279             os.makedirs(baseDir) | 278             os.makedirs(baseDir) | 
| 280 | 279 | 
| 281         # ABP for Android used to have its own update manifest format. We need t
     o | 280         # ABP for Android used to have its own update manifest format. We need t
     o | 
| 282         # generate both that and the new one in the libadblockplus format as lon
     g | 281         # generate both that and the new one in the libadblockplus format as lon
     g | 
| 283         # as a significant amount of users is on an old version. | 282         # as a significant amount of users is on an old version. | 
| 284         if self.config.type == 'android': | 283         if self.config.type == 'android': | 
| 285             newManifestPath = os.path.join(baseDir, 'update.json') | 284             newManifestPath = os.path.join(baseDir, 'update.json') | 
| 286             writeAndroidUpdateManifest(newManifestPath, [{ | 285             writeAndroidUpdateManifest(newManifestPath, [{ | 
| 287                 'basename': self.basename, | 286                 'basename': self.basename, | 
| 288                 'version': self.version, | 287                 'version': self.version, | 
| 289                 'updateURL': self.updateURL | 288                 'updateURL': self.updateURL, | 
| 290             }]) | 289             }]) | 
| 291 | 290 | 
| 292         template = get_template(get_config().get('extensions', templateName), | 291         template = get_template(get_config().get('extensions', templateName), | 
| 293                                 autoescape=autoescape) | 292                                 autoescape=autoescape) | 
| 294         template.stream({'extensions': [self]}).dump(manifestPath) | 293         template.stream({'extensions': [self]}).dump(manifestPath) | 
| 295 | 294 | 
| 296     def writeIEUpdateManifest(self, versions): | 295     def writeIEUpdateManifest(self, versions): | 
| 297         """ | 296         """ | 
| 298           Writes update.json file for the latest IE build | 297           Writes update.json file for the latest IE build | 
| 299         """ | 298         """ | 
| 300         if len(versions) == 0: | 299         if len(versions) == 0: | 
| 301             return | 300             return | 
| 302 | 301 | 
| 303         version = versions[0] | 302         version = versions[0] | 
| 304         packageName = self.basename + '-' + version + self.config.packageSuffix | 303         packageName = self.basename + '-' + version + self.config.packageSuffix | 
| 305         updateURL = urlparse.urljoin(self.config.nightliesURL, self.basename + '
     /' + packageName + '?update') | 304         updateURL = urlparse.urljoin(self.config.nightliesURL, self.basename + '
     /' + packageName + '?update') | 
| 306         baseDir = os.path.join(self.config.nightliesDirectory, self.basename) | 305         baseDir = os.path.join(self.config.nightliesDirectory, self.basename) | 
| 307         manifestPath = os.path.join(baseDir, 'update.json') | 306         manifestPath = os.path.join(baseDir, 'update.json') | 
| 308 | 307 | 
| 309         from sitescripts.extensions.utils import writeIEUpdateManifest as doWrit
     e | 308         from sitescripts.extensions.utils import writeIEUpdateManifest as doWrit
     e | 
| 310         doWrite(manifestPath, [{ | 309         doWrite(manifestPath, [{ | 
| 311             'basename': self.basename, | 310             'basename': self.basename, | 
| 312             'version': version, | 311             'version': version, | 
| 313             'updateURL': updateURL | 312             'updateURL': updateURL, | 
| 314         }]) | 313         }]) | 
| 315 | 314 | 
| 316         for suffix in ['-x86.msi', '-x64.msi', '-gpo-x86.msi', '-gpo-x64.msi']: | 315         for suffix in ['-x86.msi', '-x64.msi', '-gpo-x86.msi', '-gpo-x64.msi']: | 
| 317             linkPath = os.path.join(baseDir, '00latest%s' % suffix) | 316             linkPath = os.path.join(baseDir, '00latest%s' % suffix) | 
| 318             outputPath = os.path.join(baseDir, self.basename + '-' + version + s
     uffix) | 317             outputPath = os.path.join(baseDir, self.basename + '-' + version + s
     uffix) | 
| 319             self.symlink_or_copy(outputPath, linkPath) | 318             self.symlink_or_copy(outputPath, linkPath) | 
| 320 | 319 | 
| 321     def build(self): | 320     def build(self): | 
| 322         """ | 321         """ | 
| 323           run the build command in the tempdir | 322           run the build command in the tempdir | 
| 324         """ | 323         """ | 
| 325         baseDir = os.path.join(self.config.nightliesDirectory, self.basename) | 324         baseDir = os.path.join(self.config.nightliesDirectory, self.basename) | 
| 326         if not os.path.exists(baseDir): | 325         if not os.path.exists(baseDir): | 
| 327             os.makedirs(baseDir) | 326             os.makedirs(baseDir) | 
| 328         outputFile = '%s-%s%s' % (self.basename, self.version, self.config.packa
     geSuffix) | 327         outputFile = '%s-%s%s' % (self.basename, self.version, self.config.packa
     geSuffix) | 
| 329         self.path = os.path.join(baseDir, outputFile) | 328         self.path = os.path.join(baseDir, outputFile) | 
| 330         self.updateURL = urlparse.urljoin(self.config.nightliesURL, self.basenam
     e + '/' + outputFile + '?update') | 329         self.updateURL = urlparse.urljoin(self.config.nightliesURL, self.basenam
     e + '/' + outputFile + '?update') | 
| 331 | 330 | 
| 332         if self.config.type == 'android': | 331         if self.config.type == 'android': | 
| 333             apkFile = open(self.path, 'wb') | 332             apkFile = open(self.path, 'wb') | 
| 334 | 333 | 
| 335             try: | 334             try: | 
| 336                 try: | 335                 try: | 
| 337                     port = get_config().get('extensions', 'androidBuildPort') | 336                     port = get_config().get('extensions', 'androidBuildPort') | 
| 338                 except ConfigParser.NoOptionError: | 337                 except ConfigParser.NoOptionError: | 
| 339                     port = '22' | 338                     port = '22' | 
| 340                 command = ['ssh', '-p', port, get_config().get('extensions', 'an
     droidBuildHost')] | 339                 command = ['ssh', '-p', port, get_config().get('extensions', 'an
     droidBuildHost')] | 
| 341                 command.extend(map(pipes.quote, [ | 340                 command.extend(map(pipes.quote, [ | 
| 342                     '/home/android/bin/makedebugbuild.py', '--revision', | 341                     '/home/android/bin/makedebugbuild.py', '--revision', | 
| 343                     self.buildNum, '--version', self.version, '--stdout' | 342                     self.buildNum, '--version', self.version, '--stdout', | 
| 344                 ])) | 343                 ])) | 
| 345                 subprocess.check_call(command, stdout=apkFile, close_fds=True) | 344                 subprocess.check_call(command, stdout=apkFile, close_fds=True) | 
| 346             except: | 345             except: | 
| 347                 # clear broken output if any | 346                 # clear broken output if any | 
| 348                 if os.path.exists(self.path): | 347                 if os.path.exists(self.path): | 
| 349                     os.remove(self.path) | 348                     os.remove(self.path) | 
| 350                 raise | 349                 raise | 
| 351         else: | 350         else: | 
| 352             env = os.environ | 351             env = os.environ | 
| 353             spiderMonkeyBinary = self.config.spiderMonkeyBinary | 352             spiderMonkeyBinary = self.config.spiderMonkeyBinary | 
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 407             packageFile = self.basename + '-' + version + self.config.packageSuf
     fix | 406             packageFile = self.basename + '-' + version + self.config.packageSuf
     fix | 
| 408             changelogFile = self.basename + '-' + version + '.changelog.xhtml' | 407             changelogFile = self.basename + '-' + version + '.changelog.xhtml' | 
| 409             if not os.path.exists(os.path.join(baseDir, packageFile)): | 408             if not os.path.exists(os.path.join(baseDir, packageFile)): | 
| 410                 # Oops | 409                 # Oops | 
| 411                 continue | 410                 continue | 
| 412 | 411 | 
| 413             link = { | 412             link = { | 
| 414                 'version': version, | 413                 'version': version, | 
| 415                 'download': packageFile, | 414                 'download': packageFile, | 
| 416                 'mtime': os.path.getmtime(os.path.join(baseDir, packageFile)), | 415                 'mtime': os.path.getmtime(os.path.join(baseDir, packageFile)), | 
| 417                 'size': os.path.getsize(os.path.join(baseDir, packageFile)) | 416                 'size': os.path.getsize(os.path.join(baseDir, packageFile)), | 
| 418             } | 417             } | 
| 419             if os.path.exists(os.path.join(baseDir, changelogFile)): | 418             if os.path.exists(os.path.join(baseDir, changelogFile)): | 
| 420                 link['changelog'] = changelogFile | 419                 link['changelog'] = changelogFile | 
| 421             links.append(link) | 420             links.append(link) | 
| 422         template = get_template(get_config().get('extensions', 'nightlyIndexPage
     ')) | 421         template = get_template(get_config().get('extensions', 'nightlyIndexPage
     ')) | 
| 423         template.stream({'config': self.config, 'links': links}).dump(outputPath
     ) | 422         template.stream({'config': self.config, 'links': links}).dump(outputPath
     ) | 
| 424 | 423 | 
| 425     def read_downloads_lockfile(self): | 424     def read_downloads_lockfile(self): | 
| 426         path = get_config().get('extensions', 'downloadLockFile') | 425         path = get_config().get('extensions', 'downloadLockFile') | 
| 427         try: | 426         try: | 
| (...skipping 27 matching lines...) Expand all  Loading... | 
| 455                     del current[platform][i] | 454                     del current[platform][i] | 
| 456                 if len(current[platform]) == 0: | 455                 if len(current[platform]) == 0: | 
| 457                     del current[platform] | 456                     del current[platform] | 
| 458         except KeyError: | 457         except KeyError: | 
| 459             pass | 458             pass | 
| 460         self.write_downloads_lockfile(current) | 459         self.write_downloads_lockfile(current) | 
| 461 | 460 | 
| 462     def azure_jwt_signature_fnc(self): | 461     def azure_jwt_signature_fnc(self): | 
| 463         return ( | 462         return ( | 
| 464             'RS256', | 463             'RS256', | 
| 465             lambda s, m: PKCS1_v1_5.new(s).sign(Crypto.Hash.SHA256.new(m)) | 464             lambda s, m: PKCS1_v1_5.new(s).sign(Crypto.Hash.SHA256.new(m)), | 
| 466         ) | 465         ) | 
| 467 | 466 | 
| 468     def mozilla_jwt_signature_fnc(self): | 467     def mozilla_jwt_signature_fnc(self): | 
| 469         return ( | 468         return ( | 
| 470             'HS256', | 469             'HS256', | 
| 471             lambda s, m: hmac.new(s, msg=m, digestmod=hashlib.sha256).digest() | 470             lambda s, m: hmac.new(s, msg=m, digestmod=hashlib.sha256).digest(), | 
| 472         ) | 471         ) | 
| 473 | 472 | 
| 474     def sign_jwt(self, issuer, secret, url, signature_fnc, jwt_headers={}): | 473     def sign_jwt(self, issuer, secret, url, signature_fnc, jwt_headers={}): | 
| 475         alg, fnc = signature_fnc() | 474         alg, fnc = signature_fnc() | 
| 476 | 475 | 
| 477         header = {'typ': 'JWT'} | 476         header = {'typ': 'JWT'} | 
| 478         header.update(jwt_headers) | 477         header.update(jwt_headers) | 
| 479         header.update({'alg': alg}) | 478         header.update({'alg': alg}) | 
| 480 | 479 | 
| 481         issued = int(time.time()) | 480         issued = int(time.time()) | 
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 517         config = get_config() | 516         config = get_config() | 
| 518 | 517 | 
| 519         upload_url = ('https://addons.mozilla.org/api/v3/addons/{}/' | 518         upload_url = ('https://addons.mozilla.org/api/v3/addons/{}/' | 
| 520                       'versions/{}/').format(self.extensionID, self.version) | 519                       'versions/{}/').format(self.extensionID, self.version) | 
| 521 | 520 | 
| 522         with open(self.path, 'rb') as file: | 521         with open(self.path, 'rb') as file: | 
| 523             data, content_type = urllib3.filepost.encode_multipart_formdata({ | 522             data, content_type = urllib3.filepost.encode_multipart_formdata({ | 
| 524                 'upload': ( | 523                 'upload': ( | 
| 525                     os.path.basename(self.path), | 524                     os.path.basename(self.path), | 
| 526                     file.read(), | 525                     file.read(), | 
| 527                     'application/x-xpinstall' | 526                     'application/x-xpinstall', | 
| 528                 ) | 527                 ), | 
| 529             }) | 528             }) | 
| 530 | 529 | 
| 531         request = self.generate_mozilla_jwt_request( | 530         request = self.generate_mozilla_jwt_request( | 
| 532             config.get('extensions', 'amo_key'), | 531             config.get('extensions', 'amo_key'), | 
| 533             config.get('extensions', 'amo_secret'), | 532             config.get('extensions', 'amo_secret'), | 
| 534             upload_url, | 533             upload_url, | 
| 535             'PUT', | 534             'PUT', | 
| 536             data, | 535             data, | 
| 537             [('Content-Type', content_type)], | 536             [('Content-Type', content_type)], | 
| 538         ) | 537         ) | 
| 539 | 538 | 
| 540         try: | 539         try: | 
| 541             urllib2.urlopen(request).close() | 540             urllib2.urlopen(request).close() | 
| 542         except urllib2.HTTPError as e: | 541         except urllib2.HTTPError as e: | 
| 543             try: | 542             try: | 
| 544                 logging.error(e.read()) | 543                 logging.error(e.read()) | 
| 545             finally: | 544             finally: | 
| 546                 e.close() | 545                 e.close() | 
| 547             raise | 546             raise | 
| 548 | 547 | 
| 549         self.add_to_downloads_lockfile( | 548         self.add_to_downloads_lockfile( | 
| 550             self.config.type, | 549             self.config.type, | 
| 551             { | 550             { | 
| 552                 'buildtype': 'devbuild', | 551                 'buildtype': 'devbuild', | 
| 553                 'app_id': self.extensionID, | 552                 'app_id': self.extensionID, | 
| 554                 'version': self.version, | 553                 'version': self.version, | 
| 555             } | 554             }, | 
| 556         ) | 555         ) | 
| 557         os.remove(self.path) | 556         os.remove(self.path) | 
| 558 | 557 | 
| 559     def download_from_mozilla_addons(self, buildtype, version, app_id): | 558     def download_from_mozilla_addons(self, buildtype, version, app_id): | 
| 560         config = get_config() | 559         config = get_config() | 
| 561         iss = config.get('extensions', 'amo_key') | 560         iss = config.get('extensions', 'amo_key') | 
| 562         secret = config.get('extensions', 'amo_secret') | 561         secret = config.get('extensions', 'amo_secret') | 
| 563 | 562 | 
| 564         url = ('https://addons.mozilla.org/api/v3/addons/{}/' | 563         url = ('https://addons.mozilla.org/api/v3/addons/{}/' | 
| 565                'versions/{}/').format(app_id, version) | 564                'versions/{}/').format(app_id, version) | 
| 566 | 565 | 
| 567         request = self.generate_mozilla_jwt_request( | 566         request = self.generate_mozilla_jwt_request( | 
| 568             iss, secret, url, 'GET', | 567             iss, secret, url, 'GET', | 
| 569         ) | 568         ) | 
| 570         response = json.load(urllib2.urlopen(request)) | 569         response = json.load(urllib2.urlopen(request)) | 
| 571 | 570 | 
| 572         filename = '{}-{}.xpi'.format(self.basename, version) | 571         filename = '{}-{}.xpi'.format(self.basename, version) | 
| 573         self.path = os.path.join( | 572         self.path = os.path.join( | 
| 574             config.get('extensions', 'nightliesDirectory'), | 573             config.get('extensions', 'nightliesDirectory'), | 
| 575             self.basename, | 574             self.basename, | 
| 576             filename | 575             filename, | 
| 577         ) | 576         ) | 
| 578 | 577 | 
| 579         necessary = ['passed_review', 'reviewed', 'processed', 'valid'] | 578         necessary = ['passed_review', 'reviewed', 'processed', 'valid'] | 
| 580         if all(response[x] for x in necessary): | 579         if all(response[x] for x in necessary): | 
| 581             download_url = response['files'][0]['download_url'] | 580             download_url = response['files'][0]['download_url'] | 
| 582             checksum = response['files'][0]['hash'] | 581             checksum = response['files'][0]['hash'] | 
| 583 | 582 | 
| 584             request = self.generate_mozilla_jwt_request( | 583             request = self.generate_mozilla_jwt_request( | 
| 585                 iss, secret, download_url, 'GET', | 584                 iss, secret, download_url, 'GET', | 
| 586             ) | 585             ) | 
| (...skipping 10 matching lines...) Expand all  Loading... | 
| 597             if returned_checksum != checksum: | 596             if returned_checksum != checksum: | 
| 598                 logging.error('Checksum could not be verified: {} vs {}' | 597                 logging.error('Checksum could not be verified: {} vs {}' | 
| 599                               ''.format(checksum, returned_checksum)) | 598                               ''.format(checksum, returned_checksum)) | 
| 600 | 599 | 
| 601             with open(self.path, 'w') as fp: | 600             with open(self.path, 'w') as fp: | 
| 602                 fp.write(file_content) | 601                 fp.write(file_content) | 
| 603 | 602 | 
| 604             self.update_link = os.path.join( | 603             self.update_link = os.path.join( | 
| 605                 config.get('extensions', 'nightliesURL'), | 604                 config.get('extensions', 'nightliesURL'), | 
| 606                 self.basename, | 605                 self.basename, | 
| 607                 filename | 606                 filename, | 
| 608             ) | 607             ) | 
| 609 | 608 | 
| 610             self.remove_from_downloads_lockfile(self.config.type, | 609             self.remove_from_downloads_lockfile(self.config.type, | 
| 611                                                 'version', | 610                                                 'version', | 
| 612                                                 version) | 611                                                 version) | 
| 613         elif not response['passed_review'] or not response['valid']: | 612         elif not response['passed_review'] or not response['valid']: | 
| 614             # When the review failed for any reason, we want to know about it | 613             # When the review failed for any reason, we want to know about it | 
| 615             logging.error(json.dumps(response, indent=4)) | 614             logging.error(json.dumps(response, indent=4)) | 
| 616             self.remove_from_downloads_lockfile(self.config.type, | 615             self.remove_from_downloads_lockfile(self.config.type, | 
| 617                                                 'version', | 616                                                 'version', | 
| 618                                                 version) | 617                                                 version) | 
| 619 | 618 | 
| 620     def uploadToChromeWebStore(self): | 619     def uploadToChromeWebStore(self): | 
| 621 | 620 | 
| 622         opener = urllib2.build_opener(HTTPErrorBodyHandler) | 621         opener = urllib2.build_opener(HTTPErrorBodyHandler) | 
| 623 | 622 | 
| 624         # use refresh token to obtain a valid access token | 623         # use refresh token to obtain a valid access token | 
| 625         # https://developers.google.com/accounts/docs/OAuth2WebServer#refresh | 624         # https://developers.google.com/accounts/docs/OAuth2WebServer#refresh | 
| 626 | 625 | 
| 627         response = json.load(opener.open( | 626         response = json.load(opener.open( | 
| 628             'https://accounts.google.com/o/oauth2/token', | 627             'https://accounts.google.com/o/oauth2/token', | 
| 629 | 628 | 
| 630             urlencode([ | 629             urlencode([ | 
| 631                 ('refresh_token', self.config.refreshToken), | 630                 ('refresh_token', self.config.refreshToken), | 
| 632                 ('client_id', self.config.clientID), | 631                 ('client_id', self.config.clientID), | 
| 633                 ('client_secret', self.config.clientSecret), | 632                 ('client_secret', self.config.clientSecret), | 
| 634                 ('grant_type', 'refresh_token'), | 633                 ('grant_type', 'refresh_token'), | 
| 635             ]) | 634             ]), | 
| 636         )) | 635         )) | 
| 637 | 636 | 
| 638         auth_token = '%s %s' % (response['token_type'], response['access_token']
     ) | 637         auth_token = '%s %s' % (response['token_type'], response['access_token']
     ) | 
| 639 | 638 | 
| 640         # upload a new version with the Chrome Web Store API | 639         # upload a new version with the Chrome Web Store API | 
| 641         # https://developer.chrome.com/webstore/using_webstore_api#uploadexisitn
     g | 640         # https://developer.chrome.com/webstore/using_webstore_api#uploadexisitn
     g | 
| 642 | 641 | 
| 643         request = urllib2.Request('https://www.googleapis.com/upload/chromewebst
     ore/v1.1/items/' + self.config.devbuildGalleryID) | 642         request = urllib2.Request('https://www.googleapis.com/upload/chromewebst
     ore/v1.1/items/' + self.config.devbuildGalleryID) | 
| 644         request.get_method = lambda: 'PUT' | 643         request.get_method = lambda: 'PUT' | 
| 645         request.add_header('Authorization', auth_token) | 644         request.add_header('Authorization', auth_token) | 
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 696             'client_assertion': signed, | 695             'client_assertion': signed, | 
| 697         } | 696         } | 
| 698 | 697 | 
| 699         request = urllib2.Request(url, urlencode(oauth_params)) | 698         request = urllib2.Request(url, urlencode(oauth_params)) | 
| 700         request.get_method = lambda: 'POST' | 699         request.get_method = lambda: 'POST' | 
| 701 | 700 | 
| 702         return request | 701         return request | 
| 703 | 702 | 
| 704     def get_windows_store_access_token(self): | 703     def get_windows_store_access_token(self): | 
| 705         # use client certificate to obtain a valid access token | 704         # use client certificate to obtain a valid access token | 
| 706         url = 'https://login.microsoftonline.com/{}/oauth2/token'.format( | 705         url_template = 'https://login.microsoftonline.com/{}/oauth2/token' | 
| 707             self.config.tenantID | 706         url = url_template.format(self.config.tenantID) | 
| 708         ) |  | 
| 709 | 707 | 
| 710         with open(self.config.privateKey, 'r') as fp: | 708         with open(self.config.privateKey, 'r') as fp: | 
| 711             private_key = fp.read() | 709             private_key = fp.read() | 
| 712 | 710 | 
| 713         opener = urllib2.build_opener(HTTPErrorBodyHandler) | 711         opener = urllib2.build_opener(HTTPErrorBodyHandler) | 
| 714         request = self.generate_certificate_token_request(url, private_key) | 712         request = self.generate_certificate_token_request(url, private_key) | 
| 715 | 713 | 
| 716         with contextlib.closing(opener.open(request)) as response: | 714         with contextlib.closing(opener.open(request)) as response: | 
| 717             data = json.load(response) | 715             data = json.load(response) | 
| 718             auth_token = '{0[token_type]} {0[access_token]}'.format(data) | 716             auth_token = '{0[token_type]} {0[access_token]}'.format(data) | 
| (...skipping 25 matching lines...) Expand all  Loading... | 
| 744     def upload_to_windows_store(self): | 742     def upload_to_windows_store(self): | 
| 745         opener = urllib2.build_opener(HTTPErrorBodyHandler) | 743         opener = urllib2.build_opener(HTTPErrorBodyHandler) | 
| 746 | 744 | 
| 747         headers = {'Authorization': self.get_windows_store_access_token(), | 745         headers = {'Authorization': self.get_windows_store_access_token(), | 
| 748                    'Content-type': 'application/json'} | 746                    'Content-type': 'application/json'} | 
| 749 | 747 | 
| 750         # Get application | 748         # Get application | 
| 751         # https://docs.microsoft.com/en-us/windows/uwp/monetize/get-an-app | 749         # https://docs.microsoft.com/en-us/windows/uwp/monetize/get-an-app | 
| 752         api_path = '{}/v1.0/my/applications/{}'.format( | 750         api_path = '{}/v1.0/my/applications/{}'.format( | 
| 753             'https://manage.devcenter.microsoft.com', | 751             'https://manage.devcenter.microsoft.com', | 
| 754             self.config.devbuildGalleryID | 752             self.config.devbuildGalleryID, | 
| 755         ) | 753         ) | 
| 756 | 754 | 
| 757         request = urllib2.Request(api_path, None, headers) | 755         request = urllib2.Request(api_path, None, headers) | 
| 758         with contextlib.closing(opener.open(request)) as response: | 756         with contextlib.closing(opener.open(request)) as response: | 
| 759             app_obj = json.load(response) | 757             app_obj = json.load(response) | 
| 760 | 758 | 
| 761         # Delete existing in-progress submission | 759         # Delete existing in-progress submission | 
| 762         # https://docs.microsoft.com/en-us/windows/uwp/monetize/delete-an-app-su
     bmission | 760         # https://docs.microsoft.com/en-us/windows/uwp/monetize/delete-an-app-su
     bmission | 
| 763         submissions_path = api_path + '/submissions' | 761         submissions_path = api_path + '/submissions' | 
| 764         if 'pendingApplicationSubmission' in app_obj: | 762         if 'pendingApplicationSubmission' in app_obj: | 
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 891                         # write update manifest | 889                         # write update manifest | 
| 892                         self.writeUpdateManifest() | 890                         self.writeUpdateManifest() | 
| 893 | 891 | 
| 894                         # retire old builds | 892                         # retire old builds | 
| 895                         versions = self.retireBuilds() | 893                         versions = self.retireBuilds() | 
| 896                         # update index page | 894                         # update index page | 
| 897                         self.updateIndex(versions) | 895                         self.updateIndex(versions) | 
| 898 | 896 | 
| 899                         # Update soft link to latest build | 897                         # Update soft link to latest build | 
| 900                         baseDir = os.path.join( | 898                         baseDir = os.path.join( | 
| 901                             self.config.nightliesDirectory, self.basename | 899                             self.config.nightliesDirectory, self.basename, | 
| 902                         ) | 900                         ) | 
| 903                         linkPath = os.path.join( | 901                         linkPath = os.path.join( | 
| 904                             baseDir, '00latest' + self.config.packageSuffix | 902                             baseDir, '00latest' + self.config.packageSuffix, | 
| 905                         ) | 903                         ) | 
| 906 | 904 | 
| 907                         self.symlink_or_copy(self.path, linkPath) | 905                         self.symlink_or_copy(self.path, linkPath) | 
| 908             finally: | 906             finally: | 
| 909                 # clean up | 907                 # clean up | 
| 910                 if self.tempdir: | 908                 if self.tempdir: | 
| 911                     shutil.rmtree(self.tempdir, ignore_errors=True) | 909                     shutil.rmtree(self.tempdir, ignore_errors=True) | 
| 912 | 910 | 
| 913 | 911 | 
| 914 def main(download=False): | 912 def main(download=False): | 
| (...skipping 23 matching lines...) Expand all  Loading... | 
| 938 | 936 | 
| 939     file = open(nightlyConfigFile, 'wb') | 937     file = open(nightlyConfigFile, 'wb') | 
| 940     nightlyConfig.write(file) | 938     nightlyConfig.write(file) | 
| 941 | 939 | 
| 942 | 940 | 
| 943 if __name__ == '__main__': | 941 if __name__ == '__main__': | 
| 944     parser = argparse.ArgumentParser() | 942     parser = argparse.ArgumentParser() | 
| 945     parser.add_argument('--download', action='store_true', default=False) | 943     parser.add_argument('--download', action='store_true', default=False) | 
| 946     args = parser.parse_args() | 944     args = parser.parse_args() | 
| 947     main(args.download) | 945     main(args.download) | 
| LEFT | RIGHT | 
|---|