Пример #1
0
def runMigration(jobconf, libs):

    if not jobconf.get('migrate-files', False):
        return

    console = Context.console
    console.info("Please heed the warnings from the configuration file parsing")
    console.info("Migrating Javascript source code to most recent qooxdoo version...")
    console.indent()

    migSettings     = jobconf.get('migrate-files')
    shellCmd  = ShellCmd()

    migratorCmd = os.path.join(os.path.dirname(filetool.root()), "bin", "migrator.py")

    libPaths = []
    for lib in libs:
        lib._init_from_manifest() # Lib()'s aren't initialized yet
        libPaths.append(os.path.join(lib.path, lib.classPath))

    mig_opts = []
    if migSettings.get('from-version', False):
        mig_opts.extend(["--from-version", migSettings.get('from-version')])
    if migSettings.get('migrate-html'):
        mig_opts.append("--migrate-html")
    mig_opts.extend(["--class-path", ",".join(libPaths)])

    shcmd = " ".join(textutil.quoteCommandArgs([sys.executable, migratorCmd] + mig_opts))
    console.debug("Invoking migrator as: '%s'" % shcmd)
    shellCmd.execute(shcmd)

    console.outdent()
Пример #2
0
def getQooxdooRevision():
    shellCmd = ShellCmd()
    rcode, out, err = shellCmd.execute_piped("svnversion")
    if rcode > 0 or out == "exported":
        return "unknown"
    else:
        return "unknown"  #out.rstrip()
Пример #3
0
def getQooxdooRevision():
    shellCmd = ShellCmd()
    rcode, out, err = shellCmd.execute_piped("svnversion")
    if rcode > 0 or out == "exported":
        return "unknown"
    else:
        return out.rstrip()
Пример #4
0
def npm_install(skel_dir, options):
    shellCmd = ShellCmd()
    npm_install = 'npm install --loglevel warn'
    console.log("Adding Node.js modules...")
    shellCmd.execute(npm_install, skel_dir)
    if options.type == 'contribution':
        shellCmd.execute(npm_install, os.path.join(skel_dir, 'demo/default'))
Пример #5
0
    def runShellCommand(self, shellcmd):
        rc = 0
        self._shellCmd       = ShellCmd()

        self._console.info("Executing shell command \"%s\"..." % shellcmd)
        self._console.indent()

        rc = self._shellCmd.execute(shellcmd, self._config.getConfigDir())
        if rc != 0:
            raise RuntimeError, "Shell command returned error code: %s" % repr(rc)
        self._console.outdent()
Пример #6
0
    def runShellCommand(self, shellcmd, cmdnotfoundmsg=""):
        rc = 0
        self._shellCmd       = ShellCmd()
        self._console.info("Executing shell command \"%s\"..." % shellcmd)
        self._console.indent()

        rc = self._shellCmd.execute(shellcmd, self._config.getConfigDir())
        if rc != 0:
            # BUG #7997 (sass may not be installed)
            # 127 = given cmd is not found within PATH sys var and it's not a built-in shell cmd
            if (rc == 127 or (rc == 1 and platform.system() == "Windows")) and cmdnotfoundmsg:
                self._console.info("Skipping shell command: %s" % cmdnotfoundmsg)
            else:
                raise RuntimeError, "Shell command returned error code: %s" % repr(rc)
        self._console.outdent()
Пример #7
0
class JobLib(object):
    def __init__(self, config, console_):
        self._config   = config
        self._console  = console_
        self._shellCmd = ShellCmd()

    def clean(self, cleanMap):
        assert isinstance(cleanMap, types.DictType)
        for item in cleanMap:
            self._console.info(item)
            for file in cleanMap[item]:
                file = self._config.absPath(file) 
                # safety first
                if os.path.splitdrive(file)[1] == os.sep:
                    raise RuntimeError, "!!! I'm not going to delete '/' recursively !!!"
                self._shellCmd.execute ("rm -rf %s" % file)
Пример #8
0
 def cleanDownloadCache(self):
     if self._downloads:
         downdir = self._downloads
         if os.path.splitdrive(downdir)[1] == os.sep:
             raise RuntimeError, "I'm not going to delete '/' recursively!"
         self._console.info("Deleting download cache")
         ShellCmd().rm_rf(downdir)
Пример #9
0
class JobLib(object):
    def __init__(self, config, console_):
        self._config   = config
        self._console  = console_
        self._shellCmd = ShellCmd()

    def clean(self, cleanMap):
        assert isinstance(cleanMap, types.DictType)
        for item in cleanMap:
            self._console.info(item)
            for file in cleanMap[item]:
                file = self._config.absPath(file) 
                # safety first
                if os.path.splitdrive(file)[1] == os.sep:
                    raise RuntimeError, "!!! I'm not going to delete '/' recursively !!!"
                self._shellCmd.rm_rf(file)
Пример #10
0
class ActionLib(object):
    def __init__(self, config, console_):
        self._config   = config
        self._console  = console_
        self._shellCmd = ShellCmd()

    def clean(self, cleanMap):
        assert isinstance(cleanMap, types.DictType)
        for item in cleanMap:
            self._console.info(item)
            for file in cleanMap[item]:
                file = self._config.absPath(file) 
                # resolve file globs
                for entry in glob.glob(file):
                    # safety first
                    if os.path.splitdrive(entry)[1] == os.sep:
                        raise RuntimeError, "!!! I'm not going to delete '/' recursively !!!"
                    self._shellCmd.rm_rf(entry)
Пример #11
0
def runSimulation(jobconf):
    console = Context.console
    console.info("Running Simulation...")

    argv    = []
    javaBin = "java"
    javaClassPath = "-cp"
    argv.extend((javaBin, javaClassPath))

    configClassPath = jobconf.get("simulate/java-classpath", [])
    qxSeleniumPath = jobconf.get("simulate/qxselenium-path", False)
    if qxSeleniumPath:
        configClassPath.append(qxSeleniumPath)

    classPathSeparator = ":"
    if util.getPlatformInfo()[0] == "Windows":
        classPathSeparator = ";"

    configClassPath = classPathSeparator.join(configClassPath)

    if "CYGWIN" in util.getPlatformInfo()[0]:
        configClassPath = "`cygpath -wp " + configClassPath + "`"

    argv.append(configClassPath)

    rhinoClass = jobconf.get("simulate/rhino-class", "org.mozilla.javascript.tools.shell.Main")
    runnerScript = jobconf.get("simulate/simulator-script")
    argv.extend((rhinoClass, runnerScript))

    cmd = " ".join(textutil.quoteCommandArgs(argv))

    settings = jobconf.get("environment", None)
    for key in settings:
        if type(settings[key]) == unicode:
            settings[key] = settings[key].replace(" ", "$")
    if settings:
        settings = json.dumps(settings, separators=(",", ":"))
        settings = settings.replace('"','\\"').replace("{", "\{").replace("}", "\}")
        settings = "settings=" + settings
        cmd += " " + settings

    console.debug("Selenium start command: " + cmd)
    shell = ShellCmd()
    shell.execute_logged(cmd, console, True)
Пример #12
0
    def runShellCommand(self, shellcmd):
        rc = 0
        self._shellCmd       = ShellCmd()

        self._console.info("Executing shell command \"%s\"..." % shellcmd)
        self._console.indent()

        rc = self._shellCmd.execute(shellcmd, self._config.getConfigDir())
        if rc != 0:
            raise RuntimeError, "Shell command returned error code: %s" % repr(rc)
        self._console.outdent()
def npm_install(skel_dir, options):
    shellCmd = ShellCmd()
    npm_install = 'npm install --loglevel warn'
    console.log("Adding Node.js modules...")
    shellCmd.execute(npm_install, skel_dir)
    if options.type == 'contribution':
        shellCmd.execute(npm_install, os.path.join(skel_dir, 'demo/default'))
Пример #14
0
def runMigration(jobconf, libs):

    if not jobconf.get('migrate-files', False):
        return

    console = Context.console
    console.info(
        "Please heed the warnings from the configuration file parsing")
    console.info(
        "Migrating Javascript source code to most recent qooxdoo version...")
    console.indent()

    migSettings = jobconf.get('migrate-files')
    shellCmd = ShellCmd()

    migratorCmd = os.path.join(os.path.dirname(filetool.root()), "bin",
                               "migrator.py")

    libPaths = []
    for lib in libs:
        lib._init_from_manifest()  # Lib()'s aren't initialized yet
        libPaths.append(os.path.join(lib.path, lib.classPath))

    mig_opts = []
    if migSettings.get('from-version', False):
        mig_opts.extend(["--from-version", migSettings.get('from-version')])
    if migSettings.get('migrate-html'):
        mig_opts.append("--migrate-html")
    mig_opts.extend(["--class-path", ",".join(libPaths)])

    shcmd = " ".join(
        textutil.quoteCommandArgs([sys.executable, migratorCmd] + mig_opts))
    console.debug("Invoking migrator as: '%s'" % shcmd)
    shellCmd.execute(shcmd)

    console.outdent()
Пример #15
0
    def runShellCommand(self, shellcmd, cmdnotfoundmsg=""):
        rc = 0
        self._shellCmd       = ShellCmd()
        self._console.info("Executing shell command \"%s\"..." % shellcmd)
        self._console.indent()

        rc = self._shellCmd.execute(shellcmd, self._config.getConfigDir())
        if rc != 0:
            # BUG #7997 (sass may not be installed)
            # 127 = given cmd is not found within PATH sys var and it's not a built-in shell cmd
            if (rc == 127 or (rc == 1 and platform.system() == "Windows")) and cmdnotfoundmsg:
                self._console.info("Skipping shell command: %s" % cmdnotfoundmsg)
            else:
                raise RuntimeError, "Shell command returned error code: %s" % repr(rc)
        self._console.outdent()
Пример #16
0
    def __init__(self, console_, data, path="", **letKwargs):
        global console
        # init members
        self._console = console_
        self._data = None
        self._rawdata = None
        self._fname = None
        self._shellCmd = ShellCmd()
        self._includedConfigs = []  # to record included configs
        self._shadowedJobs = {
        }  # to record shadowed jobs, of the form {<shadowed_job_obj>: <shadowing_job_obj>}

        console = console_

        # dispatch on argument

        if isinstance(data, (types.DictType, types.ListType)):
            #self._console.debug("Creating config from data")
            self.__init__data(data, path)
        elif isinstance(data, types.StringTypes):
            #self._console.debug("Reading config file \"%s\"" % data)
            self.__init_fname(data)
        else:
            raise TypeError, str(data)

        # make sure there is at least an empty jobs map (for later filling)
        if isinstance(self._data,
                      types.DictType) and Key.JOBS_KEY not in self._data:
            self._data[Key.JOBS_KEY] = {}

        # incorporate let macros from letkwargs
        if letKwargs:
            if not Key.LET_KEY in self._data:
                self._data[Key.LET_KEY] = {}
            self._data[Key.LET_KEY].update(letKwargs)

        # expand macros for some top-level keys
        self.expandTopLevelKeys()

        # fix job key tags (like "=key")
        self.fixJobsTags()

        # do some schema sanity checking
        # - off for bug#4441, it's called for the job in generator.py anyway
        #self.checkSchema()

        return
Пример #17
0
    def __init__(self, console_, data, path=""):
        global console
        # init members
        self._console = console_
        self._data = None
        self._fname = None
        self._shellCmd = ShellCmd()

        console = console_

        # dispatch on argument
        if isinstance(data, (types.DictType, types.ListType)):
            self.__init__data(data, path)
        elif isinstance(data, types.StringTypes):
            self.__init_fname(data)
        else:
            raise TypeError, str(data)

        # make sure there is at least an empty jobs map (for later filling)
        if isinstance(self._data,
                      types.DictType) and self.JOBS_KEY not in self._data:
            self._data[self.JOBS_KEY] = {}
Пример #18
0
 def __init__(self, config, console_):
     self._config   = config
     self._console  = console_
     self._shellCmd = ShellCmd()
Пример #19
0
def npm_install(skelDir, options):
    shellCmd = ShellCmd()
    npm_install = 'npm install --loglevel warn'
    console.log("Running '" + npm_install + "'")
    shellCmd.execute(npm_install, skelDir)
Пример #20
0
class ActionLib(object):
    def __init__(self, config, console_):
        self._config   = config
        self._console  = console_
        self._shellCmd = ShellCmd()

    def clean(self, cleanMap):
        assert isinstance(cleanMap, types.DictType)
        for item in cleanMap:
            self._console.info(item)
            for file in cleanMap[item]:
                file = self._config.absPath(file)
                # resolve file globs
                for entry in glob.glob(file):
                    # safety first
                    if os.path.splitdrive(entry)[1] == os.sep:
                        raise RuntimeError, "!!! I'm not going to delete '/' recursively !!!"
                    self._shellCmd.rm_rf(entry)



    def watch(self, jobconf, confObj):
        console = Context.console
        since = time.time()
        watcher = Watcher(jobconf, confObj)
        interval = jobconf.get("watch-files/check-interval", 2)
        paths = jobconf.get("watch-files/paths", [])
        if not paths:
            return
        command = jobconf.get("watch-files/command/line", "")
        if not command:
            return
        command_tmpl = CommandLineTemplate(command)
        per_file = jobconf.get("watch-files/command/per-file", False)
        exit_on_retcode = jobconf.get("watch-files/command/exit-on-retcode", False)
        exec_on_startup = jobconf.get("watch-files/command/exec-on-startup", False)
        if exec_on_startup:
            # simply set check-time to start of epoch
            since = 1.0  # (since=0 would disable time span checking)
        console.info("Watching changes of '%s'..." % paths)
        console.info("Press Ctrl-C to terminate.")
        while True:
            osince = since
            since = time.time()
            ylist = watcher.check(osince)
            if ylist:     # ylist =[(fpath,fstamp)]
                flist = [f[0] for f in ylist]
                cmd_args = {'FILELIST': ' '.join(flist)}
                console.debug("found changed files: %s" % flist)
                try:
                    if not per_file:
                        cmd = command_tmpl.safe_substitute(cmd_args)
                        self.runShellCommand(cmd)
                    else:
                        for fname in flist:
                            cmd_args['FILE']      = fname                       # foo/bar/baz.js
                            cmd_args['DIRNAME']   = os.path.dirname(fname)      # foo/bar
                            cmd_args['BASENAME']  = os.path.basename(fname)     # baz.js
                            cmd_args['EXTENSION'] = os.path.splitext(fname)[1]  # .js
                            cmd_args['FILENAME']  = os.path.basename(os.path.splitext(fname)[0])  # baz
                            cmd = command_tmpl.safe_substitute(cmd_args)
                            self.runShellCommand(cmd)
                except RuntimeError:
                    if exit_on_retcode:
                        raise
                    else:
                        pass
            time.sleep(interval)
        return

    def runShellCommands(self, jobconf):
        if not jobconf.get("shell/command"):
            return

        shellcmd = jobconf.get("shell/command", "")
        if isinstance(shellcmd, list):
            for cmd in shellcmd:
                self.runShellCommand(cmd)
        else:
            self.runShellCommand(shellcmd)


    def runShellCommand(self, shellcmd):
        rc = 0
        self._shellCmd       = ShellCmd()

        self._console.info("Executing shell command \"%s\"..." % shellcmd)
        self._console.indent()

        rc = self._shellCmd.execute(shellcmd, self._config.getConfigDir())
        if rc != 0:
            raise RuntimeError, "Shell command returned error code: %s" % repr(rc)
        self._console.outdent()
Пример #21
0
 def __init__(self, config, console_):
     self._config   = config
     self._console  = console_
     self._shellCmd = ShellCmd()
Пример #22
0
class ActionLib(object):
    def __init__(self, config, console_):
        self._config   = config
        self._console  = console_
        self._shellCmd = ShellCmd()

    def clean(self, cleanMap):
        assert isinstance(cleanMap, types.DictType)
        for item in cleanMap:
            self._console.info(item)
            for file in cleanMap[item]:
                file = self._config.absPath(file) 
                # resolve file globs
                for entry in glob.glob(file):
                    # safety first
                    if os.path.splitdrive(entry)[1] == os.sep:
                        raise RuntimeError, "!!! I'm not going to delete '/' recursively !!!"
                    self._shellCmd.rm_rf(entry)



    def watch(self, jobconf, confObj):
        console = Context.console
        since = time.time()
        interval = jobconf.get("watch-files/interval", 2)
        paths = jobconf.get("watch-files/paths", [])
        if not paths:
            return
        include_dirs = jobconf.get("watch-files/include-dirs", False)
        exit_on_retcode = jobconf.get("watch-files/exit-on-retcode", False)
        command = jobconf.get("watch-files/command/line", "")
        if not command:
            return
        command_tmpl = CommandLineTemplate(command)
        per_file = jobconf.get("watch-files/command/per-file", False)
        console.info("Watching changes of '%s'..." % paths)
        console.info("Press Ctrl-C to terminate.")
        pattern = self._watch_pattern(jobconf.get("watch-files/include",[])) 
        while True:
            time.sleep(interval)
            ylist = []
            for path in paths:
                console.debug("checking path '%s'" % path)
                part_list = filetool.findYoungest(path, pattern=pattern, includedirs=include_dirs, since=since)
                ylist.extend(part_list)
            since = time.time()
            if ylist:     # ylist =[(fpath,fstamp)]
                flist = [f[0] for f in ylist]
                cmd_args = {'FILELIST': ' '.join(flist)}
                console.debug("found changed files: %s" % flist)
                try:
                    if not per_file:
                        cmd = command_tmpl.safe_substitute(cmd_args)
                        self.runShellCommand(cmd)
                    else:
                        for fname in flist:
                            cmd_args['FILE']      = fname                       # foo/bar/baz.js
                            cmd_args['DIRNAME']   = os.path.dirname(fname)      # foo/bar
                            cmd_args['BASENAME']  = os.path.basename(fname)     # baz.js
                            cmd_args['EXTENSION'] = os.path.splitext(fname)[1]  # .js
                            cmd_args['FILENAME']  = os.path.basename(os.path.splitext(fname)[0])  # baz
                            cmd = command_tmpl.safe_substitute(cmd_args)
                            self.runShellCommand(cmd)
                except RuntimeError:
                    if exit_on_retcode:
                        raise
                    else:
                        pass
        return

    def _watch_pattern(self, include):
        pattern = u''
        a = []
        for entry in include:
            e = textutil.toRegExpS(entry)
            a.append(e)
        pattern = '|'.join(a)
        return pattern

    def runShellCommands(self, jobconf):
        if not jobconf.get("shell/command"):
            return

        shellcmd = jobconf.get("shell/command", "")
        if isinstance(shellcmd, list):
            for cmd in shellcmd:
                self.runShellCommand(cmd)
        else:
            self.runShellCommand(shellcmd)


    def runShellCommand(self, shellcmd):
        rc = 0
        self._shellCmd       = ShellCmd()

        self._console.info("Executing shell command \"%s\"..." % shellcmd)
        self._console.indent()

        rc = self._shellCmd.execute(shellcmd, self._config.getConfigDir())
        if rc != 0:
            raise RuntimeError, "Shell command returned error code: %s" % repr(rc)
        self._console.outdent()
Пример #23
0
class ActionLib(object):
    def __init__(self, config, console_):
        self._config   = config
        self._console  = console_
        self._shellCmd = ShellCmd()

    def clean(self, cleanMap):
        assert isinstance(cleanMap, types.DictType)
        for item in cleanMap:
            self._console.info(item)
            for file in cleanMap[item]:
                file = self._config.absPath(file) 
                # resolve file globs
                for entry in glob.glob(file):
                    # safety first
                    if os.path.splitdrive(entry)[1] == os.sep:
                        raise RuntimeError, "!!! I'm not going to delete '/' recursively !!!"
                    self._shellCmd.rm_rf(entry)



    def watch(self, jobconf, confObj):
        console = Context.console
        since = time.time()
        watcher = Watcher(jobconf, confObj)
        interval = jobconf.get("watch-files/interval", 2)
        paths = jobconf.get("watch-files/paths", [])
        if not paths:
            return
        command = jobconf.get("watch-files/command/line", "")
        if not command:
            return
        command_tmpl = CommandLineTemplate(command)
        per_file = jobconf.get("watch-files/command/per-file", False)
        exit_on_retcode = jobconf.get("watch-files/command/exit-on-retcode", False)
        exec_on_startup = jobconf.get("watch-files/command/exec-on-startup", False)
        if exec_on_startup:
            # simply set check-time to start of epoch 
            since = 1.0  # (since=0 would disable time span checking)
        console.info("Watching changes of '%s'..." % paths)
        console.info("Press Ctrl-C to terminate.")
        while True:
            osince = since
            since = time.time()
            ylist = watcher.check(osince)
            if ylist:     # ylist =[(fpath,fstamp)]
                flist = [f[0] for f in ylist]
                cmd_args = {'FILELIST': ' '.join(flist)}
                console.debug("found changed files: %s" % flist)
                try:
                    if not per_file:
                        cmd = command_tmpl.safe_substitute(cmd_args)
                        self.runShellCommand(cmd)
                    else:
                        for fname in flist:
                            cmd_args['FILE']      = fname                       # foo/bar/baz.js
                            cmd_args['DIRNAME']   = os.path.dirname(fname)      # foo/bar
                            cmd_args['BASENAME']  = os.path.basename(fname)     # baz.js
                            cmd_args['EXTENSION'] = os.path.splitext(fname)[1]  # .js
                            cmd_args['FILENAME']  = os.path.basename(os.path.splitext(fname)[0])  # baz
                            cmd = command_tmpl.safe_substitute(cmd_args)
                            self.runShellCommand(cmd)
                except RuntimeError:
                    if exit_on_retcode:
                        raise
                    else:
                        pass
            time.sleep(interval)
        return

    def runShellCommands(self, jobconf):
        if not jobconf.get("shell/command"):
            return

        shellcmd = jobconf.get("shell/command", "")
        if isinstance(shellcmd, list):
            for cmd in shellcmd:
                self.runShellCommand(cmd)
        else:
            self.runShellCommand(shellcmd)


    def runShellCommand(self, shellcmd):
        rc = 0
        self._shellCmd       = ShellCmd()

        self._console.info("Executing shell command \"%s\"..." % shellcmd)
        self._console.indent()

        rc = self._shellCmd.execute(shellcmd, self._config.getConfigDir())
        if rc != 0:
            raise RuntimeError, "Shell command returned error code: %s" % repr(rc)
        self._console.outdent()
Пример #24
0
def npm_install(skel_dir, options):
    shellCmd = ShellCmd()
    shellCmd.execute('npm install', skel_dir)
    if options.type == 'contribution':
        shellCmd.execute('npm install', os.path.join(skel_dir, 'demo/default'))
Пример #25
0
##
# syntax: $0 <file.js> -- parse JS file and write it to console again
# 
# An experimental hybrid deserializer-serializer that uses 'esparse' to parse
# the JS, then uses a Moz AST to treegenerator_1 AST transformer, and writes out
# the resulting tree.
## 

import re, os, sys, types, codecs
QOOXDOO_PATH = os.path.abspath(os.path.dirname(__file__) + "/../../../..")
execfile(QOOXDOO_PATH + "/tool/bin/qxenviron.py")
from ecmascript.transform import moztree_to_tree1
from generator.runtime.ShellCmd import ShellCmd
from misc import json

shell = ShellCmd()
cmd = "esparse --raw --loc " + sys.argv[1]
#print cmd
rcode, stdout, errout = (
    shell.execute_piped(cmd))
if rcode != 0:
    print errout
    sys.exit(1)
tree_json = json.loads(stdout)
node = moztree_to_tree1.esprima_to_tree1(tree_json)
#print node.toXml()
#import pydb; pydb.debugger()
def opts():pass
opts.breaks = False
print node.toJS(opts)
Пример #26
0
def npm_install(skelDir, options):
    shellCmd = ShellCmd()
    npm_install = 'npm install --loglevel warn'
    console.log("Running '" + npm_install + "'")
    shellCmd.execute(npm_install, skelDir)
Пример #27
0
def npm_install(skel_dir, options):
    shellCmd = ShellCmd()
    shellCmd.execute('npm install', skel_dir)
    if options.type == 'contribution':
        shellCmd.execute('npm install', os.path.join(skel_dir, 'demo/default'))
Пример #28
0
import codecs
import copy
import tempfile

scriptDir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.join(scriptDir, "../../pylib"))

import demjson
from generator.runtime.Log import Log
from generator.runtime.ShellCmd import ShellCmd
from misc import filetool

global console
console = Log(None, "info")
global shell
shell = ShellCmd()

qxPatchReleases = {
    "3.0": "3.0.2",
    "2.1": "2.1.1",
    "2.0": "2.0.4",
    "1.6": "1.6.1",
    "1.5": "1.5.1",
    "1.4": "1.4.2",
    "1.3": "1.3.1",
    "1.2": "1.2.2",
    "1.1": "1.1.2",
    "1.0": "1.0.2"
}