| OLD | NEW | 
|---|
| 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 os | 5 import os | 
| 6 import sys | 6 import sys | 
| 7 import re | 7 import re | 
| 8 import subprocess | 8 import subprocess | 
| 9 import shutil | 9 import shutil | 
| 10 import buildtools | 10 import buildtools | 
| 11 from getopt import getopt, GetoptError | 11 from getopt import getopt, GetoptError | 
| 12 from StringIO import StringIO | 12 from StringIO import StringIO | 
| 13 from zipfile import ZipFile | 13 from zipfile import ZipFile | 
| 14 | 14 | 
| 15 knownTypes = ('gecko', 'gecko-webext', 'chrome', 'safari', 'generic', 'edge') | 15 knownTypes = ('gecko-webext', 'chrome', 'safari', 'generic', 'edge') | 
| 16 | 16 | 
| 17 | 17 | 
| 18 class Command(object): | 18 class Command(object): | 
| 19     name = property(lambda self: self._name) | 19     name = property(lambda self: self._name) | 
| 20     shortDescription = property( | 20     shortDescription = property( | 
| 21         lambda self: self._shortDescription, | 21         lambda self: self._shortDescription, | 
| 22         lambda self, value: self.__dict__.update({'_shortDescription': value}) | 22         lambda self, value: self.__dict__.update({'_shortDescription': value}) | 
| 23     ) | 23     ) | 
| 24     description = property( | 24     description = property( | 
| 25         lambda self: self._description, | 25         lambda self: self._description, | 
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 171             'name': command.name, | 171             'name': command.name, | 
| 172             'params': command.params, | 172             'params': command.params, | 
| 173             'description': description, | 173             'description': description, | 
| 174             'options': '\n'.join(options) | 174             'options': '\n'.join(options) | 
| 175         } | 175         } | 
| 176 | 176 | 
| 177 | 177 | 
| 178 def runBuild(baseDir, scriptName, opts, args, type): | 178 def runBuild(baseDir, scriptName, opts, args, type): | 
| 179     kwargs = {} | 179     kwargs = {} | 
| 180     for option, value in opts: | 180     for option, value in opts: | 
| 181         if option in {'-l', '--locales'} and type == 'gecko': | 181         if option in {'-b', '--build'}: | 
| 182             kwargs['locales'] = value.split(',') |  | 
| 183         elif option in {'-b', '--build'}: |  | 
| 184             kwargs['buildNum'] = value | 182             kwargs['buildNum'] = value | 
| 185             no_gecko_build = type not in {'gecko', 'gecko-webext'} | 183             if type != 'gecko-webext' and not kwargs['buildNum'].isdigit(): | 
| 186             if no_gecko_build and not kwargs['buildNum'].isdigit(): |  | 
| 187                 raise TypeError('Build number must be numerical') | 184                 raise TypeError('Build number must be numerical') | 
| 188         elif option in {'-k', '--key'}: | 185         elif option in {'-k', '--key'}: | 
| 189             kwargs['keyFile'] = value | 186             kwargs['keyFile'] = value | 
| 190         elif option in {'-m', '--multi-compartment'} and type == 'gecko': |  | 
| 191             kwargs['multicompartment'] = True |  | 
| 192         elif option in {'-r', '--release'}: | 187         elif option in {'-r', '--release'}: | 
| 193             kwargs['releaseBuild'] = True | 188             kwargs['releaseBuild'] = True | 
| 194     if len(args) > 0: | 189     if len(args) > 0: | 
| 195         kwargs['outFile'] = args[0] | 190         kwargs['outFile'] = args[0] | 
| 196 | 191 | 
| 197     if type == 'gecko': | 192     if type in {'chrome', 'gecko-webext'}: | 
| 198         import buildtools.packagerGecko as packager |  | 
| 199     elif type in {'chrome', 'gecko-webext'}: |  | 
| 200         import buildtools.packagerChrome as packager | 193         import buildtools.packagerChrome as packager | 
| 201     elif type == 'safari': | 194     elif type == 'safari': | 
| 202         import buildtools.packagerSafari as packager | 195         import buildtools.packagerSafari as packager | 
| 203     elif type == 'edge': | 196     elif type == 'edge': | 
| 204         import buildtools.packagerEdge as packager | 197         import buildtools.packagerEdge as packager | 
| 205 | 198 | 
| 206     packager.createBuild(baseDir, type=type, **kwargs) | 199     packager.createBuild(baseDir, type=type, **kwargs) | 
| 207 | 200 | 
| 208 | 201 | 
| 209 def runAutoInstall(baseDir, scriptName, opts, args, type): |  | 
| 210     if len(args) == 0: |  | 
| 211         print 'Port of the Extension Auto-Installer needs to be specified' |  | 
| 212         usage(scriptName, type, 'autoinstall') |  | 
| 213         return |  | 
| 214 |  | 
| 215     multicompartment = False |  | 
| 216     for option, value in opts: |  | 
| 217         if option in ('-m', '--multi-compartment'): |  | 
| 218             multicompartment = True |  | 
| 219 |  | 
| 220     if ':' in args[0]: |  | 
| 221         host, port = args[0].rsplit(':', 1) |  | 
| 222     else: |  | 
| 223         host, port = ('localhost', args[0]) |  | 
| 224 |  | 
| 225     import buildtools.packagerGecko as packager |  | 
| 226     packager.autoInstall(baseDir, type, host, port, multicompartment=multicompar
     tment) |  | 
| 227 |  | 
| 228 |  | 
| 229 def createDevEnv(baseDir, scriptName, opts, args, type): | 202 def createDevEnv(baseDir, scriptName, opts, args, type): | 
| 230     if type == 'safari': | 203     if type == 'safari': | 
| 231         import buildtools.packagerSafari as packager | 204         import buildtools.packagerSafari as packager | 
| 232     else: | 205     else: | 
| 233         import buildtools.packagerChrome as packager | 206         import buildtools.packagerChrome as packager | 
| 234 | 207 | 
| 235     file = StringIO() | 208     file = StringIO() | 
| 236     packager.createBuild(baseDir, type=type, outFile=file, devenv=True, releaseB
     uild=True) | 209     packager.createBuild(baseDir, type=type, outFile=file, devenv=True, releaseB
     uild=True) | 
| 237 | 210 | 
| 238     from buildtools.packager import getDevEnvPath | 211     from buildtools.packager import getDevEnvPath | 
| 239     devenv_dir = getDevEnvPath(baseDir, type) | 212     devenv_dir = getDevEnvPath(baseDir, type) | 
| 240 | 213 | 
| 241     shutil.rmtree(devenv_dir, ignore_errors=True) | 214     shutil.rmtree(devenv_dir, ignore_errors=True) | 
| 242 | 215 | 
| 243     file.seek(0) | 216     file.seek(0) | 
| 244     with ZipFile(file, 'r') as zip_file: | 217     with ZipFile(file, 'r') as zip_file: | 
| 245         zip_file.extractall(devenv_dir) | 218         zip_file.extractall(devenv_dir) | 
| 246 | 219 | 
| 247 | 220 | 
| 248 def readLocaleConfig(baseDir, type, metadata): | 221 def readLocaleConfig(baseDir, type, metadata): | 
| 249     if type == 'gecko': | 222     if type != 'generic': | 
| 250         import buildtools.packagerGecko as packager |  | 
| 251         localeDir = packager.getLocalesDir(baseDir) |  | 
| 252         localeConfig = { |  | 
| 253             'name_format': 'BCP-47', |  | 
| 254             'file_format': 'gecko-dtd', |  | 
| 255             'default_locale': packager.defaultLocale |  | 
| 256         } |  | 
| 257     elif type in {'chrome', 'gecko-webext'}: |  | 
| 258         import buildtools.packagerChrome as packager | 223         import buildtools.packagerChrome as packager | 
| 259         localeDir = os.path.join(baseDir, '_locales') | 224         localeDir = os.path.join(baseDir, '_locales') | 
| 260         localeConfig = { | 225         localeConfig = { | 
| 261             'name_format': 'ISO-15897', |  | 
| 262             'file_format': 'chrome-json', |  | 
| 263             'default_locale': packager.defaultLocale, | 226             'default_locale': packager.defaultLocale, | 
| 264         } | 227         } | 
| 265     else: | 228     else: | 
| 266         localeDir = os.path.join( | 229         localeDir = os.path.join( | 
| 267             baseDir, *metadata.get('locales', 'base_path').split('/') | 230             baseDir, *metadata.get('locales', 'base_path').split('/') | 
| 268         ) | 231         ) | 
| 269         localeConfig = { | 232         localeConfig = { | 
| 270             'name_format': metadata.get('locales', 'name_format'), |  | 
| 271             'file_format': metadata.get('locales', 'file_format'), |  | 
| 272             'default_locale': metadata.get('locales', 'default_locale') | 233             'default_locale': metadata.get('locales', 'default_locale') | 
| 273         } | 234         } | 
| 274 | 235 | 
| 275     localeConfig['base_path'] = localeDir | 236     localeConfig['base_path'] = localeDir | 
| 276 | 237 | 
| 277     locales = [(locale, os.path.join(localeDir, locale)) | 238     locales = [(locale.replace('_', '-'), os.path.join(localeDir, locale)) | 
| 278                for locale in os.listdir(localeDir)] | 239                for locale in os.listdir(localeDir)] | 
| 279     if localeConfig['name_format'] == 'ISO-15897': |  | 
| 280         locales = [(locale.replace('_', '-'), localePath) |  | 
| 281                    for locale, localePath in locales] |  | 
| 282     localeConfig['locales'] = dict(locales) | 240     localeConfig['locales'] = dict(locales) | 
| 283 | 241 | 
| 284     return localeConfig | 242     return localeConfig | 
| 285 | 243 | 
| 286 | 244 | 
| 287 def setupTranslations(baseDir, scriptName, opts, args, type): | 245 def setupTranslations(baseDir, scriptName, opts, args, type): | 
| 288     if len(args) < 1: | 246     if len(args) < 1: | 
| 289         print 'Project key is required to update translation master files.' | 247         print 'Project key is required to update translation master files.' | 
| 290         usage(scriptName, type, 'setuptrans') | 248         usage(scriptName, type, 'setuptrans') | 
| 291         return | 249         return | 
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 356     from buildtools.packager import readMetadata | 314     from buildtools.packager import readMetadata | 
| 357     metadata = readMetadata(baseDir, type) | 315     metadata = readMetadata(baseDir, type) | 
| 358 | 316 | 
| 359     basename = metadata.get('general', 'basename') | 317     basename = metadata.get('general', 'basename') | 
| 360     localeConfig = readLocaleConfig(baseDir, type, metadata) | 318     localeConfig = readLocaleConfig(baseDir, type, metadata) | 
| 361 | 319 | 
| 362     import buildtools.localeTools as localeTools | 320     import buildtools.localeTools as localeTools | 
| 363     localeTools.getTranslations(localeConfig, basename, key) | 321     localeTools.getTranslations(localeConfig, basename, key) | 
| 364 | 322 | 
| 365 | 323 | 
| 366 def showDescriptions(baseDir, scriptName, opts, args, type): |  | 
| 367     locales = None |  | 
| 368     for option, value in opts: |  | 
| 369         if option in ('-l', '--locales'): |  | 
| 370             locales = value.split(',') |  | 
| 371 |  | 
| 372     import buildtools.packagerGecko as packager |  | 
| 373     if locales == None: |  | 
| 374         locales = packager.getLocales(baseDir) |  | 
| 375     elif locales == 'all': |  | 
| 376         locales = packager.getLocales(baseDir, True) |  | 
| 377 |  | 
| 378     data = packager.readLocaleMetadata(baseDir, locales) |  | 
| 379     localeCodes = data.keys() |  | 
| 380     localeCodes.sort() |  | 
| 381     for localeCode in localeCodes: |  | 
| 382         locale = data[localeCode] |  | 
| 383         print ('''%s |  | 
| 384 %s |  | 
| 385 %s |  | 
| 386 %s |  | 
| 387 %s |  | 
| 388 ''' % (localeCode, |  | 
| 389             locale['name'] if 'name' in locale else 'None', |  | 
| 390             locale['description'] if 'description' in locale else 'None', |  | 
| 391             locale['description.short'] if 'description.short' in locale else 'N
     one', |  | 
| 392             locale['description.long'] if 'description.long' in locale else 'Non
     e', |  | 
| 393        )).encode('utf-8') |  | 
| 394 |  | 
| 395 |  | 
| 396 def generateDocs(baseDir, scriptName, opts, args, type): | 324 def generateDocs(baseDir, scriptName, opts, args, type): | 
| 397     if len(args) == 0: | 325     if len(args) == 0: | 
| 398         print 'No target directory specified for the documentation' | 326         print 'No target directory specified for the documentation' | 
| 399         usage(scriptName, type, 'docs') | 327         usage(scriptName, type, 'docs') | 
| 400         return | 328         return | 
| 401     targetDir = args[0] | 329     targetDir = args[0] | 
| 402 | 330 | 
| 403     source_dir = os.path.join(baseDir, 'lib') | 331     source_dir = os.path.join(baseDir, 'lib') | 
| 404     sources = [source_dir] | 332     sources = [source_dir] | 
| 405 | 333 | 
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 456     import buildtools.publicSuffixListUpdater as publicSuffixListUpdater | 384     import buildtools.publicSuffixListUpdater as publicSuffixListUpdater | 
| 457     publicSuffixListUpdater.updatePSL(baseDir) | 385     publicSuffixListUpdater.updatePSL(baseDir) | 
| 458 | 386 | 
| 459 with addCommand(lambda baseDir, scriptName, opts, args, type: usage(scriptName, 
     type), ('help', '-h', '--help')) as command: | 387 with addCommand(lambda baseDir, scriptName, opts, args, type: usage(scriptName, 
     type), ('help', '-h', '--help')) as command: | 
| 460     command.shortDescription = 'Show this message' | 388     command.shortDescription = 'Show this message' | 
| 461 | 389 | 
| 462 with addCommand(runBuild, 'build') as command: | 390 with addCommand(runBuild, 'build') as command: | 
| 463     command.shortDescription = 'Create a build' | 391     command.shortDescription = 'Create a build' | 
| 464     command.description = 'Creates an extension build with given file name. If o
     utput_file is missing a default name will be chosen.' | 392     command.description = 'Creates an extension build with given file name. If o
     utput_file is missing a default name will be chosen.' | 
| 465     command.params = '[options] [output_file]' | 393     command.params = '[options] [output_file]' | 
| 466     command.addOption('Only include the given locales (if omitted: all locales n
     ot marked as incomplete)', short='l', long='locales', value='l1,l2,l3', types=('
     gecko')) |  | 
| 467     command.addOption('Use given build number (if omitted the build number will 
     be retrieved from Mercurial)', short='b', long='build', value='num') | 394     command.addOption('Use given build number (if omitted the build number will 
     be retrieved from Mercurial)', short='b', long='build', value='num') | 
| 468     command.addOption('File containing private key and certificates required to 
     sign the package', short='k', long='key', value='file', types=('chrome', 'safari
     ')) | 395     command.addOption('File containing private key and certificates required to 
     sign the package', short='k', long='key', value='file', types=('chrome', 'safari
     ')) | 
| 469     command.addOption('Create a build for leak testing', short='m', long='multi-
     compartment', types=('gecko')) |  | 
| 470     command.addOption('Create a release build', short='r', long='release') | 396     command.addOption('Create a release build', short='r', long='release') | 
| 471     command.supportedTypes = ('gecko', 'gecko-webext', 'chrome', 'safari', 'edge
     ') | 397     command.supportedTypes = ('gecko-webext', 'chrome', 'safari', 'edge') | 
| 472 |  | 
| 473 with addCommand(runAutoInstall, 'autoinstall') as command: |  | 
| 474     command.shortDescription = 'Install extension automatically' |  | 
| 475     command.description = 'Will automatically install the extension in a browser
      running Extension Auto-Installer. If host parameter is omitted assumes that the
      browser runs on localhost.' |  | 
| 476     command.params = '[<host>:]<port>' |  | 
| 477     command.addOption('Create a build for leak testing', short='m', long='multi-
     compartment') |  | 
| 478     command.supportedTypes = ('gecko') |  | 
| 479 | 398 | 
| 480 with addCommand(createDevEnv, 'devenv') as command: | 399 with addCommand(createDevEnv, 'devenv') as command: | 
| 481     command.shortDescription = 'Set up a development environment' | 400     command.shortDescription = 'Set up a development environment' | 
| 482     command.description = 'Will set up or update the devenv folder as an unpacke
     d extension folder for development.' | 401     command.description = 'Will set up or update the devenv folder as an unpacke
     d extension folder for development.' | 
| 483     command.supportedTypes = ('gecko-webext', 'chrome', 'safari') | 402     command.supportedTypes = ('gecko-webext', 'chrome', 'safari') | 
| 484 | 403 | 
| 485 with addCommand(setupTranslations, 'setuptrans') as command: | 404 with addCommand(setupTranslations, 'setuptrans') as command: | 
| 486     command.shortDescription = 'Sets up translation languages' | 405     command.shortDescription = 'Sets up translation languages' | 
| 487     command.description = 'Sets up translation languages for the project on crow
     din.net.' | 406     command.description = 'Sets up translation languages for the project on crow
     din.net.' | 
| 488     command.params = '[options] project-key' | 407     command.params = '[options] project-key' | 
| 489     command.supportedTypes = ('gecko', 'chrome', 'generic') |  | 
| 490 | 408 | 
| 491 with addCommand(updateTranslationMaster, 'translate') as command: | 409 with addCommand(updateTranslationMaster, 'translate') as command: | 
| 492     command.shortDescription = 'Updates translation master files' | 410     command.shortDescription = 'Updates translation master files' | 
| 493     command.description = 'Updates the translation master files in the project o
     n crowdin.net.' | 411     command.description = 'Updates the translation master files in the project o
     n crowdin.net.' | 
| 494     command.params = '[options] project-key' | 412     command.params = '[options] project-key' | 
| 495     command.supportedTypes = ('gecko', 'chrome', 'generic') |  | 
| 496 | 413 | 
| 497 with addCommand(uploadTranslations, 'uploadtrans') as command: | 414 with addCommand(uploadTranslations, 'uploadtrans') as command: | 
| 498     command.shortDescription = 'Uploads existing translations' | 415     command.shortDescription = 'Uploads existing translations' | 
| 499     command.description = 'Uploads already existing translations to the project 
     on crowdin.net.' | 416     command.description = 'Uploads already existing translations to the project 
     on crowdin.net.' | 
| 500     command.params = '[options] project-key' | 417     command.params = '[options] project-key' | 
| 501     command.supportedTypes = ('gecko', 'chrome', 'generic') |  | 
| 502 | 418 | 
| 503 with addCommand(getTranslations, 'gettranslations') as command: | 419 with addCommand(getTranslations, 'gettranslations') as command: | 
| 504     command.shortDescription = 'Downloads translation updates' | 420     command.shortDescription = 'Downloads translation updates' | 
| 505     command.description = 'Downloads updated translations from crowdin.net.' | 421     command.description = 'Downloads updated translations from crowdin.net.' | 
| 506     command.params = '[options] project-key' | 422     command.params = '[options] project-key' | 
| 507     command.supportedTypes = ('gecko', 'chrome', 'generic') |  | 
| 508 |  | 
| 509 with addCommand(showDescriptions, 'showdesc') as command: |  | 
| 510     command.shortDescription = 'Print description strings for all locales' |  | 
| 511     command.description = 'Display description strings for all locales as specif
     ied in the corresponding meta.properties files.' |  | 
| 512     command.addOption('Only include the given locales', short='l', long='locales
     ', value='l1,l2,l3') |  | 
| 513     command.params = '[options]' |  | 
| 514     command.supportedTypes = ('gecko') |  | 
| 515 | 423 | 
| 516 with addCommand(generateDocs, 'docs') as command: | 424 with addCommand(generateDocs, 'docs') as command: | 
| 517     command.shortDescription = 'Generate documentation (requires node.js)' | 425     command.shortDescription = 'Generate documentation (requires node.js)' | 
| 518     command.description = ('Generate documentation files and write them into ' | 426     command.description = ('Generate documentation files and write them into ' | 
| 519                            'the specified directory.') | 427                            'the specified directory.') | 
| 520     command.addOption('Suppress JsDoc output', short='q', long='quiet') | 428     command.addOption('Suppress JsDoc output', short='q', long='quiet') | 
| 521     command.params = '[options] <directory>' | 429     command.params = '[options] <directory>' | 
| 522     command.supportedTypes = ('gecko', 'chrome') | 430     command.supportedTypes = ('chrome') | 
| 523 | 431 | 
| 524 with addCommand(runReleaseAutomation, 'release') as command: | 432 with addCommand(runReleaseAutomation, 'release') as command: | 
| 525     command.shortDescription = 'Run release automation' | 433     command.shortDescription = 'Run release automation' | 
| 526     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.' | 434     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.' | 
| 527     command.addOption('File containing private key and certificates required to 
     sign the release.', short='k', long='key', value='file', types=('chrome', 'safar
     i', 'edge')) | 435     command.addOption('File containing private key and certificates required to 
     sign the release.', short='k', long='key', value='file', types=('chrome', 'safar
     i', 'edge')) | 
| 528     command.addOption('Directory containing downloads repository (if omitted ../
     downloads is assumed)', short='d', long='downloads', value='dir') | 436     command.addOption('Directory containing downloads repository (if omitted ../
     downloads is assumed)', short='d', long='downloads', value='dir') | 
| 529     command.params = '[options] <version>' | 437     command.params = '[options] <version>' | 
| 530     command.supportedTypes = ('gecko', 'chrome', 'safari', 'edge') | 438     command.supportedTypes = ('chrome', 'safari', 'edge') | 
| 531 | 439 | 
| 532 with addCommand(updatePSL, 'updatepsl') as command: | 440 with addCommand(updatePSL, 'updatepsl') as command: | 
| 533     command.shortDescription = 'Updates Public Suffix List' | 441     command.shortDescription = 'Updates Public Suffix List' | 
| 534     command.description = 'Downloads Public Suffix List (see http://publicsuffix
     .org/) and generates lib/publicSuffixList.js from it.' | 442     command.description = 'Downloads Public Suffix List (see http://publicsuffix
     .org/) and generates lib/publicSuffixList.js from it.' | 
| 535     command.supportedTypes = ('chrome',) | 443     command.supportedTypes = ('chrome',) | 
| 536 | 444 | 
| 537 | 445 | 
| 538 def getType(baseDir, scriptName, args): | 446 def getType(baseDir, scriptName, args): | 
| 539     # Look for an explicit type parameter (has to be the first parameter) | 447     # Look for an explicit type parameter (has to be the first parameter) | 
| 540     if len(args) >= 2 and args[0] == '-t': | 448     if len(args) >= 2 and args[0] == '-t': | 
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 600                 if option in ('-h', '--help'): | 508                 if option in ('-h', '--help'): | 
| 601                     usage(scriptName, type, command) | 509                     usage(scriptName, type, command) | 
| 602                     sys.exit() | 510                     sys.exit() | 
| 603             commands[command](baseDir, scriptName, opts, args, type) | 511             commands[command](baseDir, scriptName, opts, args, type) | 
| 604         else: | 512         else: | 
| 605             print 'Command %s is not supported for this application type' % comm
     and | 513             print 'Command %s is not supported for this application type' % comm
     and | 
| 606             usage(scriptName, type) | 514             usage(scriptName, type) | 
| 607     else: | 515     else: | 
| 608         print 'Command %s is unrecognized' % command | 516         print 'Command %s is unrecognized' % command | 
| 609         usage(scriptName, type) | 517         usage(scriptName, type) | 
| OLD | NEW | 
|---|