2020-04-02 09:36:24 +00:00
|
|
|
# imports - standard imports
|
2020-04-02 10:40:38 +00:00
|
|
|
import atexit
|
2022-07-27 05:26:46 +00:00
|
|
|
from contextlib import contextmanager
|
|
|
|
from logging import Logger
|
2020-04-02 09:36:24 +00:00
|
|
|
import os
|
|
|
|
import pwd
|
|
|
|
import sys
|
|
|
|
|
|
|
|
# imports - third party imports
|
2014-07-10 17:21:34 +00:00
|
|
|
import click
|
2020-04-02 09:36:24 +00:00
|
|
|
|
|
|
|
# imports - module imports
|
|
|
|
import bench
|
2021-10-14 21:42:19 +00:00
|
|
|
from bench.bench import Bench
|
2016-03-22 07:44:31 +00:00
|
|
|
from bench.commands import bench_command
|
2020-04-02 09:36:24 +00:00
|
|
|
from bench.config.common_site_config import get_config
|
2021-09-09 08:24:19 +00:00
|
|
|
from bench.utils import (
|
|
|
|
check_latest_version,
|
|
|
|
drop_privileges,
|
|
|
|
find_parent_bench,
|
2022-08-02 07:44:55 +00:00
|
|
|
get_env_frappe_commands,
|
2021-09-09 08:24:19 +00:00
|
|
|
get_cmd_output,
|
|
|
|
is_bench_directory,
|
|
|
|
is_dist_editable,
|
|
|
|
is_root,
|
|
|
|
log,
|
|
|
|
setup_logging,
|
2022-07-27 05:26:46 +00:00
|
|
|
get_cmd_from_sysargv,
|
2021-09-09 08:24:19 +00:00
|
|
|
)
|
2021-11-12 20:18:30 +00:00
|
|
|
from bench.utils.bench import get_env_cmd
|
2020-02-01 13:28:37 +00:00
|
|
|
|
feat: Dynamic Output rendering
Each operation can be broken down to multiple jobs. For instance, the
update operation can be broken down into: setting up bench dirs, env,
backups, requirements, etc.
Each step has a lot of output since Frappe requires a lot of complex
stuff to be done as a pre-requisite. Bench tries to simplify a lot in a
single command. Once, a step is completed, it's output is not really
required. So, we can ignore it to reduce the noise.
Here's where the `bench.cli.fancy` variable kicks in. Along with the
refactored log and new step wrapper, it makes the above thing possible.
At this point, there's no way to set this var from the end user...given
I'm still developing this. In the later commits, the idea is to pass a
flag similar to pip's --use-feature flag.
2021-11-19 04:46:19 +00:00
|
|
|
# these variables are used to show dynamic outputs on the terminal
|
2021-11-20 05:46:19 +00:00
|
|
|
dynamic_feed = False
|
2021-11-26 10:54:56 +00:00
|
|
|
verbose = False
|
2021-11-26 10:58:18 +00:00
|
|
|
is_envvar_warn_set = None
|
2022-07-27 09:09:22 +00:00
|
|
|
from_command_line = False # set when commands are executed via the CLI
|
2021-11-17 19:06:13 +00:00
|
|
|
bench.LOG_BUFFER = []
|
feat: Dynamic Output rendering
Each operation can be broken down to multiple jobs. For instance, the
update operation can be broken down into: setting up bench dirs, env,
backups, requirements, etc.
Each step has a lot of output since Frappe requires a lot of complex
stuff to be done as a pre-requisite. Bench tries to simplify a lot in a
single command. Once, a step is completed, it's output is not really
required. So, we can ignore it to reduce the noise.
Here's where the `bench.cli.fancy` variable kicks in. Along with the
refactored log and new step wrapper, it makes the above thing possible.
At this point, there's no way to set this var from the end user...given
I'm still developing this. In the later commits, the idea is to pass a
flag similar to pip's --use-feature flag.
2021-11-19 04:46:19 +00:00
|
|
|
|
2020-03-15 14:01:27 +00:00
|
|
|
change_uid_msg = "You should not run this command as root"
|
2020-08-24 09:58:33 +00:00
|
|
|
src = os.path.dirname(__file__)
|
2014-07-11 07:40:33 +00:00
|
|
|
|
2020-04-02 09:36:24 +00:00
|
|
|
|
2022-07-27 05:26:46 +00:00
|
|
|
@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
|
|
|
|
|
|
|
|
|
2014-07-11 07:40:33 +00:00
|
|
|
def cli():
|
2023-01-24 09:51:24 +00:00
|
|
|
setup_clear_cache()
|
2022-07-27 05:26:46 +00:00
|
|
|
global from_command_line, bench_config, is_envvar_warn_set, verbose
|
2021-11-19 22:46:19 +00:00
|
|
|
|
2015-08-15 17:30:33 +00:00
|
|
|
from_command_line = True
|
2020-05-19 07:41:57 +00:00
|
|
|
command = " ".join(sys.argv)
|
2021-11-19 22:46:19 +00:00
|
|
|
argv = set(sys.argv)
|
2021-11-12 15:00:53 +00:00
|
|
|
is_envvar_warn_set = not (os.environ.get("BENCH_DEVELOPER") or os.environ.get("CI"))
|
2021-11-19 22:46:19 +00:00
|
|
|
is_cli_command = len(sys.argv) > 1 and not argv.intersection({"src", "--version"})
|
2022-07-27 05:26:46 +00:00
|
|
|
cmd_from_sys = get_cmd_from_sysargv()
|
2015-08-15 17:30:33 +00:00
|
|
|
|
2022-07-27 05:26:46 +00:00
|
|
|
if "--verbose" in argv:
|
2021-12-28 07:12:53 +00:00
|
|
|
verbose = True
|
|
|
|
|
2020-02-01 13:28:37 +00:00
|
|
|
change_working_directory()
|
2020-09-03 07:43:24 +00:00
|
|
|
logger = setup_logging()
|
2020-05-19 07:41:57 +00:00
|
|
|
logger.info(command)
|
2020-09-03 07:41:31 +00:00
|
|
|
|
2021-11-13 08:56:37 +00:00
|
|
|
bench_config = get_config(".")
|
|
|
|
|
2021-11-19 22:46:19 +00:00
|
|
|
if is_cli_command:
|
2020-09-03 07:41:31 +00:00
|
|
|
check_uid()
|
|
|
|
change_uid()
|
2020-09-24 07:26:11 +00:00
|
|
|
change_dir()
|
2016-03-22 07:44:31 +00:00
|
|
|
|
2021-11-19 22:46:19 +00:00
|
|
|
if (
|
|
|
|
is_envvar_warn_set
|
|
|
|
and is_cli_command
|
2021-11-13 08:56:37 +00:00
|
|
|
and not bench_config.get("developer_mode")
|
2022-07-27 05:26:46 +00:00
|
|
|
and is_dist_editable(bench.PROJECT_NAME)
|
2021-09-09 08:24:19 +00:00
|
|
|
):
|
|
|
|
log(
|
|
|
|
"bench is installed in editable mode!\n\nThis is not the recommended mode"
|
|
|
|
" of installation for production. Instead, install the package from PyPI"
|
|
|
|
" with: `pip install frappe-bench`\n",
|
|
|
|
level=3,
|
|
|
|
)
|
|
|
|
|
2021-09-29 00:42:19 +00:00
|
|
|
in_bench = is_bench_directory()
|
|
|
|
|
2021-09-09 08:24:19 +00:00
|
|
|
if (
|
2021-09-29 00:42:19 +00:00
|
|
|
not in_bench
|
2021-09-09 08:24:19 +00:00
|
|
|
and len(sys.argv) > 1
|
2022-07-27 09:09:22 +00:00
|
|
|
and not argv.intersection(
|
|
|
|
{"init", "find", "src", "drop", "get", "get-app", "--version"}
|
|
|
|
)
|
2021-11-19 22:46:19 +00:00
|
|
|
and not cmd_requires_root()
|
2021-09-09 08:24:19 +00:00
|
|
|
):
|
2020-04-11 15:50:24 +00:00
|
|
|
log("Command not being executed in bench directory", level=3)
|
|
|
|
|
2022-07-27 08:51:44 +00:00
|
|
|
if len(sys.argv) == 1 or sys.argv[1] == "--help":
|
|
|
|
print(click.Context(bench_command).get_help())
|
|
|
|
if in_bench:
|
2020-04-11 15:50:24 +00:00
|
|
|
print(get_frappe_help())
|
2022-07-27 08:51:44 +00:00
|
|
|
return
|
2016-03-22 07:44:31 +00:00
|
|
|
|
2022-07-27 08:51:44 +00:00
|
|
|
_opts = [x.opts + x.secondary_opts for x in bench_command.params]
|
|
|
|
opts = {item for sublist in _opts for item in sublist}
|
|
|
|
|
|
|
|
# handle usages like `--use-feature='feat-x'` and `--use-feature 'feat-x'`
|
|
|
|
if cmd_from_sys and cmd_from_sys.split("=", 1)[0].strip() in opts:
|
|
|
|
bench_command()
|
|
|
|
|
|
|
|
if cmd_from_sys in bench_command.commands:
|
2022-08-10 08:23:06 +00:00
|
|
|
with execute_cmd(check_for_update=is_cli_command, command=command, logger=logger):
|
2022-07-27 08:51:44 +00:00
|
|
|
bench_command()
|
|
|
|
|
|
|
|
if in_bench:
|
|
|
|
if cmd_from_sys in get_frappe_commands():
|
2021-09-09 09:26:32 +00:00
|
|
|
frappe_cmd()
|
2022-07-27 05:26:46 +00:00
|
|
|
else:
|
2021-09-09 09:26:32 +00:00
|
|
|
app_cmd()
|
2016-03-22 07:44:31 +00:00
|
|
|
|
2022-07-27 08:51:44 +00:00
|
|
|
bench_command()
|
|
|
|
|
2014-07-11 07:40:33 +00:00
|
|
|
|
2016-03-22 07:44:31 +00:00
|
|
|
def check_uid():
|
|
|
|
if cmd_requires_root() and not is_root():
|
2021-09-09 08:24:19 +00:00
|
|
|
log("superuser privileges required for this command", level=3)
|
2016-03-22 07:44:31 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
2020-04-11 15:50:24 +00:00
|
|
|
|
2014-11-10 09:26:09 +00:00
|
|
|
def cmd_requires_root():
|
2021-09-09 08:24:19 +00:00
|
|
|
if len(sys.argv) > 2 and sys.argv[2] in (
|
|
|
|
"production",
|
|
|
|
"sudoers",
|
|
|
|
"lets-encrypt",
|
|
|
|
"fonts",
|
|
|
|
"print",
|
|
|
|
"firewall",
|
|
|
|
"ssh-port",
|
|
|
|
"role",
|
|
|
|
"fail2ban",
|
|
|
|
"wildcard-ssl",
|
|
|
|
):
|
2017-08-30 10:08:17 +00:00
|
|
|
return True
|
2021-09-09 08:24:19 +00:00
|
|
|
if len(sys.argv) >= 2 and sys.argv[1] in (
|
|
|
|
"patch",
|
|
|
|
"renew-lets-encrypt",
|
|
|
|
"disable-production",
|
|
|
|
):
|
2017-08-30 10:08:17 +00:00
|
|
|
return True
|
2021-09-09 08:24:19 +00:00
|
|
|
if len(sys.argv) > 2 and sys.argv[1] in ("install"):
|
2017-08-30 10:08:17 +00:00
|
|
|
return True
|
2014-11-10 09:26:09 +00:00
|
|
|
|
2020-04-11 15:50:24 +00:00
|
|
|
|
2016-03-22 07:44:31 +00:00
|
|
|
def change_dir():
|
2021-09-09 08:24:19 +00:00
|
|
|
if os.path.exists("config.json") or "init" in sys.argv:
|
2016-03-22 07:44:31 +00:00
|
|
|
return
|
2021-09-09 08:24:19 +00:00
|
|
|
dir_path_file = "/etc/frappe_bench_dir"
|
2016-03-22 07:44:31 +00:00
|
|
|
if os.path.exists(dir_path_file):
|
|
|
|
with open(dir_path_file) as f:
|
|
|
|
dir_path = f.read().strip()
|
|
|
|
if os.path.exists(dir_path):
|
|
|
|
os.chdir(dir_path)
|
2014-11-07 14:42:42 +00:00
|
|
|
|
2020-04-11 15:50:24 +00:00
|
|
|
|
2014-11-07 14:42:42 +00:00
|
|
|
def change_uid():
|
2014-11-10 09:26:09 +00:00
|
|
|
if is_root() and not cmd_requires_root():
|
2021-11-13 08:56:37 +00:00
|
|
|
frappe_user = bench_config.get("frappe_user")
|
2014-11-07 14:42:42 +00:00
|
|
|
if frappe_user:
|
2014-11-10 15:51:45 +00:00
|
|
|
drop_privileges(uid_name=frappe_user, gid_name=frappe_user)
|
2021-09-09 08:24:19 +00:00
|
|
|
os.environ["HOME"] = pwd.getpwnam(frappe_user).pw_dir
|
2014-11-07 14:42:42 +00:00
|
|
|
else:
|
2020-03-15 14:01:27 +00:00
|
|
|
log(change_uid_msg, level=3)
|
2014-11-07 14:42:42 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
2020-04-11 15:50:24 +00:00
|
|
|
|
2021-09-09 08:24:19 +00:00
|
|
|
def app_cmd(bench_path="."):
|
2022-08-06 20:06:16 +00:00
|
|
|
f = get_env_cmd("python", bench_path=bench_path)
|
2021-09-09 08:24:19 +00:00
|
|
|
os.chdir(os.path.join(bench_path, "sites"))
|
|
|
|
os.execv(f, [f] + ["-m", "frappe.utils.bench_helper"] + sys.argv[1:])
|
2015-01-13 11:48:03 +00:00
|
|
|
|
2020-04-11 15:50:24 +00:00
|
|
|
|
2021-09-09 08:24:19 +00:00
|
|
|
def frappe_cmd(bench_path="."):
|
2022-08-06 20:06:16 +00:00
|
|
|
f = get_env_cmd("python", bench_path=bench_path)
|
2021-09-09 08:24:19 +00:00
|
|
|
os.chdir(os.path.join(bench_path, "sites"))
|
|
|
|
os.execv(f, [f] + ["-m", "frappe.utils.bench_helper", "frappe"] + sys.argv[1:])
|
2015-03-02 11:42:41 +00:00
|
|
|
|
2020-04-11 15:50:24 +00:00
|
|
|
|
2021-09-09 07:49:44 +00:00
|
|
|
def get_frappe_commands():
|
|
|
|
if not is_bench_directory():
|
2021-12-28 08:06:01 +00:00
|
|
|
return set()
|
2021-09-09 07:49:44 +00:00
|
|
|
|
2022-08-02 07:44:55 +00:00
|
|
|
return set(get_env_frappe_commands())
|
2020-04-11 15:50:24 +00:00
|
|
|
|
|
|
|
|
2021-09-09 08:24:19 +00:00
|
|
|
def get_frappe_help(bench_path="."):
|
2022-08-06 20:06:16 +00:00
|
|
|
python = get_env_cmd("python", bench_path=bench_path)
|
2021-09-09 08:24:19 +00:00
|
|
|
sites_path = os.path.join(bench_path, "sites")
|
2015-03-02 11:42:41 +00:00
|
|
|
try:
|
2021-09-09 08:24:19 +00:00
|
|
|
out = get_cmd_output(
|
|
|
|
f"{python} -m frappe.utils.bench_helper get-frappe-help", cwd=sites_path
|
|
|
|
)
|
|
|
|
return "\n\nFramework commands:\n" + out.split("Commands:")[1]
|
2021-09-09 09:26:32 +00:00
|
|
|
except Exception:
|
2015-03-02 11:42:41 +00:00
|
|
|
return ""
|
2020-02-01 13:28:37 +00:00
|
|
|
|
2020-04-11 15:50:24 +00:00
|
|
|
|
2020-02-01 13:28:37 +00:00
|
|
|
def change_working_directory():
|
|
|
|
"""Allows bench commands to be run from anywhere inside a bench directory"""
|
2020-02-01 18:49:58 +00:00
|
|
|
cur_dir = os.path.abspath(".")
|
|
|
|
bench_path = find_parent_bench(cur_dir)
|
2021-10-19 19:13:46 +00:00
|
|
|
bench.current_path = os.getcwd()
|
|
|
|
bench.updated_path = bench_path
|
2020-02-01 18:49:58 +00:00
|
|
|
|
|
|
|
if bench_path:
|
|
|
|
os.chdir(bench_path)
|
2021-12-02 08:52:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
def setup_clear_cache():
|
|
|
|
from copy import copy
|
2022-07-27 09:09:22 +00:00
|
|
|
|
2021-12-02 08:52:34 +00:00
|
|
|
f = copy(os.chdir)
|
|
|
|
|
|
|
|
def _chdir(*args, **kwargs):
|
|
|
|
Bench.cache_clear()
|
2022-08-02 06:27:38 +00:00
|
|
|
get_env_cmd.cache_clear()
|
2021-12-02 08:52:34 +00:00
|
|
|
return f(*args, **kwargs)
|
|
|
|
|
|
|
|
os.chdir = _chdir
|