webis.py 8.23 KB
Newer Older
1
#!/bin/bash
2 3
# -*- coding: utf-8 ; mode: python -*-
"true" '''\'
4
set -e
5 6 7 8 9 10 11
RP="${BASH_SOURCE[0]}"
while [ -h "$RP" ]; do
  DIR="$( cd -P "$( dirname "$RP" )" && pwd )"
  RP="$(readlink "$RP")"
  [[ $RP != /* ]] && RP="$DIR/$RP"
done
rootpath="$(cd "$(dirname "$RP")" && pwd -P)"
12
. ${rootpath}/libs/bashhelper.sh
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
(cd "$webiscmdrootpath"
    if [ "$1 $2" != "core update" ] && git_repo_has_updates; then
        logWarn "Your webis-cmd is outdated. You need to update your installation before you can use it."
        if ! yes_no_prompt "Do you want to update now?" "y"; then
            logError "Canceling update. Please re-run the command or update manually with '${0} core update'."
            exit 1
        fi
        if ! "$0" core update; then
            logError "Update failed."
            exit 1
        fi
        logInfo "Update successful, relaunching webis-cmd..."
        exec "$0" "$@"
    fi
)
28

29
# try running as python3, if that fails fall back to (any) python
30
if command -v python3 > /dev/null; then
31 32
    exec env python3 "$0" "$@"
fi
33
if command -v python > /dev/null; then
34 35 36 37 38 39 40
    exec env python "$0" "$@"
else
    echo -e "\033[91m[ERROR] Install Python and try again!" 1>&2
    exit 1
fi
'''
__doc__ = """The main webis command.
Steve Goering's avatar
Steve Goering committed
41

Martin Potthast's avatar
Martin Potthast committed
42
Copyright webis.de 2015-today
43
Authors: Janek Bevendorff, Steve Göring, Martin Potthast, Michael Völske
Steve Goering's avatar
Steve Goering committed
44
"""
Martin Potthast's avatar
Martin Potthast committed
45

46
import argparse
Martin Potthast's avatar
Martin Potthast committed
47
import collections
48
import json
Martin Potthast's avatar
Martin Potthast committed
49 50
import os
import sys
51

Steve Goering's avatar
Steve Goering committed
52 53
import loader

54
from lib import *
Martin Potthast's avatar
Martin Potthast committed
55
from log import *
56 57 58
from system import *


59 60 61 62 63 64 65
def check_commands_alias_definition(config):
    """Checks if the command alias definition has no collisions."""
    aliashisto = {}
    for command in config["commands_aliases"]:
        for alias in config["commands_aliases"][command]:
            aliashisto[alias] = aliashisto.get(alias, 0) + 1

Steve Goering's avatar
Steve Goering committed
66
    if len([x for x in aliashisto if aliashisto[x] > 1]):
67 68 69 70
        lError("Commands alias definition has collisions, please fix it.")
        sys.exit(1)


Martin Potthast's avatar
Martin Potthast committed
71 72 73
def load_config():
    """Returns the configuration, based on the JSON configuration file."""
    try:
74
        config = json.loads(read_file(os.path.dirname(
Martin Potthast's avatar
Martin Potthast committed
75
            os.path.realpath(__file__)) + "/config.json"))
76 77 78 79 80 81 82 83 84 85 86 87 88

        if not os.path.isdir(config["commands_directory"]):
            config["commands_directory"] = os.path.dirname(
                os.path.realpath(__file__)) + "/" + config["commands_directory"]

        if not os.path.isdir(config["commands_directory"]):
            lError(
                "moduls_directory {} is not a valid directory, "
                "check config.json".format(config["commands_directory"]))
            sys.exit(1)

        return config

Martin Potthast's avatar
Martin Potthast committed
89 90 91
    except Exception as e:
        lError("The file config.json is invalid.")
        sys.exit(1)
92

Steve Goering's avatar
Steve Goering committed
93

Martin Potthast's avatar
Martin Potthast committed
94 95 96
def get_commands(config):
    """Returns commands mapped to directories containing their subcommands."""
    commands = collections.OrderedDict()
97 98
    for directory in os.listdir(config["commands_directory"]):
        if (os.path.isdir(config["commands_directory"] + directory) and
Steve Goering's avatar
Steve Goering committed
99
                directory not in config["ignored_dirs"]):
100
            commands[directory] = config["commands_directory"] + directory
Martin Potthast's avatar
Martin Potthast committed
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
    return commands


def get_subcommands(config, commandpath):
    """Returns subcommands of a given command mapped to their script files."""
    subcommands = collections.OrderedDict()
    for script in os.listdir(commandpath):
        subcommandpath = commandpath + "/" + script
        if os.path.isfile(subcommandpath):
            allowed_scripts = config["allowed_scripts"]
            if check_subcommand_shebang(subcommandpath, allowed_scripts):
                subcommand = script.rsplit(".", 1)[0]
                subcommands[subcommand] = subcommandpath
    return subcommands


def get_subcommand_help(subcommandpath):
    """Returns the help message expected at the beginning of a command."""
    if not os.path.isfile(subcommandpath):
        return os.path.basename(subcommandpath)

    f = open(subcommandpath)
123 124 125 126 127 128 129
    lines = f.readlines()
    f.close()
    if len(lines) < 1 or lines[1][0] != "#":
        return ""
    return lines[1][1:].replace("\n", "").strip()


130 131 132
def print_help_table(config):
    """Prints a table with all commands, subcommands, and their descriptions."""
    commands = get_commands(config)
Johannes Kiesel's avatar
Johannes Kiesel committed
133
    for command in sorted(commands):
134
        command_help = str(config["commands_help"].get(command))
135
        print("{:20s} {}".format(command, command_help))
136
        subcommands = get_subcommands(config, commands[command])
Johannes Kiesel's avatar
Johannes Kiesel committed
137
        for subcommand in sorted(subcommands):
138
            subcommand_help = str(get_subcommand_help(subcommands[subcommand]))
139 140
            print("    {:20s} {}".format(subcommand, subcommand_help))
        print("")
141 142


Martin Potthast's avatar
Martin Potthast committed
143 144 145 146 147 148 149
def check_subcommand_shebang(subcommandpath, allowed_scripts):
    """Checks if the shebang of a given script is among the allowed shebangs."""
    shebang = get_shebang(subcommandpath)
    for interpreter in allowed_scripts:
        if interpreter in shebang:
            return True
    return False
Steve Goering's avatar
Steve Goering committed
150

151

Martin Potthast's avatar
Martin Potthast committed
152 153 154
def run_subcommand(subcommand, subcommandpath, params=[]):
    """Runs subcommand passing params and returns its exit code."""
    lDbg("Running " + subcommand + " at " + subcommandpath)
155 156 157
    pp = os.environ.get('PYTHONPATH')
    webis_dir = os.path.dirname(os.path.realpath(__file__))
    os.environ['PYTHONPATH'] = webis_dir if pp is None else pp + ':' + webis_dir
158 159
    if is_verbose_logging():
        os.environ['WEBIS_DEBUG'] = '1'
Martin Potthast's avatar
Martin Potthast committed
160
    cmd = " ".join([subcommandpath] + params)
161 162
    return_value = os.system(cmd)
    if return_value == 0:
163
        lDbg("Done.")
164
    else:
Martin Potthast's avatar
Martin Potthast committed
165
        lError("An error occurred while executing " + cmd)
166
    return return_value
167

168

169 170 171
def translate_alias_to_command(config, alias):
    """Transforms a given alias to the unique command."""
    for command in config["commands_aliases"]:
Steve Goering's avatar
Steve Goering committed
172
        if alias in config["commands_aliases"][command]:
173 174
            return command

175 176

def main(args):
Martin Potthast's avatar
Martin Potthast committed
177
    config = load_config()
178 179
    check_commands_alias_definition(config)

Martin Potthast's avatar
Martin Potthast committed
180 181
    commands = get_commands(config)
    parser = argparse.ArgumentParser()
182
    parser.register('action', 'parsers', AliasedSubParsersAction)
183 184 185
    parser.add_argument('-H', '--helptable',
                        help='Print help of commands and subcommands',
                        action='store_true')
186 187
    parser.add_argument('-v', '--verbose', help='Print debug messages.',
                        action='store_true')
188

Martin Potthast's avatar
Martin Potthast committed
189 190 191 192 193
    subparsers = {}
    allcommands = {}
    commandparsers = parser.add_subparsers(
        metavar='<command>', dest='<command>', help='Available commands:')
    for command in commands:
194 195 196 197 198 199
        help_msg = config["commands_help"].get(
            command,
            colorred(
                "Please add a short description of {} "
                "in config.json[commands_help].".format(command)))

Steve Goering's avatar
Steve Goering committed
200
        commandparser = commandparsers.add_parser(
201 202 203
            command, aliases=config["commands_aliases"].get(command, []),
            help=help_msg)

Martin Potthast's avatar
Martin Potthast committed
204 205 206 207 208 209 210 211 212
        subparsers[command] = commandparser
        subcommandparsers = commandparser.add_subparsers(
            metavar='<subcommand>', dest='<subcommand>',
            help='Available subcommands:')
        subcommands = get_subcommands(config, commands[command])
        allcommands[command] = subcommands
        for subcommand in subcommands:
            subcommandparser = subcommandparsers.add_parser(
                subcommand, help=str(
Steve Goering's avatar
Steve Goering committed
213
                    get_subcommand_help(subcommands[subcommand])),
Martin Potthast's avatar
Martin Potthast committed
214 215 216 217 218
                add_help=False)
            subparsers[subcommand] = subcommandparser

    if len(args) == 0:
        parser.print_help()
219 220
        return 0

Martin Potthast's avatar
Martin Potthast committed
221 222
    parseresults = parser.parse_known_args(args)
    argsdict = vars(parseresults[0])
223
    set_verbose_logging(argsdict['verbose'])
Martin Potthast's avatar
Martin Potthast committed
224
    parameter = parseresults[1]
Steve Goering's avatar
Steve Goering committed
225

226 227 228 229
    if argsdict['helptable']:
        print_help_table(config)
        return 0

Steve Goering's avatar
Steve Goering committed
230
    if argsdict['<command>'] not in subparsers:
Steve Goering's avatar
Steve Goering committed
231 232
        argsdict['<command>'] = translate_alias_to_command(
            config, argsdict['<command>'])
233

234
    if argsdict['<subcommand>'] not in allcommands[argsdict['<command>']]:
Steve Goering's avatar
Steve Goering committed
235
        subparsers[argsdict['<command>']].print_help()
Martin Potthast's avatar
Martin Potthast committed
236
        return 0
237

Steve Goering's avatar
Steve Goering committed
238 239 240 241
    return run_subcommand(
        argsdict['<subcommand>'],
        allcommands[argsdict['<command>']][argsdict['<subcommand>']],
        parameter)
242 243 244


if __name__ == "__main__":
Janek Bevendorff's avatar
Janek Bevendorff committed
245
    sys.exit(main(sys.argv[1:]) >> 8)