| Index: sitescripts/extensions/bin/createNightlies.py |
| =================================================================== |
| --- a/sitescripts/extensions/bin/createNightlies.py |
| +++ b/sitescripts/extensions/bin/createNightlies.py |
| @@ -26,8 +26,9 @@ |
| """ |
| import sys, os, os.path, codecs, subprocess, ConfigParser, traceback, json, hashlib |
| -import tempfile, re, shutil, urlparse, pipes |
| +import tempfile, re, shutil, urlparse, pipes, time, urllib2, struct |
| from datetime import datetime |
| +from urllib import urlencode |
| from xml.dom.minidom import parse as parseXml |
| from sitescripts.utils import get_config, setupStderr, get_template |
| from sitescripts.extensions.utils import compareVersions, Configuration |
| @@ -286,11 +287,11 @@ |
| if not os.path.exists(baseDir): |
| os.makedirs(baseDir) |
| outputFile = "%s-%s%s" % (self.basename, self.version, self.config.packageSuffix) |
| - outputPath = os.path.join(baseDir, outputFile) |
| + self.path = os.path.join(baseDir, outputFile) |
| self.updateURL = urlparse.urljoin(self.config.nightliesURL, self.basename + '/' + outputFile + '?update') |
| if self.config.type == 'android': |
| - apkFile = open(outputPath, 'wb') |
| + apkFile = open(self.path, 'wb') |
| try: |
| try: |
| @@ -302,29 +303,29 @@ |
| subprocess.check_call(buildCommand, stdout=apkFile, close_fds=True) |
| except: |
| # clear broken output if any |
| - if os.path.exists(outputPath): |
| - os.remove(outputPath) |
| + if os.path.exists(self.path): |
| + os.remove(self.path) |
| raise |
| elif self.config.type == 'chrome' or self.config.type == 'opera': |
| import buildtools.packagerChrome as packager |
| - packager.createBuild(self.tempdir, type=self.config.type, outFile=outputPath, buildNum=self.revision, keyFile=self.config.keyFile, experimentalAPI=self.config.experimental) |
| + packager.createBuild(self.tempdir, type=self.config.type, outFile=self.path, buildNum=self.revision, keyFile=self.config.keyFile, experimentalAPI=self.config.experimental) |
| elif self.config.type == 'safari': |
| import buildtools.packagerSafari as packager |
| - packager.createBuild(self.tempdir, type=self.config.type, outFile=outputPath, buildNum=self.revision, keyFile=self.config.keyFile) |
| + packager.createBuild(self.tempdir, type=self.config.type, outFile=self.path, buildNum=self.revision, keyFile=self.config.keyFile) |
| else: |
| import buildtools.packagerGecko as packager |
| - packager.createBuild(self.tempdir, outFile=outputPath, buildNum=self.revision, keyFile=self.config.keyFile) |
| + packager.createBuild(self.tempdir, outFile=self.path, buildNum=self.revision, keyFile=self.config.keyFile) |
| - if not os.path.exists(outputPath): |
| + if not os.path.exists(self.path): |
| raise Exception("Build failed, output file hasn't been created") |
| linkPath = os.path.join(baseDir, '00latest%s' % self.config.packageSuffix) |
| if hasattr(os, 'symlink'): |
| if os.path.exists(linkPath): |
| os.remove(linkPath) |
| - os.symlink(os.path.basename(outputPath), linkPath) |
| + os.symlink(os.path.basename(self.path), linkPath) |
| else: |
| - shutil.copyfile(outputPath, linkPath) |
| + shutil.copyfile(self.path, linkPath) |
| def retireBuilds(self): |
| """ |
| @@ -392,6 +393,46 @@ |
| finally: |
| shutil.rmtree(docsdir, ignore_errors=True) |
| + def uploadToChromeWebStore(self): |
| + # use refresh token to obtain a valid access token |
| + # https://developers.google.com/accounts/docs/OAuth2WebServer#refresh |
| + |
| + response = json.load(urllib2.urlopen( |
| + 'https://accounts.google.com/o/oauth2/token', |
| + |
| + urlencode([ |
| + ('refresh_token', self.config.refreshToken), |
| + ('client_id', self.config.clientID), |
| + ('client_secret', self.config.clientSecret), |
| + ('grant_type', 'refresh_token'), |
| + ]) |
| + )) |
| + |
| + # upload a new version with the Chrome Web Store API |
| + # https://developer.chrome.com/webstore/using_webstore_api#uploadexisitng |
| + |
| + request = urllib2.Request('https://www.googleapis.com/upload/chromewebstore/v1.1/items/' + self.config.galleryID) |
| + request.get_method = lambda: 'PUT' |
| + request.add_header('Authorization', '%s %s' % (response['token_type'], response['access_token'])) |
| + request.add_header('x-goog-api-version', '2') |
| + |
| + with open(self.path, 'rb') as file: |
| + # skip CRX magic and version |
| + offset = 8 |
| + file.seek(offset) |
| + |
| + # skip public key and signature |
| + offset += 8 + sum(struct.unpack('<II', file.read(8))) |
|
Wladimir Palant
2014/04/21 20:22:56
Please don't skip magic number and version - read
Sebastian Noack
2014/04/21 20:46:36
Done.
|
| + file.seek(offset) |
|
Wladimir Palant
2014/04/21 20:22:56
Please use os.SEEK_CUR as second parameter to file
Sebastian Noack
2014/04/21 20:46:36
Done.
|
| + |
| + request.add_header('Content-Length', os.fstat(file.fileno()).st_size - offset) |
| + request.add_data(file) |
| + |
| + response = json.load(urllib2.urlopen(request)) |
| + |
| + if response['uploadState'] == 'FAILURE': |
| + raise Exception(response['itemError']) |
| + |
| def run(self): |
| """ |
| Run the nightly build process for one extension |
| @@ -438,6 +479,9 @@ |
| # update nightlies config |
| self.config.latestRevision = self.revision |
| + |
| + if self.config.clientID and self.config.clientSecret and self.config.refreshToken: |
|
Wladimir Palant
2014/04/21 20:22:56
I think it makes sense to keep the type == 'chrome
Sebastian Noack
2014/04/21 20:46:36
Done.
|
| + self.uploadToChromeWebStore() |
| finally: |
| # clean up |
| if self.tempdir: |