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 | |
Wladimir Palant
2016/06/01 14:57:12
With these changes, self.revision is a revision ha
Vasily Kuznetsov
2016/06/01 17:14:15
Acknowledged.
| |
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=', |
Vasily Kuznetsov
2016/06/01 17:14:15
This `--config defaults.id=` is there to make sure
Wladimir Palant
2016/06/03 14:14:05
"Breaking automation is only one of the reason to
Vasily Kuznetsov
2016/06/03 16:04:49
Acknowledged.
| |
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 = [os.path.join(self.tempdir, 'ensure_dependencies.py'), '-q'] |
117 logging.disable(logging.WARNING) | 126 if os.path.isfile(command[0]): |
Vasily Kuznetsov
2016/06/01 17:14:15
Are we sure that `ensure_dependecies.py` is always
Wladimir Palant
2016/06/03 14:14:06
You are right, build.py calls Python explicitly so
| |
118 try: | 127 subprocess.check_call(command) |
119 from buildtools.ensure_dependencies import resolve_deps | |
120 resolve_deps(self.tempdir, self_update=False, | |
121 overrideroots={'hg': os.path.abspath( | |
122 os.path.join(self.config.repository, os.pardir) | |
123 )}, | |
124 skipdependencies={'buildtools'}) | |
125 finally: | |
126 logging.disable(logging.NOTSET) | |
127 | 128 |
128 def writeChangelog(self, changes): | 129 def writeChangelog(self, changes): |
129 """ | 130 """ |
130 write the changelog file into the cloned repository | 131 write the changelog file into the cloned repository |
131 """ | 132 """ |
132 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) | 133 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) |
133 if not os.path.exists(baseDir): | 134 if not os.path.exists(baseDir): |
134 os.makedirs(baseDir) | 135 os.makedirs(baseDir) |
135 changelogFile = '%s-%s.changelog.xhtml' % (self.basename, self.version) | 136 changelogFile = '%s-%s.changelog.xhtml' % (self.basename, self.version) |
136 changelogPath = os.path.join(baseDir, changelogFile) | 137 changelogPath = os.path.join(baseDir, changelogFile) |
137 self.changelogURL = urlparse.urljoin(self.config.nightliesURL, self.base name + '/' + changelogFile) | 138 self.changelogURL = urlparse.urljoin(self.config.nightliesURL, self.base name + '/' + changelogFile) |
138 | 139 |
139 template = get_template(get_config().get('extensions', 'changelogTemplat e')) | 140 template = get_template(get_config().get('extensions', 'changelogTemplat e')) |
140 template.stream({'changes': changes}).dump(changelogPath, encoding='utf- 8') | 141 template.stream({'changes': changes}).dump(changelogPath, encoding='utf- 8') |
141 | 142 |
142 linkPath = os.path.join(baseDir, '00latest.changelog.xhtml') | 143 linkPath = os.path.join(baseDir, '00latest.changelog.xhtml') |
143 if hasattr(os, 'symlink'): | 144 if hasattr(os, 'symlink'): |
144 if os.path.exists(linkPath): | 145 if os.path.exists(linkPath): |
145 os.remove(linkPath) | 146 os.remove(linkPath) |
146 os.symlink(os.path.basename(changelogPath), linkPath) | 147 os.symlink(os.path.basename(changelogPath), linkPath) |
147 else: | 148 else: |
148 shutil.copyfile(changelogPath, linkPath) | 149 shutil.copyfile(changelogPath, linkPath) |
149 | 150 |
150 def readGeckoMetadata(self): | 151 def readGeckoMetadata(self): |
151 """ | 152 """ |
152 read Gecko-specific metadata file from a cloned repository | 153 read Gecko-specific metadata file from a cloned repository |
153 and parse id, version, basename and the compat section | 154 and parse id, version, basename and the compat section |
154 out of the file | 155 out of the file |
155 """ | 156 """ |
156 import buildtools.packagerGecko as packager | 157 import buildtools.packagerGecko as packager |
Wladimir Palant
2016/06/01 14:57:12
We are still using latest buildtools revision to r
Vasily Kuznetsov
2016/06/01 17:14:15
Acknowledged.
| |
157 metadata = packager.readMetadata(self.tempdir, self.config.type) | 158 metadata = packager.readMetadata(self.tempdir, self.config.type) |
158 self.extensionID = metadata.get('general', 'id') | 159 self.extensionID = metadata.get('general', 'id') |
159 self.version = packager.getBuildVersion(self.tempdir, metadata, False, s elf.revision) | 160 self.version = packager.getBuildVersion(self.tempdir, metadata, False, s elf.buildNum) |
Vasily Kuznetsov
2016/06/01 17:14:15
This line is too long, although of course, it's no
Wladimir Palant
2016/06/03 14:14:04
This add too many unrelated changes for my taste b
| |
160 self.basename = metadata.get('general', 'basename') | 161 self.basename = metadata.get('general', 'basename') |
161 self.compat = [] | 162 self.compat = [] |
162 for key, value in packager.KNOWN_APPS.iteritems(): | 163 for key, value in packager.KNOWN_APPS.iteritems(): |
163 if metadata.has_option('compat', key): | 164 if metadata.has_option('compat', key): |
164 minVersion, maxVersion = metadata.get('compat', key).split('/') | 165 minVersion, maxVersion = metadata.get('compat', key).split('/') |
165 self.compat.append({'id': value, 'minVersion': minVersion, 'maxV ersion': maxVersion}) | 166 self.compat.append({'id': value, 'minVersion': minVersion, 'maxV ersion': maxVersion}) |
166 | 167 |
167 def readAndroidMetadata(self): | 168 def readAndroidMetadata(self): |
168 """ | 169 """ |
169 Read Android-specific metadata from AndroidManifest.xml file. | 170 Read Android-specific metadata from AndroidManifest.xml file. |
170 """ | 171 """ |
171 manifestFile = open(os.path.join(self.tempdir, 'AndroidManifest.xml'), ' r') | 172 manifestFile = open(os.path.join(self.tempdir, 'AndroidManifest.xml'), ' r') |
172 manifest = parseXml(manifestFile) | 173 manifest = parseXml(manifestFile) |
173 manifestFile.close() | 174 manifestFile.close() |
174 | 175 |
175 root = manifest.documentElement | 176 root = manifest.documentElement |
176 self.version = root.attributes['android:versionName'].value | 177 self.version = root.attributes['android:versionName'].value |
177 while self.version.count('.') < 2: | 178 while self.version.count('.') < 2: |
178 self.version += '.0' | 179 self.version += '.0' |
179 self.version = '%s.%s' % (self.version, self.revision) | 180 self.version = '%s.%s' % (self.version, self.buildNum) |
180 | 181 |
181 usesSdk = manifest.getElementsByTagName('uses-sdk')[0] | 182 usesSdk = manifest.getElementsByTagName('uses-sdk')[0] |
182 self.minSdkVersion = usesSdk.attributes['android:minSdkVersion'].value | 183 self.minSdkVersion = usesSdk.attributes['android:minSdkVersion'].value |
183 self.basename = os.path.basename(self.config.repository) | 184 self.basename = os.path.basename(self.config.repository) |
184 | 185 |
185 def readChromeMetadata(self): | 186 def readChromeMetadata(self): |
186 """ | 187 """ |
187 Read Chrome-specific metadata from metadata file. This will also | 188 Read Chrome-specific metadata from metadata file. This will also |
188 calculate extension ID from the private key. | 189 calculate extension ID from the private key. |
189 """ | 190 """ |
190 | 191 |
191 # Calculate extension ID from public key | 192 # Calculate extension ID from public key |
192 # (see http://supercollider.dk/2010/01/calculating-chrome-extension-id-f rom-your-private-key-233) | 193 # (see http://supercollider.dk/2010/01/calculating-chrome-extension-id-f rom-your-private-key-233) |
193 import buildtools.packagerChrome as packager | 194 import buildtools.packagerChrome as packager |
194 publicKey = packager.getPublicKey(self.config.keyFile) | 195 publicKey = packager.getPublicKey(self.config.keyFile) |
195 hash = hashlib.sha256() | 196 hash = hashlib.sha256() |
196 hash.update(publicKey) | 197 hash.update(publicKey) |
197 self.extensionID = hash.hexdigest()[0:32] | 198 self.extensionID = hash.hexdigest()[0:32] |
198 self.extensionID = ''.join(map(lambda c: chr(97 + int(c, 16)), self.exte nsionID)) | 199 self.extensionID = ''.join(map(lambda c: chr(97 + int(c, 16)), self.exte nsionID)) |
199 | 200 |
200 # Now read metadata file | 201 # Now read metadata file |
201 metadata = packager.readMetadata(self.tempdir, self.config.type) | 202 metadata = packager.readMetadata(self.tempdir, self.config.type) |
202 self.version = packager.getBuildVersion(self.tempdir, metadata, False, s elf.revision) | 203 self.version = packager.getBuildVersion(self.tempdir, metadata, False, s elf.buildNum) |
203 self.basename = metadata.get('general', 'basename') | 204 self.basename = metadata.get('general', 'basename') |
204 | 205 |
205 self.compat = [] | 206 self.compat = [] |
206 if metadata.has_section('compat') and metadata.has_option('compat', 'chr ome'): | 207 if metadata.has_section('compat') and metadata.has_option('compat', 'chr ome'): |
207 self.compat.append({'id': 'chrome', 'minVersion': metadata.get('comp at', 'chrome')}) | 208 self.compat.append({'id': 'chrome', 'minVersion': metadata.get('comp at', 'chrome')}) |
208 | 209 |
209 def readSafariMetadata(self): | 210 def readSafariMetadata(self): |
210 import buildtools.packagerSafari as packager | 211 import buildtools.packagerSafari as packager |
211 metadata = packager.readMetadata(self.tempdir, self.config.type) | 212 metadata = packager.readMetadata(self.tempdir, self.config.type) |
212 certs = packager.get_certificates_and_key(self.config.keyFile)[0] | 213 certs = packager.get_certificates_and_key(self.config.keyFile)[0] |
213 | 214 |
214 self.certificateID = packager.get_developer_identifier(certs) | 215 self.certificateID = packager.get_developer_identifier(certs) |
215 self.version = packager.getBuildVersion(self.tempdir, metadata, False, s elf.revision) | 216 self.version = packager.getBuildVersion(self.tempdir, metadata, False, s elf.buildNum) |
216 self.shortVersion = metadata.get('general', 'version') | 217 self.shortVersion = metadata.get('general', 'version') |
217 self.basename = metadata.get('general', 'basename') | 218 self.basename = metadata.get('general', 'basename') |
218 self.updatedFromGallery = False | 219 self.updatedFromGallery = False |
219 | 220 |
220 def writeUpdateManifest(self): | 221 def writeUpdateManifest(self): |
221 """ | 222 """ |
222 Writes update.rdf file for the current build | 223 Writes update.rdf file for the current build |
223 """ | 224 """ |
224 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) | 225 baseDir = os.path.join(self.config.nightliesDirectory, self.basename) |
225 if self.config.type == 'safari': | 226 if self.config.type == 'safari': |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
291 | 292 |
292 if self.config.type == 'android': | 293 if self.config.type == 'android': |
293 apkFile = open(self.path, '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' , 'androidBuildHost')] | 301 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'] )) | 302 buildCommand.extend(map(pipes.quote, ['/home/android/bin/makedeb ugbuild.py', '--revision', self.buildNum, '--version', self.version, '--stdout'] )) |
302 subprocess.check_call(buildCommand, stdout=apkFile, close_fds=Tr ue) | 303 subprocess.check_call(buildCommand, stdout=apkFile, close_fds=Tr ue) |
303 except: | 304 except: |
304 # clear broken output if any | 305 # clear broken output if any |
305 if os.path.exists(self.path): | 306 if os.path.exists(self.path): |
306 os.remove(self.path) | 307 os.remove(self.path) |
307 raise | 308 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: | 309 else: |
315 import buildtools.packagerGecko as packager | 310 buildCommand = [ |
Vasily Kuznetsov
2016/06/01 17:14:15
A nice side effect that we got rid of this branchi
Wladimir Palant
2016/06/03 14:14:04
It will come back, as soon as we remove signing su
Vasily Kuznetsov
2016/06/03 16:04:49
:)
| |
316 packager.createBuild(self.tempdir, outFile=self.path, buildNum=self. revision, keyFile=self.config.keyFile) | 311 os.path.join(self.tempdir, 'build.py'), '-t', self.config.type, |
312 'build', '-b', self.buildNum, '-k', self.config.keyFile, | |
313 self.path | |
314 ] | |
315 subprocess.check_call(buildCommand) | |
317 | 316 |
318 if not os.path.exists(self.path): | 317 if not os.path.exists(self.path): |
319 raise Exception("Build failed, output file hasn't been created") | 318 raise Exception("Build failed, output file hasn't been created") |
320 | 319 |
321 linkPath = os.path.join(baseDir, '00latest%s' % self.config.packageSuffi x) | 320 linkPath = os.path.join(baseDir, '00latest%s' % self.config.packageSuffi x) |
322 if hasattr(os, 'symlink'): | 321 if hasattr(os, 'symlink'): |
323 if os.path.exists(linkPath): | 322 if os.path.exists(linkPath): |
324 os.remove(linkPath) | 323 os.remove(linkPath) |
325 os.symlink(os.path.basename(self.path), linkPath) | 324 os.symlink(os.path.basename(self.path), linkPath) |
326 else: | 325 else: |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
548 Run the nightly build process for one extension | 547 Run the nightly build process for one extension |
549 """ | 548 """ |
550 try: | 549 try: |
551 if self.config.type == 'ie': | 550 if self.config.type == 'ie': |
552 # We cannot build IE builds, simply list the builds already in | 551 # We cannot build IE builds, simply list the builds already in |
553 # the directory. Basename has to be deduced from the repository name. | 552 # the directory. Basename has to be deduced from the repository name. |
554 self.basename = os.path.basename(self.config.repository) | 553 self.basename = os.path.basename(self.config.repository) |
555 else: | 554 else: |
556 # copy the repository into a temporary directory | 555 # copy the repository into a temporary directory |
557 self.copyRepository() | 556 self.copyRepository() |
557 self.buildNum = self.getCurrentBuild() | |
558 | 558 |
559 # get meta data from the repository | 559 # get meta data from the repository |
560 if self.config.type == 'android': | 560 if self.config.type == 'android': |
561 self.readAndroidMetadata() | 561 self.readAndroidMetadata() |
562 elif self.config.type == 'chrome': | 562 elif self.config.type == 'chrome': |
563 self.readChromeMetadata() | 563 self.readChromeMetadata() |
564 elif self.config.type == 'safari': | 564 elif self.config.type == 'safari': |
565 self.readSafariMetadata() | 565 self.readSafariMetadata() |
566 else: | 566 else: |
567 self.readGeckoMetadata() | 567 self.readGeckoMetadata() |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
619 except Exception, ex: | 619 except Exception, ex: |
620 logging.error('The build for %s failed:', repo) | 620 logging.error('The build for %s failed:', repo) |
621 logging.exception(ex) | 621 logging.exception(ex) |
622 | 622 |
623 file = open(nightlyConfigFile, 'wb') | 623 file = open(nightlyConfigFile, 'wb') |
624 nightlyConfig.write(file) | 624 nightlyConfig.write(file) |
625 | 625 |
626 | 626 |
627 if __name__ == '__main__': | 627 if __name__ == '__main__': |
628 main() | 628 main() |
OLD | NEW |