| Left: | ||
| Right: | 
| OLD | NEW | 
|---|---|
| 1 # This file is part of the Adblock Plus web scripts, | 1 # This file is part of the Adblock Plus web scripts, | 
| 2 # Copyright (C) 2006-2016 Eyeo GmbH | 2 # Copyright (C) 2006-2016 Eyeo GmbH | 
| 3 # | 3 # | 
| 4 # Adblock Plus is free software: you can redistribute it and/or modify | 4 # Adblock Plus is free software: you can redistribute it and/or modify | 
| 5 # it under the terms of the GNU General Public License version 3 as | 5 # it under the terms of the GNU General Public License version 3 as | 
| 6 # published by the Free Software Foundation. | 6 # published by the Free Software Foundation. | 
| 7 # | 7 # | 
| 8 # Adblock Plus is distributed in the hope that it will be useful, | 8 # Adblock Plus is distributed in the hope that it will be useful, | 
| 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 58 generating changelogs and documentation. | 58 generating changelogs and documentation. | 
| 59 """ | 59 """ | 
| 60 | 60 | 
| 61 def __init__(self, config): | 61 def __init__(self, config): | 
| 62 """ | 62 """ | 
| 63 Creates a NightlyBuild instance; we are simply | 63 Creates a NightlyBuild instance; we are simply | 
| 64 recording the configuration settings here. | 64 recording the configuration settings here. | 
| 65 """ | 65 """ | 
| 66 self.config = config | 66 self.config = config | 
| 67 self.revision = self.getCurrentRevision() | 67 self.revision = self.getCurrentRevision() | 
| 68 if self.config.type == 'gecko': | |
| 69 self.revision += '-beta' | |
| 70 try: | 68 try: | 
| 71 self.previousRevision = config.latestRevision | 69 self.previousRevision = config.latestRevision | 
| 72 except: | 70 except: | 
| 73 self.previousRevision = '0' | 71 self.previousRevision = '0' | 
| 72 self.buildNum = None | |
| 74 self.tempdir = None | 73 self.tempdir = None | 
| 75 self.outputFilename = None | 74 self.outputFilename = None | 
| 76 self.changelogFilename = None | 75 self.changelogFilename = None | 
| 77 | 76 | 
| 78 def hasChanges(self): | 77 def hasChanges(self): | 
| 79 return self.revision != self.previousRevision | 78 return self.revision != self.previousRevision | 
| 80 | 79 | 
| 81 def getCurrentRevision(self): | 80 def getCurrentRevision(self): | 
| 82 """ | 81 """ | 
| 83 retrieves the current revision number from the repository | 82 retrieves the current revision ID from the repository | 
| 84 """ | 83 """ | 
| 85 command = ['hg', 'log', '-R', self.config.repository, '-r', 'default', | 84 command = [ | 
| 86 '--template', '{rev}', '--config', 'defaults.log='] | 85 'hg', 'id', '-i', '-r', 'default', '--config', 'defaults.id=', | 
| 87 return subprocess.check_output(command) | 86 self.config.repository | 
| 87 ] | |
| 88 return subprocess.check_output(command).strip() | |
| 89 | |
| 90 def getCurrentBuild(self): | |
| 91 """ | |
| 92 calculates the (typically numerical) build ID for the current build | |
| 93 """ | |
| 94 command = ['hg', 'id', '-n', '--config', 'defaults.id=', self.tempdir] | |
| 95 build = subprocess.check_output(command).strip() | |
| 96 if self.config.type == 'gecko': | |
| 97 build += '-beta' | |
| 98 return build | |
| 88 | 99 | 
| 89 def getChanges(self): | 100 def getChanges(self): | 
| 90 """ | 101 """ | 
| 91 retrieve changes between the current and previous ("first") revision | 102 retrieve changes between the current and previous ("first") revision | 
| 92 """ | 103 """ | 
| 93 | 104 | 
| 94 command = ['hg', 'log', '-R', self.config.repository, '-r', 'tip:0', | 105 command = ['hg', 'log', '-R', self.tempdir, '-r', 'tip:0', | 
| 95 '-b', 'default', '-l', '50', '--encoding', 'utf-8', | 106 '-b', 'default', '-l', '50', '--encoding', 'utf-8', | 
| 96 '--template', '{date|isodate}\\0{author|person}\\0{rev}\\0{de sc}\\0\\0', | 107 '--template', '{date|isodate}\\0{author|person}\\0{rev}\\0{de sc}\\0\\0', | 
| 97 '--config', 'defaults.log='] | 108 '--config', 'defaults.log='] | 
| 98 result = subprocess.check_output(command).decode('utf-8') | 109 result = subprocess.check_output(command).decode('utf-8') | 
| 99 | 110 | 
| 100 for change in result.split('\x00\x00'): | 111 for change in result.split('\x00\x00'): | 
| 101 if change: | 112 if change: | 
| 102 date, author, revision, description = change.split('\x00') | 113 date, author, revision, description = change.split('\x00') | 
| 103 yield {'date': date, 'author': author, 'revision': revision, 'de scription': description} | 114 yield {'date': date, 'author': author, 'revision': revision, 'de scription': description} | 
| 104 | 115 | 
| 105 def copyRepository(self): | 116 def copyRepository(self): | 
| 106 """ | 117 """ | 
| 107 Create a repository copy in a temporary directory | 118 Create a repository copy in a temporary directory | 
| 108 """ | 119 """ | 
| 109 # We cannot use hg archive here due to | |
| 110 # http://bz.selenic.com/show_bug.cgi?id=3747, have to clone properly :-( | |
| 111 self.tempdir = tempfile.mkdtemp(prefix=self.config.repositoryName) | 120 self.tempdir = tempfile.mkdtemp(prefix=self.config.repositoryName) | 
| 112 command = ['hg', 'clone', '-q', self.config.repository, '-u', 'default', self.tempdir] | 121 command = ['hg', 'clone', '-q', self.config.repository, '-u', 'default', self.tempdir] | 
| 113 subprocess.check_call(command) | 122 subprocess.check_call(command) | 
| 114 | 123 | 
| 115 # Make sure to process the dependencies file if it is present | 124 # Make sure to run ensure_dependencies.py if present | 
| 116 import logging | 125 command = [ | 
| 117 logging.disable(logging.WARNING) | 126 sys.executable, | 
| 118 try: | 127 os.path.join(self.tempdir, 'ensure_dependencies.py'), '-q' | 
| 119 from buildtools.ensure_dependencies import resolve_deps | 128 ] | 
| 120 resolve_deps(self.tempdir, self_update=False, | 129 if os.path.isfile(command[0]): | 
| 
 
Vasily Kuznetsov
2016/06/03 16:04:49
Should not this become `command[1]` now that we've
 
Wladimir Palant
2016/06/06 11:47:46
Ouch, you are right. In fact, I had a better idea
 
Vasily Kuznetsov
2016/06/06 12:48:16
Lovely!
 
 | |
| 121 overrideroots={'hg': os.path.abspath( | 130 subprocess.check_call(command) | 
| 122 os.path.join(self.config.repository, os.pardir) | |
| 123 )}, | |
| 124 skipdependencies={'buildtools'}) | |
| 125 finally: | |
| 126 logging.disable(logging.NOTSET) | |
| 127 | 131 | 
| 128 def writeChangelog(self, changes): | 132 def writeChangelog(self, changes): | 
| 129 """ | 133 """ | 
| 130 write the changelog file into the cloned repository | 134 write the changelog file into the cloned repository | 
| 131 """ | 135 """ | 
| 132 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) | 136 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) | 
| 133 if not os.path.exists(baseDir): | 137 if not os.path.exists(baseDir): | 
| 134 os.makedirs(baseDir) | 138 os.makedirs(baseDir) | 
| 135 changelogFile = '%s-%s.changelog.xhtml' % (self.basename, self.version) | 139 changelogFile = '%s-%s.changelog.xhtml' % (self.basename, self.version) | 
| 136 changelogPath = os.path.join(baseDir, changelogFile) | 140 changelogPath = os.path.join(baseDir, changelogFile) | 
| (...skipping 12 matching lines...) Expand all Loading... | |
| 149 | 153 | 
| 150 def readGeckoMetadata(self): | 154 def readGeckoMetadata(self): | 
| 151 """ | 155 """ | 
| 152 read Gecko-specific metadata file from a cloned repository | 156 read Gecko-specific metadata file from a cloned repository | 
| 153 and parse id, version, basename and the compat section | 157 and parse id, version, basename and the compat section | 
| 154 out of the file | 158 out of the file | 
| 155 """ | 159 """ | 
| 156 import buildtools.packagerGecko as packager | 160 import buildtools.packagerGecko as packager | 
| 157 metadata = packager.readMetadata(self.tempdir, self.config.type) | 161 metadata = packager.readMetadata(self.tempdir, self.config.type) | 
| 158 self.extensionID = metadata.get('general', 'id') | 162 self.extensionID = metadata.get('general', 'id') | 
| 159 self.version = packager.getBuildVersion(self.tempdir, metadata, False, s elf.revision) | 163 self.version = packager.getBuildVersion(self.tempdir, metadata, False, | 
| 164 self.buildNum) | |
| 160 self.basename = metadata.get('general', 'basename') | 165 self.basename = metadata.get('general', 'basename') | 
| 161 self.compat = [] | 166 self.compat = [] | 
| 162 for key, value in packager.KNOWN_APPS.iteritems(): | 167 for key, value in packager.KNOWN_APPS.iteritems(): | 
| 163 if metadata.has_option('compat', key): | 168 if metadata.has_option('compat', key): | 
| 164 minVersion, maxVersion = metadata.get('compat', key).split('/') | 169 minVersion, maxVersion = metadata.get('compat', key).split('/') | 
| 165 self.compat.append({'id': value, 'minVersion': minVersion, 'maxV ersion': maxVersion}) | 170 self.compat.append({'id': value, 'minVersion': minVersion, 'maxV ersion': maxVersion}) | 
| 166 | 171 | 
| 167 def readAndroidMetadata(self): | 172 def readAndroidMetadata(self): | 
| 168 """ | 173 """ | 
| 169 Read Android-specific metadata from AndroidManifest.xml file. | 174 Read Android-specific metadata from AndroidManifest.xml file. | 
| 170 """ | 175 """ | 
| 171 manifestFile = open(os.path.join(self.tempdir, 'AndroidManifest.xml'), ' r') | 176 manifestFile = open(os.path.join(self.tempdir, 'AndroidManifest.xml'), ' r') | 
| 172 manifest = parseXml(manifestFile) | 177 manifest = parseXml(manifestFile) | 
| 173 manifestFile.close() | 178 manifestFile.close() | 
| 174 | 179 | 
| 175 root = manifest.documentElement | 180 root = manifest.documentElement | 
| 176 self.version = root.attributes['android:versionName'].value | 181 self.version = root.attributes['android:versionName'].value | 
| 177 while self.version.count('.') < 2: | 182 while self.version.count('.') < 2: | 
| 178 self.version += '.0' | 183 self.version += '.0' | 
| 179 self.version = '%s.%s' % (self.version, self.revision) | 184 self.version = '%s.%s' % (self.version, self.buildNum) | 
| 180 | 185 | 
| 181 usesSdk = manifest.getElementsByTagName('uses-sdk')[0] | 186 usesSdk = manifest.getElementsByTagName('uses-sdk')[0] | 
| 182 self.minSdkVersion = usesSdk.attributes['android:minSdkVersion'].value | 187 self.minSdkVersion = usesSdk.attributes['android:minSdkVersion'].value | 
| 183 self.basename = os.path.basename(self.config.repository) | 188 self.basename = os.path.basename(self.config.repository) | 
| 184 | 189 | 
| 185 def readChromeMetadata(self): | 190 def readChromeMetadata(self): | 
| 186 """ | 191 """ | 
| 187 Read Chrome-specific metadata from metadata file. This will also | 192 Read Chrome-specific metadata from metadata file. This will also | 
| 188 calculate extension ID from the private key. | 193 calculate extension ID from the private key. | 
| 189 """ | 194 """ | 
| 190 | 195 | 
| 191 # Calculate extension ID from public key | 196 # Calculate extension ID from public key | 
| 192 # (see http://supercollider.dk/2010/01/calculating-chrome-extension-id-f rom-your-private-key-233) | 197 # (see http://supercollider.dk/2010/01/calculating-chrome-extension-id-f rom-your-private-key-233) | 
| 193 import buildtools.packagerChrome as packager | 198 import buildtools.packagerChrome as packager | 
| 194 publicKey = packager.getPublicKey(self.config.keyFile) | 199 publicKey = packager.getPublicKey(self.config.keyFile) | 
| 195 hash = hashlib.sha256() | 200 hash = hashlib.sha256() | 
| 196 hash.update(publicKey) | 201 hash.update(publicKey) | 
| 197 self.extensionID = hash.hexdigest()[0:32] | 202 self.extensionID = hash.hexdigest()[0:32] | 
| 198 self.extensionID = ''.join(map(lambda c: chr(97 + int(c, 16)), self.exte nsionID)) | 203 self.extensionID = ''.join(map(lambda c: chr(97 + int(c, 16)), self.exte nsionID)) | 
| 199 | 204 | 
| 200 # Now read metadata file | 205 # Now read metadata file | 
| 201 metadata = packager.readMetadata(self.tempdir, self.config.type) | 206 metadata = packager.readMetadata(self.tempdir, self.config.type) | 
| 202 self.version = packager.getBuildVersion(self.tempdir, metadata, False, s elf.revision) | 207 self.version = packager.getBuildVersion(self.tempdir, metadata, False, | 
| 208 self.buildNum) | |
| 203 self.basename = metadata.get('general', 'basename') | 209 self.basename = metadata.get('general', 'basename') | 
| 204 | 210 | 
| 205 self.compat = [] | 211 self.compat = [] | 
| 206 if metadata.has_section('compat') and metadata.has_option('compat', 'chr ome'): | 212 if metadata.has_section('compat') and metadata.has_option('compat', 'chr ome'): | 
| 207 self.compat.append({'id': 'chrome', 'minVersion': metadata.get('comp at', 'chrome')}) | 213 self.compat.append({'id': 'chrome', 'minVersion': metadata.get('comp at', 'chrome')}) | 
| 208 | 214 | 
| 209 def readSafariMetadata(self): | 215 def readSafariMetadata(self): | 
| 210 import buildtools.packagerSafari as packager | 216 import buildtools.packagerSafari as packager | 
| 211 metadata = packager.readMetadata(self.tempdir, self.config.type) | 217 metadata = packager.readMetadata(self.tempdir, self.config.type) | 
| 212 certs = packager.get_certificates_and_key(self.config.keyFile)[0] | 218 certs = packager.get_certificates_and_key(self.config.keyFile)[0] | 
| 213 | 219 | 
| 214 self.certificateID = packager.get_developer_identifier(certs) | 220 self.certificateID = packager.get_developer_identifier(certs) | 
| 215 self.version = packager.getBuildVersion(self.tempdir, metadata, False, s elf.revision) | 221 self.version = packager.getBuildVersion(self.tempdir, metadata, False, | 
| 222 self.buildNum) | |
| 216 self.shortVersion = metadata.get('general', 'version') | 223 self.shortVersion = metadata.get('general', 'version') | 
| 217 self.basename = metadata.get('general', 'basename') | 224 self.basename = metadata.get('general', 'basename') | 
| 218 self.updatedFromGallery = False | 225 self.updatedFromGallery = False | 
| 219 | 226 | 
| 220 def writeUpdateManifest(self): | 227 def writeUpdateManifest(self): | 
| 221 """ | 228 """ | 
| 222 Writes update.rdf file for the current build | 229 Writes update.rdf file for the current build | 
| 223 """ | 230 """ | 
| 224 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) | 231 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) | 
| 225 if self.config.type == 'safari': | 232 if self.config.type == 'safari': | 
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 291 | 298 | 
| 292 if self.config.type == 'android': | 299 if self.config.type == 'android': | 
| 293 apkFile = open(self.path, 'wb') | 300 apkFile = open(self.path, 'wb') | 
| 294 | 301 | 
| 295 try: | 302 try: | 
| 296 try: | 303 try: | 
| 297 port = get_config().get('extensions', 'androidBuildPort') | 304 port = get_config().get('extensions', 'androidBuildPort') | 
| 298 except ConfigParser.NoOptionError: | 305 except ConfigParser.NoOptionError: | 
| 299 port = '22' | 306 port = '22' | 
| 300 buildCommand = ['ssh', '-p', port, get_config().get('extensions' , 'androidBuildHost')] | 307 buildCommand = ['ssh', '-p', port, get_config().get('extensions' , 'androidBuildHost')] | 
| 301 buildCommand.extend(map(pipes.quote, ['/home/android/bin/makedeb ugbuild.py', '--revision', self.revision, '--version', self.version, '--stdout'] )) | 308 buildCommand.extend(map(pipes.quote, [ | 
| 309 '/home/android/bin/makedebugbuild.py', '--revision', | |
| 310 self.buildNum, '--version', self.version, '--stdout' | |
| 311 ])) | |
| 302 subprocess.check_call(buildCommand, stdout=apkFile, close_fds=Tr ue) | 312 subprocess.check_call(buildCommand, stdout=apkFile, close_fds=Tr ue) | 
| 303 except: | 313 except: | 
| 304 # clear broken output if any | 314 # clear broken output if any | 
| 305 if os.path.exists(self.path): | 315 if os.path.exists(self.path): | 
| 306 os.remove(self.path) | 316 os.remove(self.path) | 
| 307 raise | 317 raise | 
| 308 elif self.config.type == 'chrome': | |
| 309 import buildtools.packagerChrome as packager | |
| 310 packager.createBuild(self.tempdir, type=self.config.type, outFile=se lf.path, buildNum=self.revision, keyFile=self.config.keyFile) | |
| 311 elif self.config.type == 'safari': | |
| 312 import buildtools.packagerSafari as packager | |
| 313 packager.createBuild(self.tempdir, type=self.config.type, outFile=se lf.path, buildNum=self.revision, keyFile=self.config.keyFile) | |
| 314 else: | 318 else: | 
| 315 import buildtools.packagerGecko as packager | 319 buildCommand = [ | 
| 316 packager.createBuild(self.tempdir, outFile=self.path, buildNum=self. revision, keyFile=self.config.keyFile) | 320 os.path.join(self.tempdir, 'build.py'), '-t', self.config.type, | 
| 321 'build', '-b', self.buildNum, '-k', self.config.keyFile, | |
| 322 self.path | |
| 323 ] | |
| 324 subprocess.check_call(buildCommand) | |
| 317 | 325 | 
| 318 if not os.path.exists(self.path): | 326 if not os.path.exists(self.path): | 
| 319 raise Exception("Build failed, output file hasn't been created") | 327 raise Exception("Build failed, output file hasn't been created") | 
| 320 | 328 | 
| 321 linkPath = os.path.join(baseDir, '00latest%s' % self.config.packageSuffi x) | 329 linkPath = os.path.join(baseDir, '00latest%s' % self.config.packageSuffi x) | 
| 322 if hasattr(os, 'symlink'): | 330 if hasattr(os, 'symlink'): | 
| 323 if os.path.exists(linkPath): | 331 if os.path.exists(linkPath): | 
| 324 os.remove(linkPath) | 332 os.remove(linkPath) | 
| 325 os.symlink(os.path.basename(self.path), linkPath) | 333 os.symlink(os.path.basename(self.path), linkPath) | 
| 326 else: | 334 else: | 
| (...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 548 Run the nightly build process for one extension | 556 Run the nightly build process for one extension | 
| 549 """ | 557 """ | 
| 550 try: | 558 try: | 
| 551 if self.config.type == 'ie': | 559 if self.config.type == 'ie': | 
| 552 # We cannot build IE builds, simply list the builds already in | 560 # We cannot build IE builds, simply list the builds already in | 
| 553 # the directory. Basename has to be deduced from the repository name. | 561 # the directory. Basename has to be deduced from the repository name. | 
| 554 self.basename = os.path.basename(self.config.repository) | 562 self.basename = os.path.basename(self.config.repository) | 
| 555 else: | 563 else: | 
| 556 # copy the repository into a temporary directory | 564 # copy the repository into a temporary directory | 
| 557 self.copyRepository() | 565 self.copyRepository() | 
| 566 self.buildNum = self.getCurrentBuild() | |
| 558 | 567 | 
| 559 # get meta data from the repository | 568 # get meta data from the repository | 
| 560 if self.config.type == 'android': | 569 if self.config.type == 'android': | 
| 561 self.readAndroidMetadata() | 570 self.readAndroidMetadata() | 
| 562 elif self.config.type == 'chrome': | 571 elif self.config.type == 'chrome': | 
| 563 self.readChromeMetadata() | 572 self.readChromeMetadata() | 
| 564 elif self.config.type == 'safari': | 573 elif self.config.type == 'safari': | 
| 565 self.readSafariMetadata() | 574 self.readSafariMetadata() | 
| 566 else: | 575 else: | 
| 567 self.readGeckoMetadata() | 576 self.readGeckoMetadata() | 
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 619 except Exception, ex: | 628 except Exception, ex: | 
| 620 logging.error('The build for %s failed:', repo) | 629 logging.error('The build for %s failed:', repo) | 
| 621 logging.exception(ex) | 630 logging.exception(ex) | 
| 622 | 631 | 
| 623 file = open(nightlyConfigFile, 'wb') | 632 file = open(nightlyConfigFile, 'wb') | 
| 624 nightlyConfig.write(file) | 633 nightlyConfig.write(file) | 
| 625 | 634 | 
| 626 | 635 | 
| 627 if __name__ == '__main__': | 636 if __name__ == '__main__': | 
| 628 main() | 637 main() | 
| OLD | NEW |