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: 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:
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, struct
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 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())
427 request.add_data(file)
428
429 response = json.load(urllib2.urlopen(request))
430
431 if response['uploadState'] == 'FAILURE':
432 raise Exception(response['itemError'])
433
395 def run(self): 434 def run(self):
396 """ 435 """
397 Run the nightly build process for one extension 436 Run the nightly build process for one extension
398 """ 437 """
399 try: 438 try:
400 if self.config.type == 'ie': 439 if self.config.type == 'ie':
401 # We cannot build IE builds, simply list the builds already in 440 # We cannot build IE builds, simply list the builds already in
402 # the directory. Basename has to be deduced from the repository name. 441 # the directory. Basename has to be deduced from the repository name.
403 self.basename = os.path.basename(self.config.repository) 442 self.basename = os.path.basename(self.config.repository)
404 else: 443 else:
(...skipping 26 matching lines...) Expand all
431 versions = self.retireBuilds() 470 versions = self.retireBuilds()
432 471
433 if self.config.type == 'ie': 472 if self.config.type == 'ie':
434 self.writeIEUpdateManifest(versions) 473 self.writeIEUpdateManifest(versions)
435 474
436 # update index page 475 # update index page
437 self.updateIndex(versions) 476 self.updateIndex(versions)
438 477
439 # update nightlies config 478 # update nightlies config
440 self.config.latestRevision = self.revision 479 self.config.latestRevision = self.revision
480
481 if self.type == 'chrome' and self.config.clientID and self.config.clientSe cret and self.config.refreshToken:
482 self.uploadToChromeWebStore()
441 finally: 483 finally:
442 # clean up 484 # clean up
443 if self.tempdir: 485 if self.tempdir:
444 shutil.rmtree(self.tempdir, ignore_errors=True) 486 shutil.rmtree(self.tempdir, ignore_errors=True)
445 487
446 488
447 def main(): 489 def main():
448 """ 490 """
449 main function for createNightlies.py 491 main function for createNightlies.py
450 """ 492 """
(...skipping 16 matching lines...) Expand all
467 except Exception, ex: 509 except Exception, ex:
468 print >>sys.stderr, "The build for %s failed:" % repo 510 print >>sys.stderr, "The build for %s failed:" % repo
469 traceback.print_exc() 511 traceback.print_exc()
470 512
471 file = open(nightlyConfigFile, 'wb') 513 file = open(nightlyConfigFile, 'wb')
472 nightlyConfig.write(file) 514 nightlyConfig.write(file)
473 515
474 516
475 if __name__ == '__main__': 517 if __name__ == '__main__':
476 main() 518 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