| OLD | NEW |
| 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 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 42 from urllib import urlencode | 42 from urllib import urlencode |
| 43 import urllib2 | 43 import urllib2 |
| 44 import urlparse | 44 import urlparse |
| 45 import zipfile | 45 import zipfile |
| 46 import contextlib | 46 import contextlib |
| 47 | 47 |
| 48 from xml.dom.minidom import parse as parseXml | 48 from xml.dom.minidom import parse as parseXml |
| 49 | 49 |
| 50 from sitescripts.extensions.utils import ( | 50 from sitescripts.extensions.utils import ( |
| 51 compareVersions, Configuration, | 51 compareVersions, Configuration, |
| 52 writeAndroidUpdateManifest | 52 writeAndroidUpdateManifest, |
| 53 ) | 53 ) |
| 54 from sitescripts.utils import get_config, get_template | 54 from sitescripts.utils import get_config, get_template |
| 55 | 55 |
| 56 MAX_BUILDS = 50 | 56 MAX_BUILDS = 50 |
| 57 | 57 |
| 58 | 58 |
| 59 # Google and Microsoft APIs use HTTP error codes with error message in | 59 # Google and Microsoft APIs use HTTP error codes with error message in |
| 60 # body. So we add the response body to the HTTPError to get more | 60 # body. So we add the response body to the HTTPError to get more |
| 61 # meaningful error messages. | 61 # meaningful error messages. |
| 62 class HTTPErrorBodyHandler(urllib2.HTTPDefaultErrorHandler): | 62 class HTTPErrorBodyHandler(urllib2.HTTPDefaultErrorHandler): |
| (...skipping 28 matching lines...) Expand all Loading... |
| 91 | 91 |
| 92 def hasChanges(self): | 92 def hasChanges(self): |
| 93 return self.revision != self.previousRevision | 93 return self.revision != self.previousRevision |
| 94 | 94 |
| 95 def getCurrentRevision(self): | 95 def getCurrentRevision(self): |
| 96 """ | 96 """ |
| 97 retrieves the current revision ID from the repository | 97 retrieves the current revision ID from the repository |
| 98 """ | 98 """ |
| 99 command = [ | 99 command = [ |
| 100 'hg', 'id', '-i', '-r', self.config.revision, '--config', | 100 'hg', 'id', '-i', '-r', self.config.revision, '--config', |
| 101 'defaults.id=', self.config.repository | 101 'defaults.id=', self.config.repository, |
| 102 ] | 102 ] |
| 103 return subprocess.check_output(command).strip() | 103 return subprocess.check_output(command).strip() |
| 104 | 104 |
| 105 def getCurrentBuild(self): | 105 def getCurrentBuild(self): |
| 106 """ | 106 """ |
| 107 calculates the (typically numerical) build ID for the current build | 107 calculates the (typically numerical) build ID for the current build |
| 108 """ | 108 """ |
| 109 command = ['hg', 'id', '-n', '--config', 'defaults.id=', self.tempdir] | 109 command = ['hg', 'id', '-n', '--config', 'defaults.id=', self.tempdir] |
| 110 build = subprocess.check_output(command).strip() | 110 build = subprocess.check_output(command).strip() |
| 111 return build | 111 return build |
| 112 | 112 |
| 113 def getChanges(self): | 113 def getChanges(self): |
| 114 """ | 114 """ |
| 115 retrieve changes between the current and previous ("first") revision | 115 retrieve changes between the current and previous ("first") revision |
| 116 """ | 116 """ |
| 117 command = [ | 117 command = [ |
| 118 'hg', 'log', '-R', self.tempdir, '-r', | 118 'hg', 'log', '-R', self.tempdir, '-r', |
| 119 'reverse(ancestors({}))'.format(self.config.revision), '-l', '50', | 119 'reverse(ancestors({}))'.format(self.config.revision), '-l', '50', |
| 120 '--encoding', 'utf-8', '--template', | 120 '--encoding', 'utf-8', '--template', |
| 121 '{date|isodate}\\0{author|person}\\0{rev}\\0{desc}\\0\\0', | 121 '{date|isodate}\\0{author|person}\\0{rev}\\0{desc}\\0\\0', |
| 122 '--config', 'defaults.log=' | 122 '--config', 'defaults.log=', |
| 123 ] | 123 ] |
| 124 result = subprocess.check_output(command).decode('utf-8') | 124 result = subprocess.check_output(command).decode('utf-8') |
| 125 | 125 |
| 126 for change in result.split('\x00\x00'): | 126 for change in result.split('\x00\x00'): |
| 127 if change: | 127 if change: |
| 128 date, author, revision, description = change.split('\x00') | 128 date, author, revision, description = change.split('\x00') |
| 129 yield {'date': date, 'author': author, 'revision': revision, 'de
scription': description} | 129 yield {'date': date, 'author': author, 'revision': revision, 'de
scription': description} |
| 130 | 130 |
| 131 def copyRepository(self): | 131 def copyRepository(self): |
| 132 """ | 132 """ |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 274 os.makedirs(baseDir) | 274 os.makedirs(baseDir) |
| 275 | 275 |
| 276 # ABP for Android used to have its own update manifest format. We need t
o | 276 # ABP for Android used to have its own update manifest format. We need t
o |
| 277 # generate both that and the new one in the libadblockplus format as lon
g | 277 # generate both that and the new one in the libadblockplus format as lon
g |
| 278 # as a significant amount of users is on an old version. | 278 # as a significant amount of users is on an old version. |
| 279 if self.config.type == 'android': | 279 if self.config.type == 'android': |
| 280 newManifestPath = os.path.join(baseDir, 'update.json') | 280 newManifestPath = os.path.join(baseDir, 'update.json') |
| 281 writeAndroidUpdateManifest(newManifestPath, [{ | 281 writeAndroidUpdateManifest(newManifestPath, [{ |
| 282 'basename': self.basename, | 282 'basename': self.basename, |
| 283 'version': self.version, | 283 'version': self.version, |
| 284 'updateURL': self.updateURL | 284 'updateURL': self.updateURL, |
| 285 }]) | 285 }]) |
| 286 | 286 |
| 287 template = get_template(get_config().get('extensions', templateName), | 287 template = get_template(get_config().get('extensions', templateName), |
| 288 autoescape=autoescape) | 288 autoescape=autoescape) |
| 289 template.stream({'extensions': [self]}).dump(manifestPath) | 289 template.stream({'extensions': [self]}).dump(manifestPath) |
| 290 | 290 |
| 291 def writeIEUpdateManifest(self, versions): | 291 def writeIEUpdateManifest(self, versions): |
| 292 """ | 292 """ |
| 293 Writes update.json file for the latest IE build | 293 Writes update.json file for the latest IE build |
| 294 """ | 294 """ |
| 295 if len(versions) == 0: | 295 if len(versions) == 0: |
| 296 return | 296 return |
| 297 | 297 |
| 298 version = versions[0] | 298 version = versions[0] |
| 299 packageName = self.basename + '-' + version + self.config.packageSuffix | 299 packageName = self.basename + '-' + version + self.config.packageSuffix |
| 300 updateURL = urlparse.urljoin(self.config.nightliesURL, self.basename + '
/' + packageName + '?update') | 300 updateURL = urlparse.urljoin(self.config.nightliesURL, self.basename + '
/' + packageName + '?update') |
| 301 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) | 301 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) |
| 302 manifestPath = os.path.join(baseDir, 'update.json') | 302 manifestPath = os.path.join(baseDir, 'update.json') |
| 303 | 303 |
| 304 from sitescripts.extensions.utils import writeIEUpdateManifest as doWrit
e | 304 from sitescripts.extensions.utils import writeIEUpdateManifest as doWrit
e |
| 305 doWrite(manifestPath, [{ | 305 doWrite(manifestPath, [{ |
| 306 'basename': self.basename, | 306 'basename': self.basename, |
| 307 'version': version, | 307 'version': version, |
| 308 'updateURL': updateURL | 308 'updateURL': updateURL, |
| 309 }]) | 309 }]) |
| 310 | 310 |
| 311 for suffix in ['-x86.msi', '-x64.msi', '-gpo-x86.msi', '-gpo-x64.msi']: | 311 for suffix in ['-x86.msi', '-x64.msi', '-gpo-x86.msi', '-gpo-x64.msi']: |
| 312 linkPath = os.path.join(baseDir, '00latest%s' % suffix) | 312 linkPath = os.path.join(baseDir, '00latest%s' % suffix) |
| 313 outputPath = os.path.join(baseDir, self.basename + '-' + version + s
uffix) | 313 outputPath = os.path.join(baseDir, self.basename + '-' + version + s
uffix) |
| 314 self.symlink_or_copy(outputPath, linkPath) | 314 self.symlink_or_copy(outputPath, linkPath) |
| 315 | 315 |
| 316 def build(self): | 316 def build(self): |
| 317 """ | 317 """ |
| 318 run the build command in the tempdir | 318 run the build command in the tempdir |
| 319 """ | 319 """ |
| 320 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) | 320 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) |
| 321 if not os.path.exists(baseDir): | 321 if not os.path.exists(baseDir): |
| 322 os.makedirs(baseDir) | 322 os.makedirs(baseDir) |
| 323 outputFile = '%s-%s%s' % (self.basename, self.version, self.config.packa
geSuffix) | 323 outputFile = '%s-%s%s' % (self.basename, self.version, self.config.packa
geSuffix) |
| 324 self.path = os.path.join(baseDir, outputFile) | 324 self.path = os.path.join(baseDir, outputFile) |
| 325 self.updateURL = urlparse.urljoin(self.config.nightliesURL, self.basenam
e + '/' + outputFile + '?update') | 325 self.updateURL = urlparse.urljoin(self.config.nightliesURL, self.basenam
e + '/' + outputFile + '?update') |
| 326 | 326 |
| 327 if self.config.type == 'android': | 327 if self.config.type == 'android': |
| 328 apkFile = open(self.path, 'wb') | 328 apkFile = open(self.path, 'wb') |
| 329 | 329 |
| 330 try: | 330 try: |
| 331 try: | 331 try: |
| 332 port = get_config().get('extensions', 'androidBuildPort') | 332 port = get_config().get('extensions', 'androidBuildPort') |
| 333 except ConfigParser.NoOptionError: | 333 except ConfigParser.NoOptionError: |
| 334 port = '22' | 334 port = '22' |
| 335 command = ['ssh', '-p', port, get_config().get('extensions', 'an
droidBuildHost')] | 335 command = ['ssh', '-p', port, get_config().get('extensions', 'an
droidBuildHost')] |
| 336 command.extend(map(pipes.quote, [ | 336 command.extend(map(pipes.quote, [ |
| 337 '/home/android/bin/makedebugbuild.py', '--revision', | 337 '/home/android/bin/makedebugbuild.py', '--revision', |
| 338 self.buildNum, '--version', self.version, '--stdout' | 338 self.buildNum, '--version', self.version, '--stdout', |
| 339 ])) | 339 ])) |
| 340 subprocess.check_call(command, stdout=apkFile, close_fds=True) | 340 subprocess.check_call(command, stdout=apkFile, close_fds=True) |
| 341 except: | 341 except: |
| 342 # clear broken output if any | 342 # clear broken output if any |
| 343 if os.path.exists(self.path): | 343 if os.path.exists(self.path): |
| 344 os.remove(self.path) | 344 os.remove(self.path) |
| 345 raise | 345 raise |
| 346 else: | 346 else: |
| 347 env = os.environ | 347 env = os.environ |
| 348 spiderMonkeyBinary = self.config.spiderMonkeyBinary | 348 spiderMonkeyBinary = self.config.spiderMonkeyBinary |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 402 packageFile = self.basename + '-' + version + self.config.packageSuf
fix | 402 packageFile = self.basename + '-' + version + self.config.packageSuf
fix |
| 403 changelogFile = self.basename + '-' + version + '.changelog.xhtml' | 403 changelogFile = self.basename + '-' + version + '.changelog.xhtml' |
| 404 if not os.path.exists(os.path.join(baseDir, packageFile)): | 404 if not os.path.exists(os.path.join(baseDir, packageFile)): |
| 405 # Oops | 405 # Oops |
| 406 continue | 406 continue |
| 407 | 407 |
| 408 link = { | 408 link = { |
| 409 'version': version, | 409 'version': version, |
| 410 'download': packageFile, | 410 'download': packageFile, |
| 411 'mtime': os.path.getmtime(os.path.join(baseDir, packageFile)), | 411 'mtime': os.path.getmtime(os.path.join(baseDir, packageFile)), |
| 412 'size': os.path.getsize(os.path.join(baseDir, packageFile)) | 412 'size': os.path.getsize(os.path.join(baseDir, packageFile)), |
| 413 } | 413 } |
| 414 if os.path.exists(os.path.join(baseDir, changelogFile)): | 414 if os.path.exists(os.path.join(baseDir, changelogFile)): |
| 415 link['changelog'] = changelogFile | 415 link['changelog'] = changelogFile |
| 416 links.append(link) | 416 links.append(link) |
| 417 template = get_template(get_config().get('extensions', 'nightlyIndexPage
')) | 417 template = get_template(get_config().get('extensions', 'nightlyIndexPage
')) |
| 418 template.stream({'config': self.config, 'links': links}).dump(outputPath
) | 418 template.stream({'config': self.config, 'links': links}).dump(outputPath
) |
| 419 | 419 |
| 420 def read_downloads_lockfile(self): | 420 def read_downloads_lockfile(self): |
| 421 path = get_config().get('extensions', 'downloadLockFile') | 421 path = get_config().get('extensions', 'downloadLockFile') |
| 422 try: | 422 try: |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 464 issued = int(time.time()) | 464 issued = int(time.time()) |
| 465 payload = { | 465 payload = { |
| 466 'iss': issuer, | 466 'iss': issuer, |
| 467 'jti': random.random(), | 467 'jti': random.random(), |
| 468 'iat': issued, | 468 'iat': issued, |
| 469 'exp': issued + 60, | 469 'exp': issued + 60, |
| 470 } | 470 } |
| 471 | 471 |
| 472 hmac_data = '{}.{}'.format( | 472 hmac_data = '{}.{}'.format( |
| 473 base64.b64encode(json.dumps(header)), | 473 base64.b64encode(json.dumps(header)), |
| 474 base64.b64encode(json.dumps(payload)) | 474 base64.b64encode(json.dumps(payload)), |
| 475 ) | 475 ) |
| 476 | 476 |
| 477 signature = hmac.new(secret, msg=hmac_data, | 477 signature = hmac.new(secret, msg=hmac_data, |
| 478 digestmod=hashlib.sha256).digest() | 478 digestmod=hashlib.sha256).digest() |
| 479 token = '{}.{}'.format(hmac_data, base64.b64encode(signature)) | 479 token = '{}.{}'.format(hmac_data, base64.b64encode(signature)) |
| 480 | 480 |
| 481 request = urllib2.Request(url, data) | 481 request = urllib2.Request(url, data) |
| 482 request.add_header('Authorization', 'JWT ' + token) | 482 request.add_header('Authorization', 'JWT ' + token) |
| 483 for header in add_headers: | 483 for header in add_headers: |
| 484 request.add_header(*header) | 484 request.add_header(*header) |
| 485 request.get_method = lambda: method | 485 request.get_method = lambda: method |
| 486 | 486 |
| 487 return request | 487 return request |
| 488 | 488 |
| 489 def uploadToMozillaAddons(self): | 489 def uploadToMozillaAddons(self): |
| 490 import urllib3 | 490 import urllib3 |
| 491 | 491 |
| 492 config = get_config() | 492 config = get_config() |
| 493 | 493 |
| 494 upload_url = ('https://addons.mozilla.org/api/v3/addons/{}/' | 494 upload_url = ('https://addons.mozilla.org/api/v3/addons/{}/' |
| 495 'versions/{}/').format(self.extensionID, self.version) | 495 'versions/{}/').format(self.extensionID, self.version) |
| 496 | 496 |
| 497 with open(self.path, 'rb') as file: | 497 with open(self.path, 'rb') as file: |
| 498 data, content_type = urllib3.filepost.encode_multipart_formdata({ | 498 data, content_type = urllib3.filepost.encode_multipart_formdata({ |
| 499 'upload': ( | 499 'upload': ( |
| 500 os.path.basename(self.path), | 500 os.path.basename(self.path), |
| 501 file.read(), | 501 file.read(), |
| 502 'application/x-xpinstall' | 502 'application/x-xpinstall', |
| 503 ) | 503 ), |
| 504 }) | 504 }) |
| 505 | 505 |
| 506 request = self.generate_jwt_request( | 506 request = self.generate_jwt_request( |
| 507 config.get('extensions', 'amo_key'), | 507 config.get('extensions', 'amo_key'), |
| 508 config.get('extensions', 'amo_secret'), | 508 config.get('extensions', 'amo_secret'), |
| 509 upload_url, | 509 upload_url, |
| 510 'PUT', | 510 'PUT', |
| 511 data, | 511 data, |
| 512 [('Content-Type', content_type)] | 512 [('Content-Type', content_type)], |
| 513 ) | 513 ) |
| 514 | 514 |
| 515 try: | 515 try: |
| 516 urllib2.urlopen(request).close() | 516 urllib2.urlopen(request).close() |
| 517 except urllib2.HTTPError as e: | 517 except urllib2.HTTPError as e: |
| 518 try: | 518 try: |
| 519 logging.error(e.read()) | 519 logging.error(e.read()) |
| 520 finally: | 520 finally: |
| 521 e.close() | 521 e.close() |
| 522 raise | 522 raise |
| 523 | 523 |
| 524 self.add_to_downloads_lockfile( | 524 self.add_to_downloads_lockfile( |
| 525 self.config.type, | 525 self.config.type, |
| 526 { | 526 { |
| 527 'buildtype': 'devbuild', | 527 'buildtype': 'devbuild', |
| 528 'app_id': self.extensionID, | 528 'app_id': self.extensionID, |
| 529 'version': self.version, | 529 'version': self.version, |
| 530 } | 530 }, |
| 531 ) | 531 ) |
| 532 os.remove(self.path) | 532 os.remove(self.path) |
| 533 | 533 |
| 534 def download_from_mozilla_addons(self, buildtype, version, app_id): | 534 def download_from_mozilla_addons(self, buildtype, version, app_id): |
| 535 config = get_config() | 535 config = get_config() |
| 536 iss = config.get('extensions', 'amo_key') | 536 iss = config.get('extensions', 'amo_key') |
| 537 secret = config.get('extensions', 'amo_secret') | 537 secret = config.get('extensions', 'amo_secret') |
| 538 | 538 |
| 539 url = ('https://addons.mozilla.org/api/v3/addons/{}/' | 539 url = ('https://addons.mozilla.org/api/v3/addons/{}/' |
| 540 'versions/{}/').format(app_id, version) | 540 'versions/{}/').format(app_id, version) |
| 541 | 541 |
| 542 request = self.generate_jwt_request(iss, secret, url, 'GET') | 542 request = self.generate_jwt_request(iss, secret, url, 'GET') |
| 543 response = json.load(urllib2.urlopen(request)) | 543 response = json.load(urllib2.urlopen(request)) |
| 544 | 544 |
| 545 filename = '{}-{}.xpi'.format(self.basename, version) | 545 filename = '{}-{}.xpi'.format(self.basename, version) |
| 546 self.path = os.path.join( | 546 self.path = os.path.join( |
| 547 config.get('extensions', 'nightliesDirectory'), | 547 config.get('extensions', 'nightliesDirectory'), |
| 548 self.basename, | 548 self.basename, |
| 549 filename | 549 filename, |
| 550 ) | 550 ) |
| 551 | 551 |
| 552 necessary = ['passed_review', 'reviewed', 'processed', 'valid'] | 552 necessary = ['passed_review', 'reviewed', 'processed', 'valid'] |
| 553 if all(response[x] for x in necessary): | 553 if all(response[x] for x in necessary): |
| 554 download_url = response['files'][0]['download_url'] | 554 download_url = response['files'][0]['download_url'] |
| 555 checksum = response['files'][0]['hash'] | 555 checksum = response['files'][0]['hash'] |
| 556 | 556 |
| 557 request = self.generate_jwt_request(iss, secret, download_url, | 557 request = self.generate_jwt_request(iss, secret, download_url, |
| 558 'GET') | 558 'GET') |
| 559 try: | 559 try: |
| 560 response = urllib2.urlopen(request) | 560 response = urllib2.urlopen(request) |
| 561 except urllib2.HTTPError as e: | 561 except urllib2.HTTPError as e: |
| 562 logging.error(e.read()) | 562 logging.error(e.read()) |
| 563 | 563 |
| 564 # Verify the extension's integrity | 564 # Verify the extension's integrity |
| 565 file_content = response.read() | 565 file_content = response.read() |
| 566 sha256 = hashlib.sha256(file_content) | 566 sha256 = hashlib.sha256(file_content) |
| 567 returned_checksum = '{}:{}'.format(sha256.name, sha256.hexdigest()) | 567 returned_checksum = '{}:{}'.format(sha256.name, sha256.hexdigest()) |
| 568 | 568 |
| 569 if returned_checksum != checksum: | 569 if returned_checksum != checksum: |
| 570 logging.error('Checksum could not be verified: {} vs {}' | 570 logging.error('Checksum could not be verified: {} vs {}' |
| 571 ''.format(checksum, returned_checksum)) | 571 ''.format(checksum, returned_checksum)) |
| 572 | 572 |
| 573 with open(self.path, 'w') as fp: | 573 with open(self.path, 'w') as fp: |
| 574 fp.write(file_content) | 574 fp.write(file_content) |
| 575 | 575 |
| 576 self.update_link = os.path.join( | 576 self.update_link = os.path.join( |
| 577 config.get('extensions', 'nightliesURL'), | 577 config.get('extensions', 'nightliesURL'), |
| 578 self.basename, | 578 self.basename, |
| 579 filename | 579 filename, |
| 580 ) | 580 ) |
| 581 | 581 |
| 582 self.remove_from_downloads_lockfile(self.config.type, | 582 self.remove_from_downloads_lockfile(self.config.type, |
| 583 'version', | 583 'version', |
| 584 version) | 584 version) |
| 585 elif not response['passed_review'] or not response['valid']: | 585 elif not response['passed_review'] or not response['valid']: |
| 586 # When the review failed for any reason, we want to know about it | 586 # When the review failed for any reason, we want to know about it |
| 587 logging.error(json.dumps(response, indent=4)) | 587 logging.error(json.dumps(response, indent=4)) |
| 588 self.remove_from_downloads_lockfile(self.config.type, | 588 self.remove_from_downloads_lockfile(self.config.type, |
| 589 'version', | 589 'version', |
| 590 version) | 590 version) |
| 591 | 591 |
| 592 def uploadToChromeWebStore(self): | 592 def uploadToChromeWebStore(self): |
| 593 | 593 |
| 594 opener = urllib2.build_opener(HTTPErrorBodyHandler) | 594 opener = urllib2.build_opener(HTTPErrorBodyHandler) |
| 595 | 595 |
| 596 # use refresh token to obtain a valid access token | 596 # use refresh token to obtain a valid access token |
| 597 # https://developers.google.com/accounts/docs/OAuth2WebServer#refresh | 597 # https://developers.google.com/accounts/docs/OAuth2WebServer#refresh |
| 598 | 598 |
| 599 response = json.load(opener.open( | 599 response = json.load(opener.open( |
| 600 'https://accounts.google.com/o/oauth2/token', | 600 'https://accounts.google.com/o/oauth2/token', |
| 601 | 601 |
| 602 urlencode([ | 602 urlencode([ |
| 603 ('refresh_token', self.config.refreshToken), | 603 ('refresh_token', self.config.refreshToken), |
| 604 ('client_id', self.config.clientID), | 604 ('client_id', self.config.clientID), |
| 605 ('client_secret', self.config.clientSecret), | 605 ('client_secret', self.config.clientSecret), |
| 606 ('grant_type', 'refresh_token'), | 606 ('grant_type', 'refresh_token'), |
| 607 ]) | 607 ]), |
| 608 )) | 608 )) |
| 609 | 609 |
| 610 auth_token = '%s %s' % (response['token_type'], response['access_token']
) | 610 auth_token = '%s %s' % (response['token_type'], response['access_token']
) |
| 611 | 611 |
| 612 # upload a new version with the Chrome Web Store API | 612 # upload a new version with the Chrome Web Store API |
| 613 # https://developer.chrome.com/webstore/using_webstore_api#uploadexisitn
g | 613 # https://developer.chrome.com/webstore/using_webstore_api#uploadexisitn
g |
| 614 | 614 |
| 615 request = urllib2.Request('https://www.googleapis.com/upload/chromewebst
ore/v1.1/items/' + self.config.devbuildGalleryID) | 615 request = urllib2.Request('https://www.googleapis.com/upload/chromewebst
ore/v1.1/items/' + self.config.devbuildGalleryID) |
| 616 request.get_method = lambda: 'PUT' | 616 request.get_method = lambda: 'PUT' |
| 617 request.add_header('Authorization', auth_token) | 617 request.add_header('Authorization', auth_token) |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 651 # https://docs.microsoft.com/en-us/azure/active-directory/active-directo
ry-protocols-oauth-code#refreshing-the-access-tokens | 651 # https://docs.microsoft.com/en-us/azure/active-directory/active-directo
ry-protocols-oauth-code#refreshing-the-access-tokens |
| 652 server = 'https://login.microsoftonline.com' | 652 server = 'https://login.microsoftonline.com' |
| 653 token_path = '{}/{}/oauth2/token'.format(server, self.config.tenantID) | 653 token_path = '{}/{}/oauth2/token'.format(server, self.config.tenantID) |
| 654 | 654 |
| 655 opener = urllib2.build_opener(HTTPErrorBodyHandler) | 655 opener = urllib2.build_opener(HTTPErrorBodyHandler) |
| 656 post_data = urlencode([ | 656 post_data = urlencode([ |
| 657 ('refresh_token', self.config.refreshToken), | 657 ('refresh_token', self.config.refreshToken), |
| 658 ('client_id', self.config.clientID), | 658 ('client_id', self.config.clientID), |
| 659 ('client_secret', self.config.clientSecret), | 659 ('client_secret', self.config.clientSecret), |
| 660 ('grant_type', 'refresh_token'), | 660 ('grant_type', 'refresh_token'), |
| 661 ('resource', 'https://graph.windows.net') | 661 ('resource', 'https://graph.windows.net'), |
| 662 ]) | 662 ]) |
| 663 request = urllib2.Request(token_path, post_data) | 663 request = urllib2.Request(token_path, post_data) |
| 664 with contextlib.closing(opener.open(request)) as response: | 664 with contextlib.closing(opener.open(request)) as response: |
| 665 data = json.load(response) | 665 data = json.load(response) |
| 666 auth_token = '{0[token_type]} {0[access_token]}'.format(data) | 666 auth_token = '{0[token_type]} {0[access_token]}'.format(data) |
| 667 | 667 |
| 668 return auth_token | 668 return auth_token |
| 669 | 669 |
| 670 def upload_appx_file_to_windows_store(self, file_upload_url): | 670 def upload_appx_file_to_windows_store(self, file_upload_url): |
| 671 # Add .appx file to a .zip file | 671 # Add .appx file to a .zip file |
| (...skipping 20 matching lines...) Expand all Loading... |
| 692 def upload_to_windows_store(self): | 692 def upload_to_windows_store(self): |
| 693 opener = urllib2.build_opener(HTTPErrorBodyHandler) | 693 opener = urllib2.build_opener(HTTPErrorBodyHandler) |
| 694 | 694 |
| 695 headers = {'Authorization': self.get_windows_store_access_token(), | 695 headers = {'Authorization': self.get_windows_store_access_token(), |
| 696 'Content-type': 'application/json'} | 696 'Content-type': 'application/json'} |
| 697 | 697 |
| 698 # Get application | 698 # Get application |
| 699 # https://docs.microsoft.com/en-us/windows/uwp/monetize/get-an-app | 699 # https://docs.microsoft.com/en-us/windows/uwp/monetize/get-an-app |
| 700 api_path = '{}/v1.0/my/applications/{}'.format( | 700 api_path = '{}/v1.0/my/applications/{}'.format( |
| 701 'https://manage.devcenter.microsoft.com', | 701 'https://manage.devcenter.microsoft.com', |
| 702 self.config.devbuildGalleryID | 702 self.config.devbuildGalleryID, |
| 703 ) | 703 ) |
| 704 | 704 |
| 705 request = urllib2.Request(api_path, None, headers) | 705 request = urllib2.Request(api_path, None, headers) |
| 706 with contextlib.closing(opener.open(request)) as response: | 706 with contextlib.closing(opener.open(request)) as response: |
| 707 app_obj = json.load(response) | 707 app_obj = json.load(response) |
| 708 | 708 |
| 709 # Delete existing in-progress submission | 709 # Delete existing in-progress submission |
| 710 # https://docs.microsoft.com/en-us/windows/uwp/monetize/delete-an-app-su
bmission | 710 # https://docs.microsoft.com/en-us/windows/uwp/monetize/delete-an-app-su
bmission |
| 711 submissions_path = api_path + '/submissions' | 711 submissions_path = api_path + '/submissions' |
| 712 if 'pendingApplicationSubmission' in app_obj: | 712 if 'pendingApplicationSubmission' in app_obj: |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 839 # write update manifest | 839 # write update manifest |
| 840 self.writeUpdateManifest() | 840 self.writeUpdateManifest() |
| 841 | 841 |
| 842 # retire old builds | 842 # retire old builds |
| 843 versions = self.retireBuilds() | 843 versions = self.retireBuilds() |
| 844 # update index page | 844 # update index page |
| 845 self.updateIndex(versions) | 845 self.updateIndex(versions) |
| 846 | 846 |
| 847 # Update soft link to latest build | 847 # Update soft link to latest build |
| 848 baseDir = os.path.join( | 848 baseDir = os.path.join( |
| 849 self.config.nightliesDirectory, self.basename | 849 self.config.nightliesDirectory, self.basename, |
| 850 ) | 850 ) |
| 851 linkPath = os.path.join( | 851 linkPath = os.path.join( |
| 852 baseDir, '00latest' + self.config.packageSuffix | 852 baseDir, '00latest' + self.config.packageSuffix, |
| 853 ) | 853 ) |
| 854 | 854 |
| 855 self.symlink_or_copy(self.path, linkPath) | 855 self.symlink_or_copy(self.path, linkPath) |
| 856 finally: | 856 finally: |
| 857 # clean up | 857 # clean up |
| 858 if self.tempdir: | 858 if self.tempdir: |
| 859 shutil.rmtree(self.tempdir, ignore_errors=True) | 859 shutil.rmtree(self.tempdir, ignore_errors=True) |
| 860 | 860 |
| 861 | 861 |
| 862 def main(download=False): | 862 def main(download=False): |
| (...skipping 23 matching lines...) Expand all Loading... |
| 886 | 886 |
| 887 file = open(nightlyConfigFile, 'wb') | 887 file = open(nightlyConfigFile, 'wb') |
| 888 nightlyConfig.write(file) | 888 nightlyConfig.write(file) |
| 889 | 889 |
| 890 | 890 |
| 891 if __name__ == '__main__': | 891 if __name__ == '__main__': |
| 892 parser = argparse.ArgumentParser() | 892 parser = argparse.ArgumentParser() |
| 893 parser.add_argument('--download', action='store_true', default=False) | 893 parser.add_argument('--download', action='store_true', default=False) |
| 894 args = parser.parse_args() | 894 args = parser.parse_args() |
| 895 main(args.download) | 895 main(args.download) |
| OLD | NEW |