Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: sitescripts/extensions/bin/createNightlies.py

Issue 5092904657747968: Issue 356 - Update devbuild in the Chrome Web Store (Closed)
Left Patch Set: Addressed comments Created April 20, 2014, 3:58 p.m.
Right Patch Set: Addressed comments Created April 21, 2014, 8:45 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « .sitescripts.example ('k') | sitescripts/extensions/utils.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 # coding: utf-8 1 # coding: utf-8
2 2
3 # This file is part of the Adblock Plus web scripts, 3 # This file is part of the Adblock Plus web scripts,
4 # Copyright (C) 2006-2013 Eyeo GmbH 4 # Copyright (C) 2006-2013 Eyeo GmbH
5 # 5 #
6 # Adblock Plus is free software: you can redistribute it and/or modify 6 # Adblock Plus is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License version 3 as 7 # it under the terms of the GNU General Public License version 3 as
8 # published by the Free Software Foundation. 8 # published by the Free Software Foundation.
9 # 9 #
10 # Adblock Plus is distributed in the hope that it will be useful, 10 # Adblock Plus is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details. 13 # GNU General Public License for more details.
14 # 14 #
15 # You should have received a copy of the GNU General Public License 15 # You should have received a copy of the GNU General Public License
16 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 16 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
17 17
18 """ 18 """
19 19
20 Nightly builds generation script 20 Nightly builds generation script
21 ================================ 21 ================================
22 22
23 This script generates nightly builds of extensions, together 23 This script generates nightly builds of extensions, together
24 with changelogs and documentation. 24 with changelogs and documentation.
25 25
26 """ 26 """
27 27
28 import sys, os, os.path, codecs, subprocess, ConfigParser, traceback, json, hash lib 28 import sys, os, os.path, codecs, subprocess, ConfigParser, traceback, json, hash lib
29 import tempfile, re, shutil, urlparse, pipes, time, base64, urllib2 29 import tempfile, re, shutil, urlparse, pipes, time, urllib2, struct
30 from datetime import datetime 30 from datetime import datetime
31 from urllib import urlencode 31 from urllib import urlencode
32 from xml.dom.minidom import parse as parseXml 32 from xml.dom.minidom import parse as parseXml
33 from sitescripts.utils import get_config, setupStderr, get_template 33 from sitescripts.utils import get_config, setupStderr, get_template
34 from sitescripts.extensions.utils import compareVersions, Configuration 34 from sitescripts.extensions.utils import compareVersions, Configuration
35 35
36 MAX_BUILDS = 50 36 MAX_BUILDS = 50
37 37
38 38
39 class NightlyBuild(object): 39 class NightlyBuild(object):
(...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after
387 subprocess.check_call(command) 387 subprocess.check_call(command)
388 388
389 try: 389 try:
390 import buildtools.build as build 390 import buildtools.build as build
391 outputPath = os.path.join(self.config.docsDirectory, self.basename) 391 outputPath = os.path.join(self.config.docsDirectory, self.basename)
392 build.generateDocs(self.tempdir, None, [("-t", docsdir), ("-q", "")], [out putPath], self.config.type) 392 build.generateDocs(self.tempdir, None, [("-t", docsdir), ("-q", "")], [out putPath], self.config.type)
393 finally: 393 finally:
394 shutil.rmtree(docsdir, ignore_errors=True) 394 shutil.rmtree(docsdir, ignore_errors=True)
395 395
396 def uploadToChromeWebStore(self): 396 def uploadToChromeWebStore(self):
397 import M2Crypto 397 # use refresh token to obtain a valid access token
398 398 # https://developers.google.com/accounts/docs/OAuth2WebServer#refresh
399 # log in with the Google Developer Service Account
400 # https://developers.google.com/accounts/docs/OAuth2ServiceAccount#creatingj wt
401
402 base64encode = lambda data: base64.urlsafe_b64encode(data).rstrip('=')
403 now = int(time.mktime(time.gmtime()))
404
405 jwt = '%s.%s' % (
406 base64encode(json.dumps({
407 'alg': 'RS256',
408 'typ': 'JWT',
409 })),
410 base64encode(json.dumps({
411 'iss': self.config.serviceAccountEmail,
412 'scope': 'https://www.googleapis.com/auth/chromewebstore',
413 'aud': 'https://accounts.google.com/o/oauth2/token',
414 'exp': now + 3600, # fails if less than about an hour
415 'iat': now,
416 })),
417 )
418
419 jwt = '%s.%s' % (jwt, base64encode(
420 M2Crypto.RSA.load_key(self.config.serviceAccountKey).sign(
421 hashlib.sha256(jwt).digest(), 'sha256'
422 )
423 ))
424 399
425 response = json.load(urllib2.urlopen( 400 response = json.load(urllib2.urlopen(
426 'https://accounts.google.com/o/oauth2/token', 401 'https://accounts.google.com/o/oauth2/token',
427 402
428 urlencode([ 403 urlencode([
429 ('grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer'), 404 ('refresh_token', self.config.refreshToken),
430 ('assertion', jwt), 405 ('client_id', self.config.clientID),
406 ('client_secret', self.config.clientSecret),
407 ('grant_type', 'refresh_token'),
431 ]) 408 ])
432 )) 409 ))
433 410
434 # upload a new version with the Chrome Web Store API 411 # upload a new version with the Chrome Web Store API
435 # https://developer.chrome.com/webstore/using_webstore_api#uploadexisitng 412 # https://developer.chrome.com/webstore/using_webstore_api#uploadexisitng
436 413
437 request = urllib2.Request('https://www.googleapis.com/upload/chromewebstore/ v1.1/items/' + self.config.galleryID) 414 request = urllib2.Request('https://www.googleapis.com/upload/chromewebstore/ v1.1/items/' + self.config.galleryID)
438 request.get_method = lambda: 'PUT' 415 request.get_method = lambda: 'PUT'
439 request.add_header('Authorization', '%s %s' % (response['token_type'], respo nse['access_token'])) 416 request.add_header('Authorization', '%s %s' % (response['token_type'], respo nse['access_token']))
440 request.add_header('x-goog-api-version', '2') 417 request.add_header('x-goog-api-version', '2')
441 418
442 with open(self.path, 'rb') as file: 419 with open(self.path, 'rb') as file:
443 request.add_header('Content-Length', '%d' % os.fstat(file.fileno()).st_siz e) 420 if file.read(8) != 'Cr24\x02\x00\x00\x00':
421 raise Exception('not a chrome extension or unknown CRX version')
422
423 # skip public key and signature
424 file.seek(sum(struct.unpack('<II', file.read(8))), os.SEEK_CUR)
425
426 request.add_header('Content-Length', os.fstat(file.fileno()).st_size - fil e.tell())
444 request.add_data(file) 427 request.add_data(file)
445 428
446 response = json.load(urllib2.urlopen(request)) 429 response = json.load(urllib2.urlopen(request))
447 430
448 if response['uploadState'] == 'FAILURE': 431 if response['uploadState'] == 'FAILURE':
449 raise Exception(response['itemError']) 432 raise Exception(response['itemError'])
450 433
451 def run(self): 434 def run(self):
452 """ 435 """
453 Run the nightly build process for one extension 436 Run the nightly build process for one extension
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
488 471
489 if self.config.type == 'ie': 472 if self.config.type == 'ie':
490 self.writeIEUpdateManifest(versions) 473 self.writeIEUpdateManifest(versions)
491 474
492 # update index page 475 # update index page
493 self.updateIndex(versions) 476 self.updateIndex(versions)
494 477
495 # update nightlies config 478 # update nightlies config
496 self.config.latestRevision = self.revision 479 self.config.latestRevision = self.revision
497 480
498 if self.config.serviceAccountEmail and self.config.serviceAccountKey: 481 if self.type == 'chrome' and self.config.clientID and self.config.clientSe cret and self.config.refreshToken:
499 self.uploadToChromeWebStore() 482 self.uploadToChromeWebStore()
500 finally: 483 finally:
501 # clean up 484 # clean up
502 if self.tempdir: 485 if self.tempdir:
503 shutil.rmtree(self.tempdir, ignore_errors=True) 486 shutil.rmtree(self.tempdir, ignore_errors=True)
504 487
505 488
506 def main(): 489 def main():
507 """ 490 """
508 main function for createNightlies.py 491 main function for createNightlies.py
(...skipping 17 matching lines...) Expand all
526 except Exception, ex: 509 except Exception, ex:
527 print >>sys.stderr, "The build for %s failed:" % repo 510 print >>sys.stderr, "The build for %s failed:" % repo
528 traceback.print_exc() 511 traceback.print_exc()
529 512
530 file = open(nightlyConfigFile, 'wb') 513 file = open(nightlyConfigFile, 'wb')
531 nightlyConfig.write(file) 514 nightlyConfig.write(file)
532 515
533 516
534 if __name__ == '__main__': 517 if __name__ == '__main__':
535 main() 518 main()
LEFTRIGHT

Powered by Google App Engine
This is Rietveld