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

Unified Diff: sitescripts/extensions/bin/createNightlies.py

Issue 5092904657747968: Issue 356 - Update devbuild in the Chrome Web Store (Closed)
Patch Set: Created April 19, 2014, 4:12 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « .sitescripts.example ('k') | sitescripts/extensions/utils.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sitescripts/extensions/bin/createNightlies.py
===================================================================
--- a/sitescripts/extensions/bin/createNightlies.py
+++ b/sitescripts/extensions/bin/createNightlies.py
@@ -26,8 +26,10 @@
"""
import sys, os, os.path, codecs, subprocess, ConfigParser, traceback, json, hashlib
-import tempfile, re, shutil, urlparse, pipes
+import tempfile, re, shutil, urlparse, pipes, time, base64
from datetime import datetime
+from urllib import urlencode
+from urllib2 import urlopen
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 +288,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 +304,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 +394,57 @@
finally:
shutil.rmtree(docsdir, ignore_errors=True)
+ def uploadToChromeWebStore(self):
+ import M2Crypto
+
+ # log in with the Google Developer Service Account
+ # https://developers.google.com/accounts/docs/OAuth2ServiceAccount#creatingjwt
+
+ base64encode = lambda data: base64.urlsafe_b64encode(data).rstrip('=')
+ now = int(time.mktime(time.gmtime()))
+
+ jwt = '%s.%s' % (
+ base64encode(json.dumps({
+ 'alg': 'RS256',
+ 'typ': 'JWT',
+ })),
+ base64encode(json.dumps({
+ 'iss': self.config.serviceAccountEmail,
+ 'scope': 'https://www.googleapis.com/auth/chromewebstore',
+ 'aud':'https://accounts.google.com/o/oauth2/token',
+ 'exp': now + 3600, # fails if less than about an hour
+ 'iat': now,
+ })),
+ )
+
+ jwt = '%s.%s' % (jwt, base64encode(
+ M2Crypto.RSA.load_key(self.config.serviceAccountKey).sign(
+ hashlib.sha256(jwt).digest(), 'sha256'
+ )
+ ))
+
+ response = json.load(urlopen(
+ 'https://accounts.google.com/o/oauth2/token',
+
+ urlencode([
+ ('grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer'),
+ ('assertion', jwt),
+ ])
+ ))
Wladimir Palant 2014/04/20 06:47:53 Check for error responses?
Sebastian Noack 2014/04/20 16:03:08 This API uses standard HTTP error codes, which ar
+
+ # upload a new version with the Chrome Web Store API
+ # https://developer.chrome.com/webstore/using_webstore_api#uploadexisitng
+
+ subprocess.check_call([
Sebastian Noack 2014/04/19 16:15:34 I use curl instead of urllib here, because urllib
Wladimir Palant 2014/04/20 06:47:53 We do file uploads with urllib in buildtools/local
Sebastian Noack 2014/04/20 16:03:08 It was actually easier to use urllib2 as I thought
+ 'curl', 'https://www.googleapis.com/upload/chromewebstore/v1.1/items/' + self.config.galleryID
+
+ '--header', 'Authorization: %s %s' % (response['token_type'], response['access_token'])
+ '--header', 'x-goog-api-version: 2',
+ '--request', 'PUT',
+ '--upload-file', self.path,
+ '--silent',
+ ])
Wladimir Palant 2014/04/20 06:47:53 Here as well - please check for error responses, i
Sebastian Noack 2014/04/20 16:03:08 Done.
+
def run(self):
"""
Run the nightly build process for one extension
@@ -438,6 +491,9 @@
# update nightlies config
self.config.latestRevision = self.revision
+
+ if self.config.type == 'chrome':
Wladimir Palant 2014/04/20 06:47:53 This step needs to be optional, only if the necess
Sebastian Noack 2014/04/20 16:03:08 Done.
+ self.uploadToChromeWebStore()
finally:
# clean up
if self.tempdir:
« no previous file with comments | « .sitescripts.example ('k') | sitescripts/extensions/utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld