mirror of
https://github.com/frappe/bench.git
synced 2025-01-08 16:14:12 +00:00
refactor(cli): Commands Resolution
The implementations so far were hacks that worked for the most used commands but broken down when challenged or expected to maintain documented usages [eg: custom app commands]. The current implementation consists of a two step approach: 1. figure out command name that user is trying to execute 2. pass the directive to the app (bench, frappe or other) that consists of the cmd --- For tackling #1, get_cmd_from_sysargv contains exhaustive rules that cover all (that i know and ive come across) combinations of valid frappe commands. For problem #2, a simple check in click's Group object does the trick. Tested with possible known commands with combinations of context flags and params, with bench, frappe & external app's commands
This commit is contained in:
parent
beac865153
commit
a6f196440a
59
bench/cli.py
59
bench/cli.py
@ -1,6 +1,8 @@
|
|||||||
# imports - standard imports
|
# imports - standard imports
|
||||||
import atexit
|
import atexit
|
||||||
|
from contextlib import contextmanager
|
||||||
import json
|
import json
|
||||||
|
from logging import Logger
|
||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
import sys
|
import sys
|
||||||
@ -25,7 +27,7 @@ from bench.utils import (
|
|||||||
is_root,
|
is_root,
|
||||||
log,
|
log,
|
||||||
setup_logging,
|
setup_logging,
|
||||||
parse_sys_argv,
|
get_cmd_from_sysargv,
|
||||||
)
|
)
|
||||||
from bench.utils.bench import get_env_cmd
|
from bench.utils.bench import get_env_cmd
|
||||||
|
|
||||||
@ -35,23 +37,41 @@ verbose = False
|
|||||||
is_envvar_warn_set = None
|
is_envvar_warn_set = None
|
||||||
from_command_line = False # set when commands are executed via the CLI
|
from_command_line = False # set when commands are executed via the CLI
|
||||||
bench.LOG_BUFFER = []
|
bench.LOG_BUFFER = []
|
||||||
sys_argv = None
|
|
||||||
|
|
||||||
change_uid_msg = "You should not run this command as root"
|
change_uid_msg = "You should not run this command as root"
|
||||||
src = os.path.dirname(__file__)
|
src = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def execute_cmd(check_for_update=True, command: str = None, logger: Logger = None):
|
||||||
|
if check_for_update:
|
||||||
|
atexit.register(check_latest_version)
|
||||||
|
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
except BaseException as e:
|
||||||
|
return_code = getattr(e, "code", 1)
|
||||||
|
|
||||||
|
if isinstance(e, Exception):
|
||||||
|
click.secho(f"ERROR: {e}", fg="red")
|
||||||
|
|
||||||
|
if return_code:
|
||||||
|
logger.warning(f"{command} executed with exit code {return_code}")
|
||||||
|
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def cli():
|
def cli():
|
||||||
global from_command_line, bench_config, is_envvar_warn_set, verbose, sys_argv
|
global from_command_line, bench_config, is_envvar_warn_set, verbose
|
||||||
|
|
||||||
from_command_line = True
|
from_command_line = True
|
||||||
command = " ".join(sys.argv)
|
command = " ".join(sys.argv)
|
||||||
argv = set(sys.argv)
|
argv = set(sys.argv)
|
||||||
is_envvar_warn_set = not (os.environ.get("BENCH_DEVELOPER") or os.environ.get("CI"))
|
is_envvar_warn_set = not (os.environ.get("BENCH_DEVELOPER") or os.environ.get("CI"))
|
||||||
is_cli_command = len(sys.argv) > 1 and not argv.intersection({"src", "--version"})
|
is_cli_command = len(sys.argv) > 1 and not argv.intersection({"src", "--version"})
|
||||||
sys_argv = parse_sys_argv()
|
cmd_from_sys = get_cmd_from_sysargv()
|
||||||
|
|
||||||
if "--verbose" in sys_argv.options:
|
if "--verbose" in argv:
|
||||||
verbose = True
|
verbose = True
|
||||||
|
|
||||||
change_working_directory()
|
change_working_directory()
|
||||||
@ -69,8 +89,8 @@ def cli():
|
|||||||
if (
|
if (
|
||||||
is_envvar_warn_set
|
is_envvar_warn_set
|
||||||
and is_cli_command
|
and is_cli_command
|
||||||
and is_dist_editable(bench.PROJECT_NAME)
|
|
||||||
and not bench_config.get("developer_mode")
|
and not bench_config.get("developer_mode")
|
||||||
|
and is_dist_editable(bench.PROJECT_NAME)
|
||||||
):
|
):
|
||||||
log(
|
log(
|
||||||
"bench is installed in editable mode!\n\nThis is not the recommended mode"
|
"bench is installed in editable mode!\n\nThis is not the recommended mode"
|
||||||
@ -95,31 +115,14 @@ def cli():
|
|||||||
print(get_frappe_help())
|
print(get_frappe_help())
|
||||||
return
|
return
|
||||||
|
|
||||||
if (
|
if cmd_from_sys in bench_command.commands:
|
||||||
sys_argv.commands.intersection(get_cached_frappe_commands())
|
with execute_cmd(check_for_update=not is_cli_command, command=command, logger=logger):
|
||||||
or sys_argv.commands.intersection(get_frappe_commands())
|
bench_command()
|
||||||
):
|
elif cmd_from_sys in get_frappe_commands():
|
||||||
frappe_cmd()
|
frappe_cmd()
|
||||||
|
else:
|
||||||
if sys.argv[1] in Bench(".").apps:
|
|
||||||
app_cmd()
|
app_cmd()
|
||||||
|
|
||||||
if not is_cli_command:
|
|
||||||
atexit.register(check_latest_version)
|
|
||||||
|
|
||||||
try:
|
|
||||||
bench_command()
|
|
||||||
except BaseException as e:
|
|
||||||
return_code = getattr(e, "code", 1)
|
|
||||||
|
|
||||||
if isinstance(e, Exception):
|
|
||||||
click.secho(f"ERROR: {e}", fg="red")
|
|
||||||
|
|
||||||
if return_code:
|
|
||||||
logger.warning(f"{command} executed with exit code {return_code}")
|
|
||||||
|
|
||||||
raise e
|
|
||||||
|
|
||||||
|
|
||||||
def check_uid():
|
def check_uid():
|
||||||
if cmd_requires_root() and not is_root():
|
if cmd_requires_root() and not is_root():
|
||||||
|
@ -165,7 +165,7 @@ def which(executable: str, raise_err: bool = False) -> str:
|
|||||||
return exec_
|
return exec_
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(bench_path=".") -> "logger":
|
def setup_logging(bench_path=".") -> logging.Logger:
|
||||||
LOG_LEVEL = 15
|
LOG_LEVEL = 15
|
||||||
logging.addLevelName(LOG_LEVEL, "LOG")
|
logging.addLevelName(LOG_LEVEL, "LOG")
|
||||||
|
|
||||||
@ -529,13 +529,41 @@ class _dict(dict):
|
|||||||
return _dict(dict(self).copy())
|
return _dict(dict(self).copy())
|
||||||
|
|
||||||
|
|
||||||
def parse_sys_argv():
|
def get_cmd_from_sysargv():
|
||||||
sys_argv = _dict(options=set(), commands=set())
|
"""Identify and segregate tokens to options and command
|
||||||
|
|
||||||
for c in sys.argv[1:]:
|
For Command: `bench --profile --site frappeframework.com migrate --no-backup`
|
||||||
if c.startswith("-"):
|
sys.argv: ["/home/frappe/.local/bin/bench", "--profile", "--site", "frappeframework.com", "migrate", "--no-backup"]
|
||||||
sys_argv.options.add(c)
|
Actual command run: migrate
|
||||||
else:
|
|
||||||
sys_argv.commands.add(c)
|
|
||||||
|
|
||||||
return sys_argv
|
"""
|
||||||
|
# context is passed as options to frappe's bench_helper
|
||||||
|
from bench.bench import Bench
|
||||||
|
frappe_context = _dict(
|
||||||
|
params={"--site"},
|
||||||
|
flags={"--verbose", "--profile", "--force"}
|
||||||
|
)
|
||||||
|
cmd_from_ctx = None
|
||||||
|
sys_argv = sys.argv[1:]
|
||||||
|
skip_next = False
|
||||||
|
|
||||||
|
for arg in sys_argv:
|
||||||
|
if skip_next:
|
||||||
|
skip_next = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
if arg in frappe_context.flags:
|
||||||
|
continue
|
||||||
|
|
||||||
|
elif arg in frappe_context.params:
|
||||||
|
skip_next = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if sys_argv.index(arg) == 0 and arg in Bench(".").apps:
|
||||||
|
continue
|
||||||
|
|
||||||
|
cmd_from_ctx = arg
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
return cmd_from_ctx
|
||||||
|
Loading…
Reference in New Issue
Block a user