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: Created Nov. 15, 2017, 10:25 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 import argparse
5 import os 6 import os
6 import sys 7 import sys
7 import re 8 import re
8 import subprocess 9 import subprocess
9 import shutil 10 import shutil
10 from getopt import getopt, GetoptError 11 from functools import partial
11 from StringIO import StringIO 12 from StringIO import StringIO
12 from zipfile import ZipFile 13 from zipfile import ZipFile
13 14
14 knownTypes = {'gecko', 'chrome', 'generic', 'edge'} 15 KNOWN_PLATFORMS = {'gecko', 'chrome', 'generic', 'edge'}
16
17 parser = argparse.ArgumentParser(
18 description=__doc__,
19 formatter_class=argparse.RawDescriptionHelpFormatter)
20
21 subs = parser.add_subparsers(title='Commands', dest='action',
22 metavar='[command]')
15 23
16 24
17 class Command(object): 25 def make_argument(*args, **kwargs):
18 name = property(lambda self: self._name) 26 def _make_argument(*args, **kwargs):
19 shortDescription = property( 27 parser = kwargs.pop('parser')
20 lambda self: self._shortDescription, 28 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 29
37 def __init__(self, handler, name): 30 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 31
79 32
80 commandsList = [] 33 def argparse_command(name=None, platforms=None, arguments=()):
81 commands = {} 34 def wrapper(func):
35 def func_wrapper(*args, **kwargs):
36 return func(*args, **kwargs)
37
38 short_desc, long_desc = func.__doc__.split(os.linesep + os.linesep, 1)
39
40 new_parser = subs.add_parser(
41 name or func.__name__,
42 description=long_desc,
43 formatter_class=argparse.RawDescriptionHelpFormatter,
44 help=short_desc,
45 )
46
47 if len(platforms) > 1:
48 new_parser.add_argument('-t', '--type', dest='platform',
49 choices=platforms, required=True)
Wladimir Palant 2017/11/17 09:26:10 This is suboptimal for repositories where only one
tlucas 2017/11/17 11:36:45 Done. (See collect_platforms)
50 elif len(platforms) == 1:
51 new_parser.set_defaults(platform=platforms.pop())
52
53 for argument in arguments:
54 argument(parser=new_parser)
55
56 new_parser.set_defaults(function=func)
57 return func_wrapper
58
59 return wrapper
82 60
83 61
84 def addCommand(handler, name): 62 @argparse_command(
85 if isinstance(name, basestring): 63 name='build', platforms={'gecko', 'chrome', 'edge'},
86 aliases = () 64 arguments=(
87 else: 65 make_argument(
88 name, aliases = (name[0], name[1:]) 66 '-b', '--build',
67 help=('Use given build number (if omitted the build number will '
68 'be retrieved from Mercurial)')),
69 make_argument(
70 '-k', '--key', dest='key_file',
71 help=('File containing private key and certificates required to '
72 'sign the package')),
73 make_argument(
74 '-r', '--release', action='store_true',
75 help='Create a release build'),
76 make_argument('output_file', nargs='?')
77 )
78 )
79 def run_build(base_dir, build, key_file, release, output_file, platform,
Wladimir Palant 2017/11/17 09:26:10 Nit: Maybe rename this function into build and dro
tlucas 2017/11/17 11:36:44 Done.
80 **kwargs):
81 """
82 Create a build.
89 83
90 global commandsList, commands 84 Creates an extension build with given file name. If output_file is missing
91 command = Command(handler, name) 85 a default name will be chosen.
92 commandsList.append(command) 86 """
93 commands[name] = command 87 kwargs = {}
94 for alias in aliases: 88 if platform in {'chrome', 'gecko'}:
95 commands[alias] = command 89 import buildtools.packagerChrome as packager
96 return command 90 elif platform == 'edge':
91 import buildtools.packagerEdge as packager
Wladimir Palant 2017/11/17 09:26:10 Nit: This is code you merely moved around but cons
tlucas 2017/11/17 11:36:44 Done.
92
93 kwargs['keyFile'] = key_file
94 kwargs['outFile'] = output_file
95 kwargs['releaseBuild'] = release
96 kwargs['buildNum'] = build
97
98 packager.createBuild(base_dir, type=platform, **kwargs)
97 99
98 100
99 def splitByLength(string, maxLen): 101 @argparse_command(
100 parts = [] 102 name='devenv', platforms={'chrome', 'gecko', 'edge'}
101 currentPart = '' 103 )
102 for match in re.finditer(r'\s*(\S+)', string): 104 def create_devenv(base_dir, platform, **kwargs):
103 if len(match.group(0)) + len(currentPart) < maxLen: 105 """
104 currentPart += match.group(0) 106 Set up a development environment.
105 else:
106 parts.append(currentPart)
107 currentPart = match.group(1)
108 if len(currentPart):
109 parts.append(currentPart)
110 return parts
111 107
112 108 Will set up or update the devenv folder as an unpacked extension folder '
113 def usage(scriptName, type, commandName=None): 109 for development.
114 if commandName == None: 110 """
115 global commandsList 111 if platform == 'edge':
116 descriptions = []
117 for command in commandsList:
118 if not command.isSupported(type):
119 continue
120 commandText = ('%s %s' % (command.name, command.params)).ljust(39)
121 descriptionParts = splitByLength(command.shortDescription, 29)
122 descriptions.append(' %s [-t %s] %s %s' % (scriptName, type, comman dText, descriptionParts[0]))
123 for part in descriptionParts[1:]:
124 descriptions.append(' %s %s %s %s' % (' ' * len(scriptName ), ' ' * len(type), ' ' * len(commandText), part))
125 print '''Usage:
126
127 %(descriptions)s
128
129 For details on a command run:
130
131 %(scriptName)s [-t %(type)s] <command> --help
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
176
177 def runBuild(baseDir, scriptName, opts, args, type):
178 kwargs = {}
179 for option, value in opts:
180 if option in {'-b', '--build'}:
181 kwargs['buildNum'] = value
182 if type != 'gecko' and not kwargs['buildNum'].isdigit():
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
191 if type in {'chrome', 'gecko'}:
192 import buildtools.packagerChrome as packager
193 elif type == 'edge':
194 import buildtools.packagerEdge as packager
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 112 import buildtools.packagerEdge as packager
202 else: 113 else:
203 import buildtools.packagerChrome as packager 114 import buildtools.packagerChrome as packager
204 115
205 file = StringIO() 116 file = StringIO()
206 packager.createBuild(baseDir, type=type, outFile=file, devenv=True, releaseB uild=True) 117 packager.createBuild(base_dir, type=platform, outFile=file, devenv=True,
118 releaseBuild=True)
207 119
208 from buildtools.packager import getDevEnvPath 120 from buildtools.packager import getDevEnvPath
209 devenv_dir = getDevEnvPath(baseDir, type) 121 devenv_dir = getDevEnvPath(base_dir, platform)
210 122
211 shutil.rmtree(devenv_dir, ignore_errors=True) 123 shutil.rmtree(devenv_dir, ignore_errors=True)
212 124
213 file.seek(0) 125 file.seek(0)
214 with ZipFile(file, 'r') as zip_file: 126 with ZipFile(file, 'r') as zip_file:
215 zip_file.extractall(devenv_dir) 127 zip_file.extractall(devenv_dir)
216 128
217 129
218 def readLocaleConfig(baseDir, type, metadata): 130 def read_locale_config(base_dir, platform, metadata):
219 if type != 'generic': 131 if platform != 'generic':
220 import buildtools.packagerChrome as packager 132 import buildtools.packagerChrome as packager
221 localeDir = os.path.join(baseDir, '_locales') 133 locale_dir = os.path.join(base_dir, '_locales')
222 localeConfig = { 134 locale_config = {
223 'default_locale': packager.defaultLocale, 135 'default_locale': packager.defaultLocale,
224 } 136 }
225 else: 137 else:
226 localeDir = os.path.join( 138 locale_dir = os.path.join(
227 baseDir, *metadata.get('locales', 'base_path').split('/') 139 base_dir, *metadata.get('locales', 'base_path').split('/')
228 ) 140 )
229 localeConfig = { 141 locale_config = {
230 'default_locale': metadata.get('locales', 'default_locale') 142 'default_locale': metadata.get('locales', 'default_locale')
231 } 143 }
232 144
233 localeConfig['base_path'] = localeDir 145 locale_config['base_path'] = locale_dir
234 146
235 locales = [(locale.replace('_', '-'), os.path.join(localeDir, locale)) 147 locales = [(locale.replace('_', '-'), os.path.join(locale_dir, locale))
236 for locale in os.listdir(localeDir)] 148 for locale in os.listdir(locale_dir)]
237 localeConfig['locales'] = dict(locales) 149 locale_config['locales'] = dict(locales)
238 150
239 return localeConfig 151 return locale_config
240 152
241 153
242 def setupTranslations(baseDir, scriptName, opts, args, type): 154 project_key_argument = make_argument(
243 if len(args) < 1: 155 'project_key', help='The crowdin project key (required).', nargs='?'
Wladimir Palant 2017/11/17 09:26:10 This is a required parameter, why not nargs=1? Thi
tlucas 2017/11/17 11:36:44 nargs=1 would produce a list of one value, leaving
244 print 'Project key is required to update translation master files.' 156 )
245 usage(scriptName, type, 'setuptrans')
246 return
247 157
248 key = args[0]
249 158
159 @argparse_command(
160 name='setuptrans', platforms=KNOWN_PLATFORMS,
161 arguments=(project_key_argument, )
162 )
163 def setup_translations(base_dir, project_key, platform, **kwargs):
164 """
165 Set up translation languages.
166
167 Set up translation languages for the project on crowdin.net.
Wladimir Palant 2017/11/17 09:26:10 Here and for the commands below: it's crowdin.com.
tlucas 2017/11/17 11:36:44 Done.
168 """
250 from buildtools.packager import readMetadata 169 from buildtools.packager import readMetadata
251 metadata = readMetadata(baseDir, type) 170 metadata = readMetadata(base_dir, platform)
252 171
253 basename = metadata.get('general', 'basename') 172 basename = metadata.get('general', 'basename')
254 localeConfig = readLocaleConfig(baseDir, type, metadata) 173 locale_config = read_locale_config(base_dir, platform, metadata)
255 174
256 import buildtools.localeTools as localeTools 175 import buildtools.localeTools as localeTools
257 localeTools.setupTranslations(localeConfig, basename, key) 176 localeTools.setupTranslations(locale_config, basename, project_key)
258 177
259 178
260 def updateTranslationMaster(baseDir, scriptName, opts, args, type): 179 @argparse_command(
261 if len(args) < 1: 180 name='translate', platforms=KNOWN_PLATFORMS,
262 print 'Project key is required to update translation master files.' 181 arguments=(project_key_argument, )
263 usage(scriptName, type, 'translate') 182 )
264 return 183 def update_translation_master(base_dir, project_key, platform, **kwargs):
184 """
185 Update translation master files.
265 186
266 key = args[0] 187 Update the translation master files in the project on crowdin.net.
267 188 """
268 from buildtools.packager import readMetadata 189 from buildtools.packager import readMetadata
269 metadata = readMetadata(baseDir, type) 190 metadata = readMetadata(base_dir, platform)
270 191
271 basename = metadata.get('general', 'basename') 192 basename = metadata.get('general', 'basename')
272 localeConfig = readLocaleConfig(baseDir, type, metadata) 193 locale_config = read_locale_config(base_dir, platform, metadata)
273 194
274 defaultLocaleDir = os.path.join(localeConfig['base_path'], 195 default_locale_dir = os.path.join(locale_config['base_path'],
275 localeConfig['default_locale']) 196 locale_config['default_locale'])
276 197
277 import buildtools.localeTools as localeTools 198 import buildtools.localeTools as localeTools
278 localeTools.updateTranslationMaster(localeConfig, metadata, defaultLocaleDir , 199 localeTools.updateTranslationMaster(locale_config, metadata,
279 basename, key) 200 default_locale_dir, basename,
201 project_key)
280 202
281 203
282 def uploadTranslations(baseDir, scriptName, opts, args, type): 204 @argparse_command(
283 if len(args) < 1: 205 name='uploadtrans', platforms=KNOWN_PLATFORMS,
284 print 'Project key is required to upload existing translations.' 206 arguments=(project_key_argument, )
285 usage(scriptName, type, 'uploadtrans') 207 )
286 return 208 def upload_translations(base_dir, project_key, platform, **kwargs):
209 """
210 Upload existing translations.
287 211
288 key = args[0] 212 Upload already existing translations to the project on crowdin.net.
289 213 """
290 from buildtools.packager import readMetadata 214 from buildtools.packager import readMetadata
291 metadata = readMetadata(baseDir, type) 215 metadata = readMetadata(base_dir, platform)
292 216
293 basename = metadata.get('general', 'basename') 217 basename = metadata.get('general', 'basename')
294 localeConfig = readLocaleConfig(baseDir, type, metadata) 218 locale_config = read_locale_config(base_dir, platform, metadata)
295 219
296 import buildtools.localeTools as localeTools 220 import buildtools.localeTools as localeTools
297 for locale, localeDir in localeConfig['locales'].iteritems(): 221 for locale, locale_dir in locale_config['locales'].iteritems():
298 if locale != localeConfig['default_locale'].replace('_', '-'): 222 if locale != locale_config['default_locale'].replace('_', '-'):
299 localeTools.uploadTranslations(localeConfig, metadata, localeDir, lo cale, 223 localeTools.uploadTranslations(locale_config, metadata, locale_dir,
300 basename, key) 224 locale, basename, project_key)
301 225
302 226
303 def getTranslations(baseDir, scriptName, opts, args, type): 227 @argparse_command(
304 if len(args) < 1: 228 name='gettranslations', platforms=KNOWN_PLATFORMS,
305 print 'Project key is required to update translation master files.' 229 arguments=(project_key_argument, )
306 usage(scriptName, type, 'translate') 230 )
307 return 231 def get_translations(base_dir, project_key, platform, **kwargs):
232 """
233 Download translation updates.
308 234
309 key = args[0] 235 Download updated translations from crowdin.net.
310 236 """
311 from buildtools.packager import readMetadata 237 from buildtools.packager import readMetadata
312 metadata = readMetadata(baseDir, type) 238 metadata = readMetadata(base_dir, platform)
313 239
314 basename = metadata.get('general', 'basename') 240 basename = metadata.get('general', 'basename')
315 localeConfig = readLocaleConfig(baseDir, type, metadata) 241 locale_config = read_locale_config(base_dir, platform, metadata)
316 242
317 import buildtools.localeTools as localeTools 243 import buildtools.localeTools as localeTools
318 localeTools.getTranslations(localeConfig, basename, key) 244 localeTools.getTranslations(locale_config, basename, project_key)
319 245
320 246
321 def generateDocs(baseDir, scriptName, opts, args, type): 247 @argparse_command(
322 if len(args) == 0: 248 name='docs', platforms={'chrome'},
323 print 'No target directory specified for the documentation' 249 arguments=(
324 usage(scriptName, type, 'docs') 250 make_argument('target_dir'),
325 return 251 make_argument('-q', '--quiet', help='Suppress JsDoc output'),
326 targetDir = args[0] 252 )
253 )
254 def generate_docs(base_dir, target_dir, quiet, platform, **kwargs):
255 """
256 Generate documentation (requires node.js).
327 257
328 source_dir = os.path.join(baseDir, 'lib') 258 Generate documentation files and write them into the specified directory.
259 """
260 source_dir = os.path.join(base_dir, 'lib')
329 sources = [source_dir] 261 sources = [source_dir]
330 262
331 # JSDoc struggles wih huge objects: https://github.com/jsdoc3/jsdoc/issues/9 76 263 # JSDoc struggles wih huge objects:
332 if type == 'chrome': 264 # 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'] 265 if platform == 'chrome':
Wladimir Palant 2017/11/17 09:26:10 Nit: This check can be removed, we have no other p
tlucas 2017/11/17 11:36:44 Done.
266 sources = [os.path.join(source_dir, filename)
267 for filename in os.listdir(source_dir)
268 if filename != 'publicSuffixList.js']
334 269
335 buildtools_path = os.path.dirname(__file__) 270 buildtools_path = os.path.dirname(__file__)
336 config = os.path.join(buildtools_path, 'jsdoc.conf') 271 config = os.path.join(buildtools_path, 'jsdoc.conf')
337 272
338 command = ['npm', 'run-script', 'jsdoc', '--', '--destination', targetDir, 273 command = ['npm', 'run-script', 'jsdoc', '--', '--destination', target_dir,
339 '--configure', config] + sources 274 '--configure', config] + sources
340 if any(opt in {'-q', '--quiet'} for opt, _ in opts): 275 if quiet:
341 process = subprocess.Popen(command, stdout=subprocess.PIPE, 276 process = subprocess.Popen(command, stdout=subprocess.PIPE,
342 stderr=subprocess.PIPE, cwd=buildtools_path) 277 stderr=subprocess.PIPE, cwd=buildtools_path)
343 stderr = process.communicate()[1] 278 stderr = process.communicate()[1]
344 retcode = process.poll() 279 retcode = process.poll()
345 if retcode: 280 if retcode:
346 sys.stderr.write(stderr) 281 sys.stderr.write(stderr)
347 raise subprocess.CalledProcessError(command, retcode) 282 raise subprocess.CalledProcessError(command, retcode)
348 else: 283 else:
349 subprocess.check_call(command, cwd=buildtools_path) 284 subprocess.check_call(command, cwd=buildtools_path)
350 285
351 286
352 def runReleaseAutomation(baseDir, scriptName, opts, args, type): 287 def valid_version_format(value):
353 keyFile = None 288 if re.search(r'[^\d\.]', value):
354 downloadsRepo = os.path.join(baseDir, '..', 'downloads') 289 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 290
361 if len(args) == 0: 291 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 292
371 if type == 'chrome' and keyFile is None: 293
372 print >>sys.stderr, 'Error: you must specify a key file for this release ' 294 @argparse_command(
373 usage(scriptName, type, 'release') 295 name='release', platforms={'chrome', 'edge'},
374 return 296 arguments=(
297 make_argument(
298 '-k', '--key', dest='key_file',
299 help=('File containing private key and certificates required to '
300 'sign the release.')),
301 make_argument(
302 '-d', '--downloads-repository', dest='download_repository',
Wladimir Palant 2017/11/17 09:26:10 Is the naming download_repository rather than down
tlucas 2017/11/17 11:36:44 No, this was not intentional. Done.
303 default='../downloads',
Wladimir Palant 2017/11/17 09:26:10 Please use os.path.join() here. Also, the default
tlucas 2017/11/17 11:36:44 Done.
304 help=('Directory containing downloads repository (if omitted '
305 '../downloads is assumed)')),
306 make_argument(
307 'version', nargs='?', help='Version number of the release',
Wladimir Palant 2017/11/17 09:26:10 This parameter isn't optional.
tlucas 2017/11/17 11:36:44 Done.
308 type=valid_version_format)
309 )
310 )
311 def run_release_automation(base_dir, download_repository, key_file, platform,
312 version, **kwargs):
313 """
314 Run release automation.
315
316 Note: If you are not the project owner then you probably don't want to run
317 this!
318
319 Run release automation: create downloads for the new version, tag source
320 code repository as well as downloads and buildtools repository.
321 """
322 if platform == 'chrome' and key_file is None:
323 raise ValueError('You must specify a key file for this release')
375 324
376 import buildtools.releaseAutomation as releaseAutomation 325 import buildtools.releaseAutomation as releaseAutomation
377 releaseAutomation.run(baseDir, type, version, keyFile, downloadsRepo) 326 releaseAutomation.run(base_dir, platform, version, key_file,
327 download_repository)
378 328
379 329
380 def updatePSL(baseDir, scriptName, opts, args, type): 330 @argparse_command(name='updatepsl', platforms={'chrome'})
331 def update_psl(base_dir, **kwargs):
332 """Update Public Suffix List.
333
334 Downloads Public Suffix List (see http://publicsuffix.org/) and generates
335 lib/publicSuffixList.js from it.
336 """
381 import buildtools.publicSuffixListUpdater as publicSuffixListUpdater 337 import buildtools.publicSuffixListUpdater as publicSuffixListUpdater
382 publicSuffixListUpdater.updatePSL(baseDir) 338 publicSuffixListUpdater.updatePSL(base_dir)
383 339
384 340
385 with addCommand(lambda baseDir, scriptName, opts, args, type: usage(scriptName, type), ('help', '-h', '--help')) as command: 341 def process_args(base_dir, *args):
386 command.shortDescription = 'Show this message' 342 parser.set_defaults(base_dir=base_dir)
387 343
388 with addCommand(runBuild, 'build') as command: 344 # If no args are provided, this module is run directly from the command
389 command.shortDescription = 'Create a build' 345 # 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.' 346 arguments = 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 347
397 with addCommand(createDevEnv, 'devenv') as command: 348 function = arguments.function
398 command.shortDescription = 'Set up a development environment' 349 del arguments.function
399 command.description = 'Will set up or update the devenv folder as an unpacke d extension folder for development.' 350 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