| Left: | ||
| Right: |
| LEFT | RIGHT |
|---|---|
| 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-2015 Eyeo GmbH | 4 # Copyright (C) 2006-2015 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, subprocess, ConfigParser, json, hashlib | 28 import ConfigParser |
|
Felix Dahlke
2015/08/13 12:52:27
Nit: Might be a good opportunity to clean the comm
Wladimir Palant
2015/08/13 13:00:40
As I said, I intentionally didn't clean up the imp
| |
| 29 import tempfile, shutil, urlparse, pipes, time, urllib2, struct | |
| 30 import cookielib | 29 import cookielib |
| 30 from datetime import datetime | |
| 31 import hashlib | |
| 31 import HTMLParser | 32 import HTMLParser |
| 33 import json | |
| 32 import logging | 34 import logging |
| 33 from datetime import datetime | 35 import os |
| 36 import pipes | |
| 37 import shutil | |
| 38 import struct | |
| 39 import subprocess | |
| 40 import sys | |
| 41 import tempfile | |
| 42 import time | |
| 34 from urllib import urlencode | 43 from urllib import urlencode |
| 44 import urllib2 | |
| 45 import urlparse | |
| 35 from xml.dom.minidom import parse as parseXml | 46 from xml.dom.minidom import parse as parseXml |
| 36 from sitescripts.utils import get_config, get_template | 47 |
| 37 from sitescripts.extensions.utils import ( | 48 from sitescripts.extensions.utils import ( |
| 38 compareVersions, Configuration, | 49 compareVersions, Configuration, |
| 39 writeAndroidUpdateManifest | 50 writeAndroidUpdateManifest |
| 40 ) | 51 ) |
| 52 from sitescripts.utils import get_config, get_template | |
| 41 | 53 |
| 42 MAX_BUILDS = 50 | 54 MAX_BUILDS = 50 |
| 43 | 55 |
| 44 | 56 |
| 45 class NightlyBuild(object): | 57 class NightlyBuild(object): |
| 46 """ | 58 """ |
| 47 Performs the build process for an extension, | 59 Performs the build process for an extension, |
| 48 generating changelogs and documentation. | 60 generating changelogs and documentation. |
| 49 """ | 61 """ |
| 50 | 62 |
| (...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 384 password = get_config().get('extensions', 'amo_password') | 396 password = get_config().get('extensions', 'amo_password') |
| 385 | 397 |
| 386 slug = self.config.galleryID | 398 slug = self.config.galleryID |
| 387 login_url= 'https://addons.mozilla.org/en-US/firefox/users/login' | 399 login_url= 'https://addons.mozilla.org/en-US/firefox/users/login' |
| 388 upload_url = 'https://addons.mozilla.org/en-US/developers/addon/%s/upload' % slug | 400 upload_url = 'https://addons.mozilla.org/en-US/developers/addon/%s/upload' % slug |
| 389 add_url = 'https://addons.mozilla.org/en-US/developers/addon/%s/versions/add ' % slug | 401 add_url = 'https://addons.mozilla.org/en-US/developers/addon/%s/versions/add ' % slug |
| 390 | 402 |
| 391 cookie_jar = cookielib.CookieJar() | 403 cookie_jar = cookielib.CookieJar() |
| 392 opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie_jar)) | 404 opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie_jar)) |
| 393 | 405 |
| 394 def load_url(url, data=None): | 406 def load_url(url, data=None): |
|
Sebastian Noack
2015/08/12 13:29:26
Any reason, why you don'T simply use urllib3.reque
Wladimir Palant
2015/08/12 13:34:10
Yes, urllib3 doesn't support cookie jars.
| |
| 395 content_type = 'application/x-www-form-urlencoded' | 407 content_type = 'application/x-www-form-urlencoded' |
| 396 if isinstance(data, dict): | 408 if isinstance(data, dict): |
| 397 if any(isinstance(v, tuple) for v in data.itervalues()): | 409 if any(isinstance(v, tuple) for v in data.itervalues()): |
| 398 data, content_type = urllib3.filepost.encode_multipart_formdata(data) | 410 data, content_type = urllib3.filepost.encode_multipart_formdata(data) |
| 399 else: | 411 else: |
| 400 data = urlencode(data.items()) | 412 data = urlencode(data.items()) |
| 401 | 413 |
| 402 request = urllib2.Request(url, data, headers={'Content-Type': content_type }) | 414 request = urllib2.Request(url, data, headers={'Content-Type': content_type }) |
| 403 response = opener.open(request) | 415 response = opener.open(request) |
| 404 try: | 416 try: |
| 405 return response.read() | 417 return response.read() |
| 406 finally: | 418 finally: |
| 407 response.close() | 419 response.close() |
| 408 | 420 |
| 409 class CSRFParser(HTMLParser.HTMLParser): | 421 class CSRFParser(HTMLParser.HTMLParser): |
|
Sebastian Noack
2015/08/12 13:29:26
Don't we already use minidom somewhere else? That
Wladimir Palant
2015/08/12 13:34:10
Does minidom parse HTML? Not that I know.
Felix Dahlke
2015/08/13 12:52:27
I've used BeautifulSoup for this kind of stuff. Bu
Sebastian Noack
2015/08/18 09:04:13
Alright, minidom is only for XML, though it also w
| |
| 410 result = None | 422 result = None |
| 411 dummy_exception = Exception() | 423 dummy_exception = Exception() |
| 412 | 424 |
| 413 def __init__(self, data): | 425 def __init__(self, data): |
| 414 HTMLParser.HTMLParser.__init__(self) | 426 HTMLParser.HTMLParser.__init__(self) |
| 415 try: | 427 try: |
| 416 self.feed(data) | 428 self.feed(data) |
| 417 self.close() | 429 self.close() |
| 418 except Exception, e: | 430 except Exception, e: |
| 419 if e != self.dummy_exception: | 431 if e != self.dummy_exception: |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 453 upload_response = json.loads(load_url( | 465 upload_response = json.loads(load_url( |
| 454 upload_url, | 466 upload_url, |
| 455 { | 467 { |
| 456 'csrfmiddlewaretoken': csrf_token, | 468 'csrfmiddlewaretoken': csrf_token, |
| 457 'upload': (os.path.basename(self.path), file.read(), 'application/x-xp install'), | 469 'upload': (os.path.basename(self.path), file.read(), 'application/x-xp install'), |
| 458 } | 470 } |
| 459 )) | 471 )) |
| 460 | 472 |
| 461 # Wait for validation to finish | 473 # Wait for validation to finish |
| 462 while not upload_response.get('validation'): | 474 while not upload_response.get('validation'): |
| 463 time.sleep(2) | 475 time.sleep(2) |
|
Sebastian Noack
2015/08/12 13:29:26
I suppose there is no way to get notified when it'
Wladimir Palant
2015/08/12 13:34:10
No, it's exactly how the web interface does it - b
| |
| 464 upload_response = json.loads(load_url( | 476 upload_response = json.loads(load_url( |
| 465 upload_url + '/' + upload_response.get('upload') | 477 upload_url + '/' + upload_response.get('upload') |
| 466 )) | 478 )) |
| 467 | 479 |
| 468 if upload_response['validation'].get('errors', 0): | 480 if upload_response['validation'].get('errors', 0): |
|
Sebastian Noack
2015/08/12 13:29:26
Nit: Omit the default value? None evaluates to Fal
Wladimir Palant
2015/08/12 13:34:10
Yes, I considered it but the default value indicat
| |
| 469 raise Exception('Build failed AMO validation, see https://addons.mozilla.o rg%s' % upload_response.get('full_report_url')) | 481 raise Exception('Build failed AMO validation, see https://addons.mozilla.o rg%s' % upload_response.get('full_report_url')) |
| 470 | 482 |
| 471 # Add version | 483 # Add version |
| 472 add_response = json.loads(load_url( | 484 add_response = json.loads(load_url( |
| 473 add_url, | 485 add_url, |
| 474 { | 486 { |
| 475 'csrfmiddlewaretoken': csrf_token, | 487 'csrfmiddlewaretoken': csrf_token, |
| 476 'upload': upload_response.get('upload'), | 488 'upload': upload_response.get('upload'), |
| 477 'source': ('', '', 'application/octet-stream'), | 489 'source': ('', '', 'application/octet-stream'), |
| 478 'beta': 'on', | 490 'beta': 'on', |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 565 self.readSafariMetadata() | 577 self.readSafariMetadata() |
| 566 else: | 578 else: |
| 567 self.readGeckoMetadata() | 579 self.readGeckoMetadata() |
| 568 | 580 |
| 569 # create development build | 581 # create development build |
| 570 self.build() | 582 self.build() |
| 571 | 583 |
| 572 # write out changelog | 584 # write out changelog |
| 573 self.writeChangelog(self.getChanges()) | 585 self.writeChangelog(self.getChanges()) |
| 574 | 586 |
| 575 # write update.rdf file | 587 # write update manifest |
| 576 self.writeUpdateManifest() | 588 if self.config.type != 'gecko': |
| 589 self.writeUpdateManifest() | |
| 577 | 590 |
| 578 # update documentation | 591 # update documentation |
| 579 self.updateDocs() | 592 self.updateDocs() |
| 580 | 593 |
| 581 # retire old builds | 594 # retire old builds |
| 582 versions = self.retireBuilds() | 595 versions = self.retireBuilds() |
| 583 | 596 |
| 584 if self.config.type == 'ie': | 597 if self.config.type == 'ie': |
| 585 self.writeIEUpdateManifest(versions) | 598 self.writeIEUpdateManifest(versions) |
| 586 | 599 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 621 except Exception, ex: | 634 except Exception, ex: |
| 622 logging.error("The build for %s failed:", repo) | 635 logging.error("The build for %s failed:", repo) |
| 623 logging.exception(ex) | 636 logging.exception(ex) |
| 624 | 637 |
| 625 file = open(nightlyConfigFile, 'wb') | 638 file = open(nightlyConfigFile, 'wb') |
| 626 nightlyConfig.write(file) | 639 nightlyConfig.write(file) |
| 627 | 640 |
| 628 | 641 |
| 629 if __name__ == '__main__': | 642 if __name__ == '__main__': |
| 630 main() | 643 main() |
| LEFT | RIGHT |