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

Side by Side Diff: sitescripts/extensions/bin/createNightlies.py

Issue 5092904657747968: Issue 356 - Update devbuild in the Chrome Web Store (Closed)
Patch Set: Use Client ID for installed applications Created April 20, 2014, 8:46 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « .sitescripts.example ('k') | sitescripts/extensions/utils.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 29 import tempfile, re, shutil, urlparse, pipes, time, urllib2
30 from datetime import datetime 30 from datetime import datetime
31 from urllib import urlencode
31 from xml.dom.minidom import parse as parseXml 32 from xml.dom.minidom import parse as parseXml
32 from sitescripts.utils import get_config, setupStderr, get_template 33 from sitescripts.utils import get_config, setupStderr, get_template
33 from sitescripts.extensions.utils import compareVersions, Configuration 34 from sitescripts.extensions.utils import compareVersions, Configuration
34 35
35 MAX_BUILDS = 50 36 MAX_BUILDS = 50
36 37
37 38
38 class NightlyBuild(object): 39 class NightlyBuild(object):
39 """ 40 """
40 Performs the build process for an extension, 41 Performs the build process for an extension,
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after
279 280
280 281
281 def build(self): 282 def build(self):
282 """ 283 """
283 run the build command in the tempdir 284 run the build command in the tempdir
284 """ 285 """
285 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) 286 baseDir = os.path.join(self.config.nightliesDirectory, self.basename)
286 if not os.path.exists(baseDir): 287 if not os.path.exists(baseDir):
287 os.makedirs(baseDir) 288 os.makedirs(baseDir)
288 outputFile = "%s-%s%s" % (self.basename, self.version, self.config.packageSu ffix) 289 outputFile = "%s-%s%s" % (self.basename, self.version, self.config.packageSu ffix)
289 outputPath = os.path.join(baseDir, outputFile) 290 self.path = os.path.join(baseDir, outputFile)
290 self.updateURL = urlparse.urljoin(self.config.nightliesURL, self.basename + '/' + outputFile + '?update') 291 self.updateURL = urlparse.urljoin(self.config.nightliesURL, self.basename + '/' + outputFile + '?update')
291 292
292 if self.config.type == 'android': 293 if self.config.type == 'android':
293 apkFile = open(outputPath, 'wb') 294 apkFile = open(self.path, 'wb')
294 295
295 try: 296 try:
296 try: 297 try:
297 port = get_config().get('extensions', 'androidBuildPort') 298 port = get_config().get('extensions', 'androidBuildPort')
298 except ConfigParser.NoOptionError: 299 except ConfigParser.NoOptionError:
299 port = '22' 300 port = '22'
300 buildCommand = ['ssh', '-p', port, get_config().get('extensions', 'andro idBuildHost')] 301 buildCommand = ['ssh', '-p', port, get_config().get('extensions', 'andro idBuildHost')]
301 buildCommand.extend(map(pipes.quote, ['/home/android/bin/makedebugbuild. py', '--revision', self.revision, '--version', self.version, '--stdout'])) 302 buildCommand.extend(map(pipes.quote, ['/home/android/bin/makedebugbuild. py', '--revision', self.revision, '--version', self.version, '--stdout']))
302 subprocess.check_call(buildCommand, stdout=apkFile, close_fds=True) 303 subprocess.check_call(buildCommand, stdout=apkFile, close_fds=True)
303 except: 304 except:
304 # clear broken output if any 305 # clear broken output if any
305 if os.path.exists(outputPath): 306 if os.path.exists(self.path):
306 os.remove(outputPath) 307 os.remove(self.path)
307 raise 308 raise
308 elif self.config.type == 'chrome' or self.config.type == 'opera': 309 elif self.config.type == 'chrome' or self.config.type == 'opera':
309 import buildtools.packagerChrome as packager 310 import buildtools.packagerChrome as packager
310 packager.createBuild(self.tempdir, type=self.config.type, outFile=outputPa th, buildNum=self.revision, keyFile=self.config.keyFile, experimentalAPI=self.co nfig.experimental) 311 packager.createBuild(self.tempdir, type=self.config.type, outFile=self.pat h, buildNum=self.revision, keyFile=self.config.keyFile, experimentalAPI=self.con fig.experimental)
311 elif self.config.type == 'safari': 312 elif self.config.type == 'safari':
312 import buildtools.packagerSafari as packager 313 import buildtools.packagerSafari as packager
313 packager.createBuild(self.tempdir, type=self.config.type, outFile=outputPa th, buildNum=self.revision, keyFile=self.config.keyFile) 314 packager.createBuild(self.tempdir, type=self.config.type, outFile=self.pat h, buildNum=self.revision, keyFile=self.config.keyFile)
314 else: 315 else:
315 import buildtools.packagerGecko as packager 316 import buildtools.packagerGecko as packager
316 packager.createBuild(self.tempdir, outFile=outputPath, buildNum=self.revis ion, keyFile=self.config.keyFile) 317 packager.createBuild(self.tempdir, outFile=self.path, buildNum=self.revisi on, keyFile=self.config.keyFile)
317 318
318 if not os.path.exists(outputPath): 319 if not os.path.exists(self.path):
319 raise Exception("Build failed, output file hasn't been created") 320 raise Exception("Build failed, output file hasn't been created")
320 321
321 linkPath = os.path.join(baseDir, '00latest%s' % self.config.packageSuffix) 322 linkPath = os.path.join(baseDir, '00latest%s' % self.config.packageSuffix)
322 if hasattr(os, 'symlink'): 323 if hasattr(os, 'symlink'):
323 if os.path.exists(linkPath): 324 if os.path.exists(linkPath):
324 os.remove(linkPath) 325 os.remove(linkPath)
325 os.symlink(os.path.basename(outputPath), linkPath) 326 os.symlink(os.path.basename(self.path), linkPath)
326 else: 327 else:
327 shutil.copyfile(outputPath, linkPath) 328 shutil.copyfile(self.path, linkPath)
328 329
329 def retireBuilds(self): 330 def retireBuilds(self):
330 """ 331 """
331 removes outdated builds, returns the sorted version numbers of remaining 332 removes outdated builds, returns the sorted version numbers of remaining
332 builds 333 builds
333 """ 334 """
334 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) 335 baseDir = os.path.join(self.config.nightliesDirectory, self.basename)
335 versions = [] 336 versions = []
336 prefix = self.basename + '-' 337 prefix = self.basename + '-'
337 suffix = self.config.packageSuffix 338 suffix = self.config.packageSuffix
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
385 command = ['hg', 'archive', '-R', get_config().get('extensions', 'jsdocRepos itory'), '-r', 'default', docsdir] 386 command = ['hg', 'archive', '-R', get_config().get('extensions', 'jsdocRepos itory'), '-r', 'default', docsdir]
386 subprocess.check_call(command) 387 subprocess.check_call(command)
387 388
388 try: 389 try:
389 import buildtools.build as build 390 import buildtools.build as build
390 outputPath = os.path.join(self.config.docsDirectory, self.basename) 391 outputPath = os.path.join(self.config.docsDirectory, self.basename)
391 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)
392 finally: 393 finally:
393 shutil.rmtree(docsdir, ignore_errors=True) 394 shutil.rmtree(docsdir, ignore_errors=True)
394 395
396 def uploadToChromeWebStore(self):
397 # use refresh token to obtain a valid access token
398 # https://developers.google.com/accounts/docs/OAuth2WebServer#refresh
399
400 response = json.load(urllib2.urlopen(
401 'https://accounts.google.com/o/oauth2/token',
402
403 urlencode([
404 ('refresh_token', self.config.refreshToken),
405 ('client_id', self.config.clientID),
406 ('client_secret', self.config.clientSecret),
407 ('grant_type', 'refresh_token'),
408 ])
409 ))
410
411 # upload a new version with the Chrome Web Store API
412 # https://developer.chrome.com/webstore/using_webstore_api#uploadexisitng
413
414 request = urllib2.Request('https://www.googleapis.com/upload/chromewebstore/ v1.1/items/' + self.config.galleryID)
415 request.get_method = lambda: 'PUT'
416 request.add_header('Authorization', '%s %s' % (response['token_type'], respo nse['access_token']))
417 request.add_header('x-goog-api-version', '2')
418
419 with open(self.path, 'rb') as file:
420 request.add_header('Content-Length', '%d' % os.fstat(file.fileno()).st_siz e)
421 request.add_data(file)
422
423 response = json.load(urllib2.urlopen(request))
424
425 if response['uploadState'] == 'FAILURE':
426 raise Exception(response['itemError'])
427
395 def run(self): 428 def run(self):
396 """ 429 """
397 Run the nightly build process for one extension 430 Run the nightly build process for one extension
398 """ 431 """
399 try: 432 try:
400 if self.config.type == 'ie': 433 if self.config.type == 'ie':
401 # We cannot build IE builds, simply list the builds already in 434 # We cannot build IE builds, simply list the builds already in
402 # the directory. Basename has to be deduced from the repository name. 435 # the directory. Basename has to be deduced from the repository name.
403 self.basename = os.path.basename(self.config.repository) 436 self.basename = os.path.basename(self.config.repository)
404 else: 437 else:
(...skipping 26 matching lines...) Expand all
431 versions = self.retireBuilds() 464 versions = self.retireBuilds()
432 465
433 if self.config.type == 'ie': 466 if self.config.type == 'ie':
434 self.writeIEUpdateManifest(versions) 467 self.writeIEUpdateManifest(versions)
435 468
436 # update index page 469 # update index page
437 self.updateIndex(versions) 470 self.updateIndex(versions)
438 471
439 # update nightlies config 472 # update nightlies config
440 self.config.latestRevision = self.revision 473 self.config.latestRevision = self.revision
474
475 if self.config.clientID and self.config.clientSecret and self.config.refre shToken:
476 self.uploadToChromeWebStore()
441 finally: 477 finally:
442 # clean up 478 # clean up
443 if self.tempdir: 479 if self.tempdir:
444 shutil.rmtree(self.tempdir, ignore_errors=True) 480 shutil.rmtree(self.tempdir, ignore_errors=True)
445 481
446 482
447 def main(): 483 def main():
448 """ 484 """
449 main function for createNightlies.py 485 main function for createNightlies.py
450 """ 486 """
(...skipping 16 matching lines...) Expand all
467 except Exception, ex: 503 except Exception, ex:
468 print >>sys.stderr, "The build for %s failed:" % repo 504 print >>sys.stderr, "The build for %s failed:" % repo
469 traceback.print_exc() 505 traceback.print_exc()
470 506
471 file = open(nightlyConfigFile, 'wb') 507 file = open(nightlyConfigFile, 'wb')
472 nightlyConfig.write(file) 508 nightlyConfig.write(file)
473 509
474 510
475 if __name__ == '__main__': 511 if __name__ == '__main__':
476 main() 512 main()
OLDNEW
« 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