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

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

Issue 29345508: Issue 4098 - Get rid of special build setup for development builds (Closed)
Patch Set: Created June 1, 2016, 2:47 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
OLDNEW
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
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
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
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
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()
OLDNEW

Powered by Google App Engine
This is Rietveld