Пример #1
0
def __run_dependencies(config, should_run):
    store = config_to_dict(config)
    importlib.reload(config)
    importlib.reload(config.confply)
    confply.config.run = should_run
    depends_return = 0
    if len(store["confply"]["dependencies"]) > 0:
        for d in store["confply"]["dependencies"]:
            if d not in __configs_run:
                confply.config.log_topic = store["confply"]["log_topic"]
                log.normal("running dependency: " + str(d))
                confply.config.log_topic = "confply"
                log.linebreak()
                __configs_run.add(d)
                depends_return = run_commandline(["--in", d])
                if depends_return < 0:
                    confply.config.log_topic = store["confply"]["log_topic"]
                    confply.config.log_file = store["confply"]["log_file"]
                    log.error("failed to run: " + str(d))
                    if not input_prompt("continue execution?"):
                        log.normal("aborting final commands")
                        break
                    else:
                        log.normal("continuing execution.")
        pass
    # reset confply.config
    apply_to_config(store)
    # #todo: make this jump to the mail section
    return depends_return
Пример #2
0
def __handle_version_arg(in_args):
    log.linebreak()
    log.normal("Confply " + get_version())
    log.normal("Copyright (C) 2021 Graham Hughes.")
    log.normal("License MIT.")
    log.normal("This is free software; " +
               "you are free to change and redistribute it.")
    log.normal("There is NO WARRANTY, to the extent permitted by law.")
Пример #3
0
def launcher(in_args, aliases):
    return_code = -999999

    def print_header():
        log.confply_header()
        log.linebreak()

    confply_dir = __get_confply_dir()
    if len(in_args) != 0:
        alias = in_args[0]
        args = " ".join(in_args[1:])

        if alias in aliases:
            system_code = 0
            cmd = ("python " + confply_dir + "/confply.py ")
            cmd += (aliases[alias] + " " + args)
            cmd = cmd.replace(" -- ", " " + args + " -- ")
            # windows doesn't support os.WEXITSTATUS
            if os.name == 'nt':
                system_code = os.system(cmd)
            else:
                system_code = os.WEXITSTATUS(os.system(cmd))

            if (system_code > return_code) and (system_code != 0):
                return_code = system_code
        else:
            print_header()
            log.error(alias + " is not in aliases.")
            return_code = -1
    else:
        print_header()
        log.error("no arguements supplied.")
        with open(os.path.join(confply_dir, "help.md")) as help_file:
            print("\n" + help_file.read())
        return_code = -1
    log.linebreak()

    if (return_code != -999999):
        exit(abs(return_code))
    else:
        exit(0)
Пример #4
0
def run_dict(in_dict):
    """
    runs the confply dict for the supplied config_type

    usage: run_dict(in_dict)
    """
    log.linebreak()
    log.header("run config")
    log.linebreak()

    # setup config run
    should_run = confply.config.run
    if ("config_path" in in_dict["confply"]):
        path = in_dict["confply"]["config_path"]
    else:
        path = None
    __load_vcs_info(path)
    confply.config.config_path = path
    config_name = os.path.basename(path)
    confply.config.config_name = config_name
    confply.config.modified = os.path.getmtime(path).real
    config_locals = apply_to_config(in_dict)
    if not config_locals:
        log.error("failed to load:  json")
        return -1

    if ("config" not in config_locals):
        log.error("confply failed import")
        log.normal(
            "\tuse: '__package__': 'confply.[config_type].config' in json/ini")
        clean_modules()
        return -1

    if (not __validate_types(config_locals["config"])):
        log.error("failed to run config")
        return -1

    # ensure we don't run if should_run was EVER false
    if should_run is not True:
        confply.config.run = should_run
    return __run_config(config_locals)
Пример #5
0
def __run_shell_cmd(shell_cmd, cmd_env, tool):
    if confply.config.log_file:
        sys.stdout.flush()
        # #todo: check if this can be ansi-coloured
        result = subprocess.run(shell_cmd,
                                stdout=sys.stdout,
                                stderr=subprocess.STDOUT,
                                text=True,
                                shell=True,
                                env=cmd_env)
    else:
        result = subprocess.run(shell_cmd, shell=True, env=cmd_env)

    if result.returncode == 0:
        log.linebreak()
        log.success(tool + " succeeded!")
        return 0
    else:
        log.linebreak()
        log.error(tool + " failed.")
        log.error(tool + " return code: " + str(result.returncode))
        return -2
Пример #6
0
#!/usr/bin/env python
import os
import sys
import confply
import confply.log as log

if __name__ == "__main__":
    in_args = sys.argv[1:]
    version = sys.version_info
    version = (version.major, version.minor, version.micro)
    if "--no_header" not in in_args:
        log.confply_header()
        log.linebreak()
        log.normal("python" + str(version))
        log.normal("confply " + confply.get_version())
        log.normal("date: " + str(confply.datetime.now()))
    if (version[0] < 3 or (version[0] == 3 and version[1] < 8)):
        log.error("python version must be 3.8 or above")
        log.linebreak()
        exit(1)

    return_code = -999999
    if len(in_args) != 0:
        while len(in_args) > 0:
            cmd_return = confply.run_commandline(in_args)

            if (cmd_return > return_code and cmd_return != 0):
                return_code = cmd_return

            # fatal error.
            if return_code == -1:
Пример #7
0
 def print_header():
     log.confply_header()
     log.linebreak()
Пример #8
0
def __run_config(config_locals):
    in_args = confply.config.args
    config = config_locals["config"]
    return_code = 0
    should_run = confply.config.run
    file_path = confply.config.config_path
    __apply_overrides(config)
    new_working_dir = os.path.dirname(file_path)
    old_stdout = sys.stdout
    # setting confply command configuration up
    with pushd(new_working_dir):
        if confply.config.log_file:
            log.normal("writing to: " + confply.config.log_file + "....")
            try:
                sys.stdout = open(confply.config.log_file, "w")
                version = sys.version_info
                version = (version.major, version.minor, version.micro)
                if "--no_header" not in in_args:
                    log.confply_header()
                    log.linebreak()
                    log.normal("python" + str(version))
                    log.normal("confply " + get_version())
                    log.normal("date: " + str(datetime.now()))
                    log.linebreak()
                log.normal("confply logging to " + confply.config.log_file)
            except Exception:
                log.error("couldn't open " + confply.config.log_file +
                          " for write.")
                return_code = -1
        try:
            if (confply.config.post_load
                    and inspect.isfunction(confply.config.post_load)):
                log.normal("running post load script: " +
                           confply.config.post_load.__name__)
                sys.stdout.flush()
                exec(confply.config.post_load.__code__, config_locals, {})
                log.linebreak()

        except Exception:
            log.error("failed to exec " + confply.config.post_load.__name__)
            trace = traceback.format_exc()
            log.normal("traceback:\n\n" + trace)
            pass

        diff_config = __get_diff_config(config)
        should_run = confply.config.run
        report = {
            "config_path": file_path,
            "config_json": json.dumps(diff_config, indent=4),
            "config_type": "unknown",
            "tool": "unknown",
            "status": "failure",
            "vcs_root": confply.config.vcs_root,
            "vcs_log": confply.config.vcs_log,
            "vcs": confply.config.vcs,
            "vcs_branch": confply.config.vcs_branch,
            "vcs_author": confply.config.vcs_author,
            "time_taken": "00:00:00"
        }
        if return_code >= 0:
            if confply.config.log_config is not False:
                __print_config(os.path.basename(file_path), config)
            try:
                time_start = timeit.default_timer()
                # #todo: tool selection phase should happen first.
                tools = __validate_config(config)
                config_type = config.__package__
                tool = confply.config.tool
                report["tool"] = tool
                report["config_type"] = config_type
                if tools:
                    shell_cmd = tools[tool]
                    shell_cmd.handle_args()
                    # #todo: rename generate to gen_config_type.
                    shell_cmd = shell_cmd.generate() if tools else None
                else:
                    shell_cmd = None

                __run_dependencies(config, should_run)

                if shell_cmd is not None:
                    cmd_env = tools[tool].get_environ()
                    if len(shell_cmd) > 0:
                        if isinstance(shell_cmd, list):
                            log.normal("final commands:\n")
                            for shell_str in shell_cmd:
                                cmd = confply.config.command_prepend + shell_str
                                cmd = cmd + confply.config.command_append
                                print(cmd)
                            print("")
                        else:
                            cmd = confply.config.command_prepend + shell_cmd
                            cmd = shell_cmd + confply.config.command_append
                            log.normal("final command:\n\n" + str(cmd) + "\n")
                        if should_run:
                            log.header("begin " + tool)
                    sys.stdout.flush()

                    if should_run and isinstance(shell_cmd, list):
                        for cmd in shell_cmd:
                            cmd_time_start = timeit.default_timer()
                            sys.stdout.flush()
                            log.linebreak()
                            cmd = confply.config.command_prepend + cmd
                            cmd = cmd + confply.config.command_append
                            log.normal(cmd)
                            log.normal("", flush=True)
                            return_code = __run_shell_cmd(cmd, cmd_env, tool)
                            cmd_time_end = timeit.default_timer()
                            # #todo: this can be tidied with format var capture
                            s = cmd_time_end - cmd_time_start
                            m = int(s / 60)
                            h = int(m / 60)
                            # time formating via format specifiers
                            # https://docs.python.org/3.8/library/string.html#formatspec
                            time = f"{h:0>2.0f}:{m:0>2.0f}:{s:0>5.2f}"
                            log.normal("time elapsed " + time)
                    elif should_run:
                        return_code = __run_shell_cmd(shell_cmd, cmd_env, tool)
                    else:
                        log.warning("no commands run")
                else:
                    log.error("failed to generate a valid command.")
                    return_code = -1

                time_end = timeit.default_timer()
                s = time_end - time_start
                m = int(s / 60)
                h = int(m / 60)
                # time formating via format specifiers
                # https://docs.python.org/3.8/library/string.html#formatspec
                time = f"{h:0>2.0f}:{m:0>2.0f}:{s:0>5.2f}"
                log.normal("total time elapsed " + time)
                report["time_taken"] = time
                report["status"] = "success" if return_code == 0 else "failure"

            except Exception:
                log.error("failed to run config: ")
                trace = traceback.format_exc()
                log.normal("traceback:\n\n" + trace)
                return_code = -1
            sys.stdout.flush()
            if (confply.config.post_run
                    and inspect.isfunction(confply.config.post_run)):
                try:
                    log.linebreak()
                    log.normal("running post run script: " +
                               confply.config.post_run.__name__)
                    sys.stdout.flush()
                    exec(confply.config.post_run.__code__, config_locals, {})
                    log.linebreak()
                except Exception:
                    log.error("failed to exec " +
                              confply.config.post_run.__name__)
                    trace = traceback.format_exc()
                    log.normal("traceback:\n\n" + trace)
            log.normal("date: " + str(datetime.now()))
            log.linebreak()
            sys.stdout.flush()

            if sys.stdout != old_stdout:
                sys.stdout.close()
                sys.stdout = old_stdout
                if confply.config.log_echo_file:
                    with open(confply.config.log_file) as out_log:
                        log_str = out_log.read()
                        log_str = log_str.split("confply logging to " +
                                                confply.config.log_file)[1]
                        log.normal("wrote:" + log_str)

            if (confply.config.mail_send == report["status"]
                    or confply.config.mail_send == "all"):
                mail.host = confply.config.__mail_host
                mail.sender = confply.config.mail_from
                mail.recipients = confply.config.mail_to
                mail.login = confply.config.__mail_login
                mail.attachments = confply.config.mail_attachments
                if (confply.config.log_file and report["status"] == "failure"):
                    mail.attachments.append(
                        os.path.abspath(confply.config.log_file))
                    pass
                if mail.login:
                    mail.send_report(report)

            if (confply.config.slack_send == report["status"]
                    or confply.config.slack_send == "all"):
                slack.bot_token = confply.config.__slack_bot_token
                slack.uploads = confply.config.slack_uploads
                if (confply.config.log_file and report["status"] == "failure"):
                    slack.uploads.append(
                        os.path.abspath(confply.config.log_file))
                if slack.bot_token:
                    slack.send_report(report)
    clean_modules()
    return return_code
Пример #9
0
def load_config(path):
    global config_modules
    if os.name == "nt":
        confply.config.platform = "windows"
    elif os.name == "posix":
        confply.config.platform = "linux"

    __load_vcs_info(path)
    # find group config in parent directories
    directory_paths = __get_group_configs(path)
    directory_paths.append(path)
    config_locals = {}
    # load and execute the config files
    for dir_path in directory_paths:
        if dir_path is None:
            continue
        if os.path.exists(dir_path) and os.path.isfile(dir_path):
            config_name = os.path.basename(dir_path)
            confply.config.config_name = config_name
            confply.config.modified = os.path.getmtime(dir_path).real
            with open(dir_path) as config_file:
                with pushd(os.path.dirname(dir_path)):
                    try:
                        exec(config_file.read(), {}, config_locals)
                        # find imported confply configs for cleanup
                        for k, v in config_locals.items():
                            m_valid = isinstance(v, types.ModuleType)
                            if not m_valid:
                                continue

                            v_name = v.__name__
                            m_valid = m_valid and v not in config_modules
                            m_valid = m_valid and v_name.startswith("confply.")
                            m_valid = m_valid and v_name.endswith(".config")
                            if m_valid:
                                config_modules.add(v)

                        # validate there are less than 2 imported configs
                        if len(config_modules) > 1:
                            log.error("too many confply configs imported:")
                            for c in config_modules:
                                log.normal("\t " + c)
                            log.normal(
                                "confply only supports one config import.")
                            clean_modules()
                            return None, None

                        log.linebreak()
                        log.success("loaded: " + str(dir_path))
                    except Exception:
                        log.error("failed to exec: " + str(dir_path))
                        trace = traceback.format_exc().replace(
                            "<string>", str(dir_path))
                        log.normal("traceback:\n\n" + trace)
                        return None, None

        else:
            log.error("failed to find: " + str(dir_path))
            return None, None

    return config_locals
Пример #10
0
def run_commandline(in_args):
    """
    runs the confply config, with supplied arguements.
    confply reservered options will be stripped. e.g. --help
    see help.md

    usage: run_commandline(["path_to_config", "optional", "arguements"])
    """
    log.normal("called with args: " + str(in_args))
    in_args = __strip_confply_args(in_args)
    confply.config.args = in_args
    if confply.config.config_path:
        config_path = confply.config.config_path
        if not os.path.exists(config_path):
            log.error("couldn't find: " + config_path)
            return -1
        if config_path.endswith(".py"):
            log.linebreak()
            log.header("run config")
            log.linebreak()
            # setup config run
            should_run = confply.config.run
            config_locals = load_config(config_path)
            if not config_locals:
                log.error("failed to load: " + config_path)
                return -1

            if ("config" not in config_locals):
                log.error("confply config incorrectly imported")
                log.normal(
                    "\tuse: 'import confply.[config_type].config as config'")
                clean_modules()
                return -1

            config = config_locals["config"]
            if (not __validate_types(config)):
                log.error("failed to run config")
                return -1
            # ensure we don't run if should_run was EVER false
            if should_run is not True:
                confply.config.run = should_run
            # config
            config_hash = md5_file(config.__file__)
            if ("config_hash" not in config_locals
                    or config_hash != config_locals["config_hash"]):
                log.warning("warning: config_hash doesn't match expected hash")
                log.normal("\tconfig file might not function correctly")
                log.normal("\texpected:")
                log.normal("\t\t" + "config_hash='" + config_hash + "'")
                log.normal("")
            return __run_config(config_locals)

        elif config_path.endswith(".json"):
            if os.path.exists(config_path):
                with open(config_path) as in_json:
                    in_dict = json.loads(in_json.read())
                    in_dict["confply"].update({"config_path": config_path})
                    return run_dict(in_dict)
        elif config_path.endswith(".ini"):
            if os.path.exists(config_path):
                import configparser

                def parse_lit(in_val):
                    try:
                        return ast.literal_eval(in_val)
                    except Exception:
                        return in_val

                conf = configparser.ConfigParser()
                conf.read(config_path)
                in_dict = {"confply": {"config_path": config_path}}
                for (key, val) in conf["config"].items():
                    in_dict[key] = parse_lit(val)
                for (key, val) in conf["confply"].items():
                    in_dict["confply"][key] = parse_lit(val)
                return run_dict(in_dict)
        else:
            log.error("unsupported config type: " + config_path)
            return -1
    else:
        return 0
    return 0