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

Side by Side Diff: build.py

Issue 29609559: Issue 6021 - Refactoring build.py (Closed) Base URL: https://hg.adblockplus.org/buildtools/file/79688f4a4aff
Patch Set: Addressing Wladimir's comments Created Nov. 17, 2017, 11:30 a.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 | « no previous file | tests/test_packagerWebExt.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 # This Source Code Form is subject to the terms of the Mozilla Public 1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 4
5 from __future__ import print_function
6
7 import argparse
5 import os 8 import os
6 import sys 9 import sys
7 import re 10 import re
8 import subprocess 11 import subprocess
9 import shutil 12 import shutil
10 from getopt import getopt, GetoptError 13 from functools import partial
11 from StringIO import StringIO 14 from StringIO import StringIO
12 from zipfile import ZipFile 15 from zipfile import ZipFile
13 16
14 knownTypes = {'gecko', 'chrome', 'generic', 'edge'} 17 KNOWN_PLATFORMS = {'gecko', 'chrome', 'generic', 'edge'}
18
19 MAIN_PARSER = argparse.ArgumentParser(
20 description=__doc__,
21 formatter_class=argparse.RawDescriptionHelpFormatter)
22
23 SUB_PARSERS = MAIN_PARSER.add_subparsers(title='Commands', dest='action',
24 metavar='[command]')
15 25
16 26
17 class Command(object): 27 def make_argument(*args, **kwargs):
18 name = property(lambda self: self._name) 28 def _make_argument(*args, **kwargs):
19 shortDescription = property( 29 parser = kwargs.pop('parser')
20 lambda self: self._shortDescription, 30 parser.add_argument(*args, **kwargs)
21 lambda self, value: self.__dict__.update({'_shortDescription': value})
22 )
23 description = property(
24 lambda self: self._description,
25 lambda self, value: self.__dict__.update({'_description': value})
26 )
27 params = property(
28 lambda self: self._params,
29 lambda self, value: self.__dict__.update({'_params': value})
30 )
31 supportedTypes = property(
32 lambda self: self._supportedTypes,
33 lambda self, value: self.__dict__.update({'_supportedTypes': value})
34 )
35 options = property(lambda self: self._options)
36 31
37 def __init__(self, handler, name): 32 return partial(_make_argument, *args, **kwargs)
38 self._handler = handler
39 self._name = name
40 self._shortDescription = ''
41 self._description = ''
42 self._params = ''
43 self._supportedTypes = None
44 self._options = []
45 self.addOption('Show this message and exit', short='h', long='help')
46
47 def __enter__(self):
48 return self
49
50 def __exit__(self, exc_type, exc_value, traceback):
51 pass
52
53 def __call__(self, baseDir, scriptName, opts, args, type):
54 return self._handler(baseDir, scriptName, opts, args, type)
55
56 def isSupported(self, type):
57 return self._supportedTypes == None or type in self._supportedTypes
58
59 def addOption(self, description, short=None, long=None, value=None, types=No ne):
60 self._options.append((description, short, long, value, types))
61
62 def parseArgs(self, type, args):
63 shortOptions = map(
64 lambda o: o[1] + ':' if o[3] != None else o[1],
65 filter(
66 lambda o: o[1] != None and (o[4] == None or type in o[4]),
67 self._options
68 )
69 )
70 longOptions = map(
71 lambda o: o[2] + '=' if o[3] != None else o[2],
72 filter(
73 lambda o: o[2] != None and (o[4] == None or type in o[4]),
74 self._options
75 )
76 )
77 return getopt(args, ''.join(shortOptions), longOptions)
78 33
79 34
80 commandsList = [] 35 def argparse_command(valid_platforms=None, arguments=()):
81 commands = {} 36 def wrapper(func):
37 def func_wrapper(*args, **kwargs):
38 return func(*args, **kwargs)
39
40 short_desc, long_desc = func.__doc__.split(os.linesep + os.linesep, 1)
41
42 new_parser = SUB_PARSERS.add_parser(
43 func.__name__,
44 description=long_desc,
45 formatter_class=argparse.RawDescriptionHelpFormatter,
46 help=short_desc,
47 )
48
49 new_parser.__valid_platforms = valid_platforms or KNOWN_PLATFORMS
50
51 for argument in arguments:
52 argument(parser=new_parser)
53
54 new_parser.set_defaults(function=func)
55 return func_wrapper
56
57 return wrapper
82 58
83 59
84 def addCommand(handler, name): 60 def collect_platforms(base_dir):
85 if isinstance(name, basestring): 61 """Construct valid platforms for each subcommand.
86 aliases = ()
87 else:
88 name, aliases = (name[0], name[1:])
89 62
90 global commandsList, commands 63 Search 'base_dir' for existing metadata.<type> files and make <type> an
91 command = Command(handler, name) 64 avaible choice for the subcommand, intersected with their respective valid
92 commandsList.append(command) 65 platforms.
93 commands[name] = command 66 """
94 for alias in aliases: 67 if collect_platforms._result is not None:
95 commands[alias] = command 68 # Tests might run this code multiple times, make sure the collection
96 return command 69 # of platforms is only run once.
70 return collect_platforms._result
71
72 types = set()
73 for p in KNOWN_PLATFORMS:
74 if os.path.exists(os.path.join(base_dir, 'metadata.' + p)):
75 types.add(p)
76
77 if len(types) == 0:
78 print('No metadata file found in this repository, a metadata file '
79 'metadata.* is required.', file=sys.stderr)
Wladimir Palant 2017/11/17 11:51:58 This message is confusing now, with only known pla
tlucas 2017/11/17 13:32:04 Done.
80 collect_platforms._result = False
81 return False
82
83 for _, sub_parser in SUB_PARSERS.choices.items():
Wladimir Palant 2017/11/17 11:51:58 If you don't need the keys, why iterate over items
tlucas 2017/11/17 13:32:04 This will be obsolete with addressing the below co
84 platforms = types.intersection(sub_parser.__valid_platforms)
85 if len(platforms) > 1:
86 sub_parser.add_argument('-t', '--type', dest='platform',
87 choices=platforms)
88 else:
89 sub_parser.set_defaults(platform=platforms.pop())
Wladimir Palant 2017/11/17 11:51:58 What about len(platforms) == 0?
tlucas 2017/11/17 13:32:04 This was not trivial - but i came up with a soluti
90
91 collect_platforms._result = True
92 return True
97 93
98 94
99 def splitByLength(string, maxLen): 95 collect_platforms._result = None
tlucas 2017/11/17 11:36:45 I did not see the necessity to introduce a fully f
100 parts = []
101 currentPart = ''
102 for match in re.finditer(r'\s*(\S+)', string):
103 if len(match.group(0)) + len(currentPart) < maxLen:
104 currentPart += match.group(0)
105 else:
106 parts.append(currentPart)
107 currentPart = match.group(1)
108 if len(currentPart):
109 parts.append(currentPart)
110 return parts
111 96
112 97
113 def usage(scriptName, type, commandName=None): 98 @argparse_command(
114 if commandName == None: 99 valid_platforms={'gecko', 'chrome', 'edge'},
115 global commandsList 100 arguments=(
116 descriptions = [] 101 make_argument(
117 for command in commandsList: 102 '-b', '--build-num', dest='build_num',
118 if not command.isSupported(type): 103 help=('Use given build number (if omitted the build number will '
119 continue 104 'be retrieved from Mercurial)')),
120 commandText = ('%s %s' % (command.name, command.params)).ljust(39) 105 make_argument(
121 descriptionParts = splitByLength(command.shortDescription, 29) 106 '-k', '--key', dest='key_file',
122 descriptions.append(' %s [-t %s] %s %s' % (scriptName, type, comman dText, descriptionParts[0])) 107 help=('File containing private key and certificates required to '
123 for part in descriptionParts[1:]: 108 'sign the package')),
124 descriptions.append(' %s %s %s %s' % (' ' * len(scriptName ), ' ' * len(type), ' ' * len(commandText), part)) 109 make_argument(
125 print '''Usage: 110 '-r', '--release', action='store_true',
111 help='Create a release build'),
112 make_argument('output_file', nargs='?')
113 )
114 )
115 def build(base_dir, build_num, key_file, release, output_file, platform,
116 **kwargs):
117 """
118 Create a build.
126 119
127 %(descriptions)s 120 Creates an extension build with given file name. If output_file is missing
121 a default name will be chosen.
122 """
123 kwargs = {}
124 if platform == 'edge':
125 import buildtools.packagerEdge as packager
126 else:
127 import buildtools.packagerChrome as packager
128 128
129 For details on a command run: 129 kwargs['keyFile'] = key_file
130 kwargs['outFile'] = output_file
131 kwargs['releaseBuild'] = release
132 kwargs['buildNum'] = build_num
130 133
131 %(scriptName)s [-t %(type)s] <command> --help 134 packager.createBuild(base_dir, type=platform, **kwargs)
132 ''' % {
133 'scriptName': scriptName,
134 'type': type,
135 'descriptions': '\n'.join(descriptions)
136 }
137 else:
138 global commands
139 command = commands[commandName]
140 description = '\n'.join(map(lambda s: '\n'.join(splitByLength(s, 80)), c ommand.description.split('\n')))
141 options = []
142 for descr, short, long, value, types in command.options:
143 if types != None and type not in types:
144 continue
145 if short == None:
146 shortText = ''
147 elif value == None:
148 shortText = '-%s' % short
149 else:
150 shortText = '-%s %s' % (short, value)
151 if long == None:
152 longText = ''
153 elif value == None:
154 longText = '--%s' % long
155 else:
156 longText = '--%s=%s' % (long, value)
157 descrParts = splitByLength(descr, 46)
158 options.append(' %s %s %s' % (shortText.ljust(11), longText.ljust(1 9), descrParts[0]))
159 for part in descrParts[1:]:
160 options.append(' %s %s %s' % (' ' * 11, ' ' * 19, part))
161 print '''%(scriptName)s [-t %(type)s] %(name)s %(params)s
162
163 %(description)s
164
165 Options:
166 %(options)s
167 ''' % {
168 'scriptName': scriptName,
169 'type': type,
170 'name': command.name,
171 'params': command.params,
172 'description': description,
173 'options': '\n'.join(options)
174 }
175 135
176 136
177 def runBuild(baseDir, scriptName, opts, args, type): 137 @argparse_command(
178 kwargs = {} 138 valid_platforms={'chrome', 'gecko', 'edge'}
179 for option, value in opts: 139 )
180 if option in {'-b', '--build'}: 140 def devenv(base_dir, platform, **kwargs):
181 kwargs['buildNum'] = value 141 """
182 if type != 'gecko' and not kwargs['buildNum'].isdigit(): 142 Set up a development environment.
183 raise TypeError('Build number must be numerical')
184 elif option in {'-k', '--key'}:
185 kwargs['keyFile'] = value
186 elif option in {'-r', '--release'}:
187 kwargs['releaseBuild'] = True
188 if len(args) > 0:
189 kwargs['outFile'] = args[0]
190 143
191 if type in {'chrome', 'gecko'}: 144 Will set up or update the devenv folder as an unpacked extension folder '
192 import buildtools.packagerChrome as packager 145 for development.
193 elif type == 'edge': 146 """
194 import buildtools.packagerEdge as packager 147 if platform == 'edge':
195
196 packager.createBuild(baseDir, type=type, **kwargs)
197
198
199 def createDevEnv(baseDir, scriptName, opts, args, type):
200 if type == 'edge':
201 import buildtools.packagerEdge as packager 148 import buildtools.packagerEdge as packager
202 else: 149 else:
203 import buildtools.packagerChrome as packager 150 import buildtools.packagerChrome as packager
204 151
205 file = StringIO() 152 file = StringIO()
206 packager.createBuild(baseDir, type=type, outFile=file, devenv=True, releaseB uild=True) 153 packager.createBuild(base_dir, type=platform, outFile=file, devenv=True,
154 releaseBuild=True)
207 155
208 from buildtools.packager import getDevEnvPath 156 from buildtools.packager import getDevEnvPath
209 devenv_dir = getDevEnvPath(baseDir, type) 157 devenv_dir = getDevEnvPath(base_dir, platform)
210 158
211 shutil.rmtree(devenv_dir, ignore_errors=True) 159 shutil.rmtree(devenv_dir, ignore_errors=True)
212 160
213 file.seek(0) 161 file.seek(0)
214 with ZipFile(file, 'r') as zip_file: 162 with ZipFile(file, 'r') as zip_file:
215 zip_file.extractall(devenv_dir) 163 zip_file.extractall(devenv_dir)
216 164
217 165
218 def readLocaleConfig(baseDir, type, metadata): 166 def read_locale_config(base_dir, platform, metadata):
219 if type != 'generic': 167 if platform != 'generic':
220 import buildtools.packagerChrome as packager 168 import buildtools.packagerChrome as packager
221 localeDir = os.path.join(baseDir, '_locales') 169 locale_dir = os.path.join(base_dir, '_locales')
222 localeConfig = { 170 locale_config = {
223 'default_locale': packager.defaultLocale, 171 'default_locale': packager.defaultLocale,
224 } 172 }
225 else: 173 else:
226 localeDir = os.path.join( 174 locale_dir = os.path.join(
227 baseDir, *metadata.get('locales', 'base_path').split('/') 175 base_dir, *metadata.get('locales', 'base_path').split('/')
228 ) 176 )
229 localeConfig = { 177 locale_config = {
230 'default_locale': metadata.get('locales', 'default_locale') 178 'default_locale': metadata.get('locales', 'default_locale')
231 } 179 }
232 180
233 localeConfig['base_path'] = localeDir 181 locale_config['base_path'] = locale_dir
234 182
235 locales = [(locale.replace('_', '-'), os.path.join(localeDir, locale)) 183 locales = [(locale.replace('_', '-'), os.path.join(locale_dir, locale))
236 for locale in os.listdir(localeDir)] 184 for locale in os.listdir(locale_dir)]
237 localeConfig['locales'] = dict(locales) 185 locale_config['locales'] = dict(locales)
238 186
239 return localeConfig 187 return locale_config
240 188
241 189
242 def setupTranslations(baseDir, scriptName, opts, args, type): 190 project_key_argument = make_argument(
243 if len(args) < 1: 191 'project_key', help='The crowdin project key.'
244 print 'Project key is required to update translation master files.' 192 )
245 usage(scriptName, type, 'setuptrans')
246 return
247 193
248 key = args[0]
249 194
195 @argparse_command(
196 arguments=(project_key_argument, )
197 )
198 def setuptrans(base_dir, project_key, platform, **kwargs):
199 """
200 Set up translation languages.
201
202 Set up translation languages for the project on crowdin.com.
203 """
250 from buildtools.packager import readMetadata 204 from buildtools.packager import readMetadata
251 metadata = readMetadata(baseDir, type) 205 metadata = readMetadata(base_dir, platform)
252 206
253 basename = metadata.get('general', 'basename') 207 basename = metadata.get('general', 'basename')
254 localeConfig = readLocaleConfig(baseDir, type, metadata) 208 locale_config = read_locale_config(base_dir, platform, metadata)
255 209
256 import buildtools.localeTools as localeTools 210 import buildtools.localeTools as localeTools
257 localeTools.setupTranslations(localeConfig, basename, key) 211 localeTools.setupTranslations(locale_config, basename, project_key)
258 212
259 213
260 def updateTranslationMaster(baseDir, scriptName, opts, args, type): 214 @argparse_command(
261 if len(args) < 1: 215 arguments=(project_key_argument, )
262 print 'Project key is required to update translation master files.' 216 )
263 usage(scriptName, type, 'translate') 217 def translate(base_dir, project_key, platform, **kwargs):
264 return 218 """
219 Update translation master files.
265 220
266 key = args[0] 221 Update the translation master files in the project on crowdin.com.
267 222 """
268 from buildtools.packager import readMetadata 223 from buildtools.packager import readMetadata
269 metadata = readMetadata(baseDir, type) 224 metadata = readMetadata(base_dir, platform)
270 225
271 basename = metadata.get('general', 'basename') 226 basename = metadata.get('general', 'basename')
272 localeConfig = readLocaleConfig(baseDir, type, metadata) 227 locale_config = read_locale_config(base_dir, platform, metadata)
273 228
274 defaultLocaleDir = os.path.join(localeConfig['base_path'], 229 default_locale_dir = os.path.join(locale_config['base_path'],
275 localeConfig['default_locale']) 230 locale_config['default_locale'])
276 231
277 import buildtools.localeTools as localeTools 232 import buildtools.localeTools as localeTools
278 localeTools.updateTranslationMaster(localeConfig, metadata, defaultLocaleDir , 233 localeTools.updateTranslationMaster(locale_config, metadata,
279 basename, key) 234 default_locale_dir, basename,
235 project_key)
280 236
281 237
282 def uploadTranslations(baseDir, scriptName, opts, args, type): 238 @argparse_command(
283 if len(args) < 1: 239 arguments=(project_key_argument, )
284 print 'Project key is required to upload existing translations.' 240 )
285 usage(scriptName, type, 'uploadtrans') 241 def uploadtrans(base_dir, project_key, platform, **kwargs):
286 return 242 """
243 Upload existing translations.
287 244
288 key = args[0] 245 Upload already existing translations to the project on crowdin.com.
289 246 """
290 from buildtools.packager import readMetadata 247 from buildtools.packager import readMetadata
291 metadata = readMetadata(baseDir, type) 248 metadata = readMetadata(base_dir, platform)
292 249
293 basename = metadata.get('general', 'basename') 250 basename = metadata.get('general', 'basename')
294 localeConfig = readLocaleConfig(baseDir, type, metadata) 251 locale_config = read_locale_config(base_dir, platform, metadata)
295 252
296 import buildtools.localeTools as localeTools 253 import buildtools.localeTools as localeTools
297 for locale, localeDir in localeConfig['locales'].iteritems(): 254 for locale, locale_dir in locale_config['locales'].iteritems():
298 if locale != localeConfig['default_locale'].replace('_', '-'): 255 if locale != locale_config['default_locale'].replace('_', '-'):
299 localeTools.uploadTranslations(localeConfig, metadata, localeDir, lo cale, 256 localeTools.uploadTranslations(locale_config, metadata, locale_dir,
300 basename, key) 257 locale, basename, project_key)
301 258
302 259
303 def getTranslations(baseDir, scriptName, opts, args, type): 260 @argparse_command(
304 if len(args) < 1: 261 arguments=(project_key_argument, )
305 print 'Project key is required to update translation master files.' 262 )
306 usage(scriptName, type, 'translate') 263 def gettranslations(base_dir, project_key, platform, **kwargs):
307 return 264 """
265 Download translation updates.
308 266
309 key = args[0] 267 Download updated translations from crowdin.com.
310 268 """
311 from buildtools.packager import readMetadata 269 from buildtools.packager import readMetadata
312 metadata = readMetadata(baseDir, type) 270 metadata = readMetadata(base_dir, platform)
313 271
314 basename = metadata.get('general', 'basename') 272 basename = metadata.get('general', 'basename')
315 localeConfig = readLocaleConfig(baseDir, type, metadata) 273 locale_config = read_locale_config(base_dir, platform, metadata)
316 274
317 import buildtools.localeTools as localeTools 275 import buildtools.localeTools as localeTools
318 localeTools.getTranslations(localeConfig, basename, key) 276 localeTools.getTranslations(locale_config, basename, project_key)
319 277
320 278
321 def generateDocs(baseDir, scriptName, opts, args, type): 279 @argparse_command(
322 if len(args) == 0: 280 valid_platforms={'chrome'},
323 print 'No target directory specified for the documentation' 281 arguments=(
324 usage(scriptName, type, 'docs') 282 make_argument('target_dir'),
325 return 283 make_argument('-q', '--quiet', help='Suppress JsDoc output'),
326 targetDir = args[0] 284 )
285 )
286 def docs(base_dir, target_dir, quiet, platform, **kwargs):
287 """
288 Generate documentation (requires node.js).
327 289
328 source_dir = os.path.join(baseDir, 'lib') 290 Generate documentation files and write them into the specified directory.
329 sources = [source_dir] 291 """
292 source_dir = os.path.join(base_dir, 'lib')
330 293
331 # JSDoc struggles wih huge objects: https://github.com/jsdoc3/jsdoc/issues/9 76 294 # JSDoc struggles wih huge objects:
332 if type == 'chrome': 295 # https://github.com/jsdoc3/jsdoc/issues/976
333 sources = [os.path.join(source_dir, filename) for filename in os.listdir (source_dir) if filename != 'publicSuffixList.js'] 296 sources = [os.path.join(source_dir, filename)
297 for filename in os.listdir(source_dir)
298 if filename != 'publicSuffixList.js']
334 299
335 buildtools_path = os.path.dirname(__file__) 300 buildtools_path = os.path.dirname(__file__)
336 config = os.path.join(buildtools_path, 'jsdoc.conf') 301 config = os.path.join(buildtools_path, 'jsdoc.conf')
337 302
338 command = ['npm', 'run-script', 'jsdoc', '--', '--destination', targetDir, 303 command = ['npm', 'run-script', 'jsdoc', '--', '--destination', target_dir,
339 '--configure', config] + sources 304 '--configure', config] + sources
340 if any(opt in {'-q', '--quiet'} for opt, _ in opts): 305 if quiet:
341 process = subprocess.Popen(command, stdout=subprocess.PIPE, 306 process = subprocess.Popen(command, stdout=subprocess.PIPE,
342 stderr=subprocess.PIPE, cwd=buildtools_path) 307 stderr=subprocess.PIPE, cwd=buildtools_path)
343 stderr = process.communicate()[1] 308 stderr = process.communicate()[1]
344 retcode = process.poll() 309 retcode = process.poll()
345 if retcode: 310 if retcode:
346 sys.stderr.write(stderr) 311 sys.stderr.write(stderr)
347 raise subprocess.CalledProcessError(command, retcode) 312 raise subprocess.CalledProcessError(command, retcode)
348 else: 313 else:
349 subprocess.check_call(command, cwd=buildtools_path) 314 subprocess.check_call(command, cwd=buildtools_path)
350 315
351 316
352 def runReleaseAutomation(baseDir, scriptName, opts, args, type): 317 def valid_version_format(value):
353 keyFile = None 318 if re.search(r'[^\d\.]', value):
354 downloadsRepo = os.path.join(baseDir, '..', 'downloads') 319 raise argparse.ArgumentTypeError('Wrong version number format')
355 for option, value in opts:
356 if option in {'-k', '--key'}:
357 keyFile = value
358 elif option in {'-d', '--downloads'}:
359 downloadsRepo = value
360 320
361 if len(args) == 0: 321 return value
362 print 'No version number specified for the release'
363 usage(scriptName, type, 'release')
364 return
365 version = args[0]
366 if re.search(r'[^\d\.]', version):
367 print 'Wrong version number format'
368 usage(scriptName, type, 'release')
369 return
370 322
371 if type == 'chrome' and keyFile is None: 323
372 print >>sys.stderr, 'Error: you must specify a key file for this release ' 324 @argparse_command(
373 usage(scriptName, type, 'release') 325 valid_platforms={'chrome', 'edge'},
374 return 326 arguments=(
327 make_argument(
328 '-k', '--key', dest='key_file',
329 help=('File containing private key and certificates required to '
330 'sign the release.')),
331 make_argument(
332 '-d', '--downloads-repository', dest='downloads_repository',
333 help=('Directory containing downloads repository (if omitted '
334 '../downloads is assumed)')),
335 make_argument(
336 'version', help='Version number of the release',
337 type=valid_version_format)
338 )
339 )
340 def release(base_dir, downloads_repository, key_file, platform, version,
341 **kwargs):
342 """
343 Run release automation.
344
345 Note: If you are not the project owner then you probably don't want to run
346 this!
347
348 Run release automation: create downloads for the new version, tag source
349 code repository as well as downloads and buildtools repository.
350 """
351 if downloads_repository is None:
352 downloads_repository = os.path.join(base_dir, os.pardir, 'downloads')
353
354 if platform == 'chrome' and key_file is None:
355 raise ValueError('You must specify a key file for this release')
375 356
376 import buildtools.releaseAutomation as releaseAutomation 357 import buildtools.releaseAutomation as releaseAutomation
377 releaseAutomation.run(baseDir, type, version, keyFile, downloadsRepo) 358 releaseAutomation.run(base_dir, platform, version, key_file,
359 downloads_repository)
378 360
379 361
380 def updatePSL(baseDir, scriptName, opts, args, type): 362 @argparse_command(valid_platforms={'chrome'})
363 def updatepsl(base_dir, **kwargs):
364 """Update Public Suffix List.
365
366 Downloads Public Suffix List (see http://publicsuffix.org/) and generates
367 lib/publicSuffixList.js from it.
368 """
381 import buildtools.publicSuffixListUpdater as publicSuffixListUpdater 369 import buildtools.publicSuffixListUpdater as publicSuffixListUpdater
382 publicSuffixListUpdater.updatePSL(baseDir) 370 publicSuffixListUpdater.updatePSL(base_dir)
383 371
384 372
385 with addCommand(lambda baseDir, scriptName, opts, args, type: usage(scriptName, type), ('help', '-h', '--help')) as command: 373 def process_args(base_dir, *args):
386 command.shortDescription = 'Show this message' 374 if collect_platforms(base_dir):
375 MAIN_PARSER.set_defaults(base_dir=base_dir)
387 376
388 with addCommand(runBuild, 'build') as command: 377 # If no args are provided, this module is run directly from the command
389 command.shortDescription = 'Create a build' 378 # line. argparse will take care of consuming sys.argv.
390 command.description = 'Creates an extension build with given file name. If o utput_file is missing a default name will be chosen.' 379 arguments = MAIN_PARSER.parse_args(args if len(args) > 0 else None)
391 command.params = '[options] [output_file]'
392 command.addOption('Use given build number (if omitted the build number will be retrieved from Mercurial)', short='b', long='build', value='num')
393 command.addOption('File containing private key and certificates required to sign the package', short='k', long='key', value='file', types={'chrome'})
394 command.addOption('Create a release build', short='r', long='release')
395 command.supportedTypes = {'gecko', 'chrome', 'edge'}
396 380
397 with addCommand(createDevEnv, 'devenv') as command: 381 function = arguments.function
398 command.shortDescription = 'Set up a development environment' 382 del arguments.function
399 command.description = 'Will set up or update the devenv folder as an unpacke d extension folder for development.' 383 function(**vars(arguments))
400 command.supportedTypes = {'gecko', 'chrome', 'edge'}
401
402 with addCommand(setupTranslations, 'setuptrans') as command:
403 command.shortDescription = 'Sets up translation languages'
404 command.description = 'Sets up translation languages for the project on crow din.net.'
405 command.params = '[options] project-key'
406
407 with addCommand(updateTranslationMaster, 'translate') as command:
408 command.shortDescription = 'Updates translation master files'
409 command.description = 'Updates the translation master files in the project o n crowdin.net.'
410 command.params = '[options] project-key'
411
412 with addCommand(uploadTranslations, 'uploadtrans') as command:
413 command.shortDescription = 'Uploads existing translations'
414 command.description = 'Uploads already existing translations to the project on crowdin.net.'
415 command.params = '[options] project-key'
416
417 with addCommand(getTranslations, 'gettranslations') as command:
418 command.shortDescription = 'Downloads translation updates'
419 command.description = 'Downloads updated translations from crowdin.net.'
420 command.params = '[options] project-key'
421
422 with addCommand(generateDocs, 'docs') as command:
423 command.shortDescription = 'Generate documentation (requires node.js)'
424 command.description = ('Generate documentation files and write them into '
425 'the specified directory.')
426 command.addOption('Suppress JsDoc output', short='q', long='quiet')
427 command.params = '[options] <directory>'
428 command.supportedTypes = {'chrome'}
429
430 with addCommand(runReleaseAutomation, 'release') as command:
431 command.shortDescription = 'Run release automation'
432 command.description = 'Note: If you are not the project owner then you ' "probably don't want to run this!\n\n" 'Runs release automation: crea tes downloads for the new version, tags ' 'source code repository as well as downloads and buildtools repository.'
433 command.addOption('File containing private key and certificates required to sign the release.', short='k', long='key', value='file', types={'chrome', 'edge' })
434 command.addOption('Directory containing downloads repository (if omitted ../ downloads is assumed)', short='d', long='downloads', value='dir')
435 command.params = '[options] <version>'
436 command.supportedTypes = {'chrome', 'edge'}
437
438 with addCommand(updatePSL, 'updatepsl') as command:
439 command.shortDescription = 'Updates Public Suffix List'
440 command.description = 'Downloads Public Suffix List (see http://publicsuffix .org/) and generates lib/publicSuffixList.js from it.'
441 command.supportedTypes = {'chrome'}
442
443
444 def getType(baseDir, scriptName, args):
445 # Look for an explicit type parameter (has to be the first parameter)
446 if len(args) >= 2 and args[0] == '-t':
447 type = args[1]
448 del args[1]
449 del args[0]
450 if type not in knownTypes:
451 print '''
452 Unknown type %s specified, supported types are: %s
453 ''' % (type, ', '.join(knownTypes))
454 return None
455 return type
456
457 # Try to guess repository type
458 types = []
459 for t in knownTypes:
460 if os.path.exists(os.path.join(baseDir, 'metadata.%s' % t)):
461 types.append(t)
462
463 if len(types) == 1:
464 return types[0]
465 elif len(types) > 1:
466 print '''
467 Ambiguous repository type, please specify -t parameter explicitly, e.g.
468 %s -t %s build
469 ''' % (scriptName, types[0])
470 return None
471 else:
472 print '''
473 No metadata file found in this repository, a metadata file like
474 metadata.* is required.
475 '''
476 return None
477
478
479 def processArgs(baseDir, args):
480 global commands
481
482 scriptName = os.path.basename(args[0])
483 args = args[1:]
484 type = getType(baseDir, scriptName, args)
485 if type == None:
486 return
487
488 if len(args) == 0:
489 args = ['build']
490 print '''
491 No command given, assuming "build". For a list of commands run:
492
493 %s help
494 ''' % scriptName
495
496 command = args[0]
497 if command in commands:
498 if commands[command].isSupported(type):
499 try:
500 opts, args = commands[command].parseArgs(type, args[1:])
501 except GetoptError as e:
502 print str(e)
503 usage(scriptName, type, command)
504 sys.exit(2)
505 for option, value in opts:
506 if option in {'-h', '--help'}:
507 usage(scriptName, type, command)
508 sys.exit()
509 commands[command](baseDir, scriptName, opts, args, type)
510 else:
511 print 'Command %s is not supported for this application type' % comm and
512 usage(scriptName, type)
513 else:
514 print 'Command %s is unrecognized' % command
515 usage(scriptName, type)
OLDNEW
« no previous file with comments | « no previous file | tests/test_packagerWebExt.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld