mirror of
https://github.com/frappe/bench.git
synced 2024-11-18 11:05:12 +00:00
Merge branch 'develop' into fix-delete
This commit is contained in:
commit
70cb41fa0d
15
README.md
15
README.md
@ -141,12 +141,19 @@ In case the setup fails, the log file is saved under `/tmp/logs/install_bench.lo
|
|||||||
- Create an Issue in this repository with the log file attached.
|
- Create an Issue in this repository with the log file attached.
|
||||||
- Search for an existing issue or post the log file on the [Frappe/ERPNext Discuss Forum](https://discuss.erpnext.com/c/bench) with the tag `installation_problem` under "Install/Update" category.
|
- Search for an existing issue or post the log file on the [Frappe/ERPNext Discuss Forum](https://discuss.erpnext.com/c/bench) with the tag `installation_problem` under "Install/Update" category.
|
||||||
|
|
||||||
For more information and advanced setup instructions, check out the [Easy Install Documentation](https://github.com/frappe/bench/blob/master/docs/easy_install.md).
|
For more information and advanced setup instructions, check out the [Easy Install Documentation](https://github.com/frappe/bench/blob/develop/docs/easy_install.md).
|
||||||
|
|
||||||
|
|
||||||
### Manual Installation
|
### Manual Installation
|
||||||
|
|
||||||
Although not recommended, some might want to manually setup a bench instance locally for development. To quickly get started on installing bench the hard way, you can follow [Installing Bench and Frappe](https://frappe.io/docs/user/en/installation).
|
Some might want to manually setup a bench instance locally for development. To quickly get started on installing bench the hard way, you can follow the guide on [Installing Bench and the Frappe Framework](https://frappe.io/docs/user/en/installation).
|
||||||
|
|
||||||
|
You'll have to set up the system dependencies required for setting up a Frappe Environment. Checkout [docs/installation](https://github.com/frappe/bench/blob/develop/docs/installation.md) for more information on this. If you've already set up, install bench via pip:
|
||||||
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ pip install frappe-bench
|
||||||
|
```
|
||||||
|
|
||||||
For more extensive distribution-dependent documentation, check out the following guides:
|
For more extensive distribution-dependent documentation, check out the following guides:
|
||||||
|
|
||||||
@ -200,12 +207,12 @@ For more extensive distribution-dependent documentation, check out the following
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
For more in-depth information on commands and their usage, follow [Commands and Usage](https://github.com/frappe/bench/blob/master/docs/commands_and_usage.md). As for a consolidated list of bench commands, check out [Bench Usage](https://github.com/frappe/bench/blob/master/docs/bench_usage.md).
|
For more in-depth information on commands and their usage, follow [Commands and Usage](https://github.com/frappe/bench/blob/develop/docs/commands_and_usage.md). As for a consolidated list of bench commands, check out [Bench Usage](https://github.com/frappe/bench/blob/develop/docs/bench_usage.md).
|
||||||
|
|
||||||
|
|
||||||
## Custom Bench Commands
|
## Custom Bench Commands
|
||||||
|
|
||||||
If you wish to extend the capabilities of bench with your own custom Frappe Application, you may follow [Adding Custom Bench Commands](https://github.com/frappe/bench/blob/master/docs/bench_custom_cmd.md).
|
If you wish to extend the capabilities of bench with your own custom Frappe Application, you may follow [Adding Custom Bench Commands](https://github.com/frappe/bench/blob/develop/docs/bench_custom_cmd.md).
|
||||||
|
|
||||||
|
|
||||||
## Bench Manager
|
## Bench Manager
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
VERSION = "5.0.0"
|
VERSION = "5.1.0"
|
||||||
PROJECT_NAME = "frappe-bench"
|
PROJECT_NAME = "frappe-bench"
|
||||||
FRAPPE_VERSION = None
|
FRAPPE_VERSION = None
|
||||||
|
|
||||||
|
47
bench/app.py
47
bench/app.py
@ -20,11 +20,10 @@ from six.moves import reload_module
|
|||||||
# imports - module imports
|
# imports - module imports
|
||||||
import bench
|
import bench
|
||||||
from bench.config.common_site_config import get_config
|
from bench.config.common_site_config import get_config
|
||||||
from bench.utils import CommandFailedError, build_assets, check_git_for_shallow_clone, exec_cmd, get_cmd_output, get_frappe, restart_supervisor_processes, restart_systemd_processes, run_frappe_cmd
|
from bench.utils import color, CommandFailedError, build_assets, check_git_for_shallow_clone, exec_cmd, get_cmd_output, get_frappe, restart_supervisor_processes, restart_systemd_processes, run_frappe_cmd
|
||||||
|
|
||||||
|
|
||||||
logging.basicConfig(level="INFO")
|
logger = logging.getLogger(bench.PROJECT_NAME)
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidBranchException(Exception): pass
|
class InvalidBranchException(Exception): pass
|
||||||
@ -59,17 +58,10 @@ def write_appstxt(apps, bench_path='.'):
|
|||||||
with open(os.path.join(bench_path, 'sites', 'apps.txt'), 'w') as f:
|
with open(os.path.join(bench_path, 'sites', 'apps.txt'), 'w') as f:
|
||||||
return f.write('\n'.join(apps))
|
return f.write('\n'.join(apps))
|
||||||
|
|
||||||
def check_url(url, raise_err=True):
|
def is_git_url(url):
|
||||||
from six.moves.urllib.parse import urlparse
|
# modified to allow without the tailing .git from https://github.com/jonschlinkert/is-git-url.git
|
||||||
|
pattern = r"(?:git|ssh|https?|git@[-\w.]+):(\/\/)?(.*?)(\.git)?(\/?|\#[-\d\w._]+?)$"
|
||||||
parsed = urlparse(url)
|
return bool(re.match(pattern, url))
|
||||||
if not parsed.scheme:
|
|
||||||
if raise_err:
|
|
||||||
raise TypeError('{url} Not a valid URL'.format(url=url))
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_excluded_apps(bench_path='.'):
|
def get_excluded_apps(bench_path='.'):
|
||||||
try:
|
try:
|
||||||
@ -98,9 +90,9 @@ def remove_from_excluded_apps_txt(app, bench_path='.'):
|
|||||||
apps.remove(app)
|
apps.remove(app)
|
||||||
return write_excluded_apps_txt(apps, bench_path=bench_path)
|
return write_excluded_apps_txt(apps, bench_path=bench_path)
|
||||||
|
|
||||||
def get_app(git_url, branch=None, bench_path='.', skip_assets=False, verbose=False, postprocess=True, overwrite=False):
|
def get_app(git_url, branch=None, bench_path='.', skip_assets=False, verbose=False, restart_bench=True, overwrite=False):
|
||||||
if not os.path.exists(git_url):
|
if not os.path.exists(git_url):
|
||||||
if not check_url(git_url, raise_err=False):
|
if not is_git_url(git_url):
|
||||||
orgs = ['frappe', 'erpnext']
|
orgs = ['frappe', 'erpnext']
|
||||||
for org in orgs:
|
for org in orgs:
|
||||||
url = 'https://api.github.com/repos/{org}/{app}'.format(org=org, app=git_url)
|
url = 'https://api.github.com/repos/{org}/{app}'.format(org=org, app=git_url)
|
||||||
@ -135,7 +127,8 @@ Do you want to continue and overwrite it?'''.format(repo_name)):
|
|||||||
install_app(app=app_name, bench_path=bench_path, verbose=verbose, skip_assets=skip_assets)
|
install_app(app=app_name, bench_path=bench_path, verbose=verbose, skip_assets=skip_assets)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
logger.info('Getting app {0}'.format(repo_name))
|
print('\n{0}Getting {1}{2}'.format(color.yellow, repo_name, color.nc))
|
||||||
|
logger.log('Getting app {0}'.format(repo_name))
|
||||||
exec_cmd("git clone {git_url} {branch} {shallow_clone} --origin upstream".format(
|
exec_cmd("git clone {git_url} {branch} {shallow_clone} --origin upstream".format(
|
||||||
git_url=git_url,
|
git_url=git_url,
|
||||||
shallow_clone=shallow_clone,
|
shallow_clone=shallow_clone,
|
||||||
@ -160,7 +153,7 @@ def get_app_name(bench_path, repo_name):
|
|||||||
def new_app(app, bench_path='.'):
|
def new_app(app, bench_path='.'):
|
||||||
# For backwards compatibility
|
# For backwards compatibility
|
||||||
app = app.lower().replace(" ", "_").replace("-", "_")
|
app = app.lower().replace(" ", "_").replace("-", "_")
|
||||||
logger.info('creating new app {}'.format(app))
|
logger.log('creating new app {}'.format(app))
|
||||||
apps = os.path.abspath(os.path.join(bench_path, 'apps'))
|
apps = os.path.abspath(os.path.join(bench_path, 'apps'))
|
||||||
bench.set_frappe_version(bench_path=bench_path)
|
bench.set_frappe_version(bench_path=bench_path)
|
||||||
|
|
||||||
@ -172,8 +165,9 @@ def new_app(app, bench_path='.'):
|
|||||||
install_app(app, bench_path=bench_path)
|
install_app(app, bench_path=bench_path)
|
||||||
|
|
||||||
|
|
||||||
def install_app(app, bench_path=".", verbose=False, no_cache=False, postprocess=True, skip_assets=False):
|
def install_app(app, bench_path=".", verbose=False, no_cache=False, restart_bench=True, skip_assets=False):
|
||||||
logger.info("installing {}".format(app))
|
print('\n{0}Installing {1}{2}'.format(color.yellow, app, color.nc))
|
||||||
|
logger.log("installing {}".format(app))
|
||||||
|
|
||||||
pip_path = os.path.join(bench_path, "env", "bin", "pip")
|
pip_path = os.path.join(bench_path, "env", "bin", "pip")
|
||||||
quiet_flag = "-q" if not verbose else ""
|
quiet_flag = "-q" if not verbose else ""
|
||||||
@ -187,9 +181,10 @@ def install_app(app, bench_path=".", verbose=False, no_cache=False, postprocess=
|
|||||||
|
|
||||||
add_to_appstxt(app, bench_path=bench_path)
|
add_to_appstxt(app, bench_path=bench_path)
|
||||||
|
|
||||||
if postprocess:
|
|
||||||
if not skip_assets:
|
if not skip_assets:
|
||||||
build_assets(bench_path=bench_path, app=app)
|
build_assets(bench_path=bench_path, app=app)
|
||||||
|
|
||||||
|
if restart_bench:
|
||||||
conf = get_config(bench_path=bench_path)
|
conf = get_config(bench_path=bench_path)
|
||||||
|
|
||||||
if conf.get('restart_supervisor_on_update'):
|
if conf.get('restart_supervisor_on_update'):
|
||||||
@ -267,7 +262,7 @@ Here are your choices:
|
|||||||
add_to_excluded_apps_txt(app, bench_path=bench_path)
|
add_to_excluded_apps_txt(app, bench_path=bench_path)
|
||||||
print("Skipping pull for app {}, since remote doesn't exist, and adding it to excluded apps".format(app))
|
print("Skipping pull for app {}, since remote doesn't exist, and adding it to excluded apps".format(app))
|
||||||
continue
|
continue
|
||||||
logger.info('pulling {0}'.format(app))
|
logger.log('pulling {0}'.format(app))
|
||||||
if reset:
|
if reset:
|
||||||
exec_cmd("git fetch --all", cwd=app_dir)
|
exec_cmd("git fetch --all", cwd=app_dir)
|
||||||
exec_cmd("git reset --hard {remote}/{branch}".format(
|
exec_cmd("git reset --hard {remote}/{branch}".format(
|
||||||
@ -359,10 +354,6 @@ def get_upstream_version(app, branch=None, bench_path='.'):
|
|||||||
raise
|
raise
|
||||||
return get_version_from_string(contents)
|
return get_version_from_string(contents)
|
||||||
|
|
||||||
def get_upstream_url(app, bench_path='.'):
|
|
||||||
repo_dir = get_repo_dir(app, bench_path=bench_path)
|
|
||||||
return subprocess.check_output(['git', 'config', '--get', 'remote.upstream.url'], cwd=repo_dir).strip()
|
|
||||||
|
|
||||||
def get_repo_dir(app, bench_path='.'):
|
def get_repo_dir(app, bench_path='.'):
|
||||||
return os.path.join(bench_path, 'apps', app)
|
return os.path.join(bench_path, 'apps', app)
|
||||||
|
|
||||||
@ -390,7 +381,7 @@ def switch_branch(branch, apps=None, bench_path='.', upgrade=False, check_upgrad
|
|||||||
bench.utils.log("Fetching upstream {0}for {1}".format("unshallow " if unshallow_flag else "", app))
|
bench.utils.log("Fetching upstream {0}for {1}".format("unshallow " if unshallow_flag else "", app))
|
||||||
|
|
||||||
bench.utils.exec_cmd("git remote set-branches upstream '*'", cwd=app_dir)
|
bench.utils.exec_cmd("git remote set-branches upstream '*'", cwd=app_dir)
|
||||||
bench.utils.exec_cmd("git fetch --all{0}".format(" --unshallow" if unshallow_flag else ""), cwd=app_dir)
|
bench.utils.exec_cmd("git fetch --all{0} --quiet".format(" --unshallow" if unshallow_flag else ""), cwd=app_dir)
|
||||||
|
|
||||||
if check_upgrade:
|
if check_upgrade:
|
||||||
version_upgrade = is_version_upgrade(app=app, bench_path=bench_path, branch=branch)
|
version_upgrade = is_version_upgrade(app=app, bench_path=bench_path, branch=branch)
|
||||||
@ -399,7 +390,7 @@ def switch_branch(branch, apps=None, bench_path='.', upgrade=False, check_upgrad
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print("Switching for "+app)
|
print("Switching for "+app)
|
||||||
bench.utils.exec_cmd("git checkout {0}".format(branch), cwd=app_dir)
|
bench.utils.exec_cmd("git checkout -f {0}".format(branch), cwd=app_dir)
|
||||||
|
|
||||||
if str(repo.active_branch) == branch:
|
if str(repo.active_branch) == branch:
|
||||||
switched_apps.append(app)
|
switched_apps.append(app)
|
||||||
|
13
bench/cli.py
13
bench/cli.py
@ -14,9 +14,8 @@ import bench
|
|||||||
from bench.app import get_apps
|
from bench.app import get_apps
|
||||||
from bench.commands import bench_command
|
from bench.commands import bench_command
|
||||||
from bench.config.common_site_config import get_config
|
from bench.config.common_site_config import get_config
|
||||||
from bench.utils import PatchError, bench_cache_file, check_latest_version, drop_privileges, find_parent_bench, generate_command_cache, get_cmd_output, get_env_cmd, get_frappe, is_bench_directory, is_dist_editable, is_root, log
|
from bench.utils import PatchError, bench_cache_file, check_latest_version, drop_privileges, find_parent_bench, generate_command_cache, get_cmd_output, get_env_cmd, get_frappe, is_bench_directory, is_dist_editable, is_root, log, setup_logging
|
||||||
|
|
||||||
logger = logging.getLogger(bench.PROJECT_NAME)
|
|
||||||
from_command_line = False
|
from_command_line = False
|
||||||
change_uid_msg = "You should not run this command as root"
|
change_uid_msg = "You should not run this command as root"
|
||||||
|
|
||||||
@ -24,8 +23,11 @@ change_uid_msg = "You should not run this command as root"
|
|||||||
def cli():
|
def cli():
|
||||||
global from_command_line
|
global from_command_line
|
||||||
from_command_line = True
|
from_command_line = True
|
||||||
|
command = " ".join(sys.argv)
|
||||||
|
|
||||||
change_working_directory()
|
change_working_directory()
|
||||||
|
logger = setup_logging() or logging.getLogger(bench.PROJECT_NAME)
|
||||||
|
logger.info(command)
|
||||||
check_uid()
|
check_uid()
|
||||||
change_dir()
|
change_dir()
|
||||||
change_uid()
|
change_uid()
|
||||||
@ -56,8 +58,11 @@ def cli():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
bench_command()
|
bench_command()
|
||||||
except PatchError:
|
except BaseException as e:
|
||||||
sys.exit(1)
|
return_code = getattr(e, "code", 0)
|
||||||
|
if return_code:
|
||||||
|
logger.warning("{0} executed with exit code {1}".format(command, return_code))
|
||||||
|
sys.exit(return_code)
|
||||||
|
|
||||||
|
|
||||||
def check_uid():
|
def check_uid():
|
||||||
|
@ -14,10 +14,7 @@ def print_bench_version(ctx, param, value):
|
|||||||
@click.option('--version', is_flag=True, is_eager=True, callback=print_bench_version, expose_value=False)
|
@click.option('--version', is_flag=True, is_eager=True, callback=print_bench_version, expose_value=False)
|
||||||
def bench_command(bench_path='.'):
|
def bench_command(bench_path='.'):
|
||||||
import bench
|
import bench
|
||||||
from bench.utils import setup_logging
|
|
||||||
|
|
||||||
bench.set_frappe_version(bench_path=bench_path)
|
bench.set_frappe_version(bench_path=bench_path)
|
||||||
setup_logging(bench_path=bench_path)
|
|
||||||
|
|
||||||
|
|
||||||
from bench.commands.make import init, get_app, new_app, remove_app, exclude_app_for_update, include_app_for_update, pip
|
from bench.commands.make import init, get_app, new_app, remove_app, exclude_app_for_update, include_app_for_update, pip
|
||||||
|
@ -80,6 +80,7 @@ def remove_common_config(keys):
|
|||||||
config.add_command(config_restart_supervisor_on_update)
|
config.add_command(config_restart_supervisor_on_update)
|
||||||
config.add_command(config_restart_systemd_on_update)
|
config.add_command(config_restart_systemd_on_update)
|
||||||
config.add_command(config_dns_multitenant)
|
config.add_command(config_dns_multitenant)
|
||||||
|
config.add_command(config_rebase_on_pull)
|
||||||
config.add_command(config_serve_default_site)
|
config.add_command(config_serve_default_site)
|
||||||
config.add_command(config_http_timeout)
|
config.add_command(config_http_timeout)
|
||||||
config.add_command(set_common_config)
|
config.add_command(set_common_config)
|
||||||
|
@ -4,7 +4,6 @@ import sys
|
|||||||
|
|
||||||
# imports - third party imports
|
# imports - third party imports
|
||||||
import click
|
import click
|
||||||
from six import PY3
|
|
||||||
|
|
||||||
# imports - module imports
|
# imports - module imports
|
||||||
import bench.config.lets_encrypt
|
import bench.config.lets_encrypt
|
||||||
@ -128,13 +127,17 @@ def setup_socketio():
|
|||||||
@click.option("--node", help="Update only Node packages", default=False, is_flag=True)
|
@click.option("--node", help="Update only Node packages", default=False, is_flag=True)
|
||||||
@click.option("--python", help="Update only Python packages", default=False, is_flag=True)
|
@click.option("--python", help="Update only Python packages", default=False, is_flag=True)
|
||||||
def setup_requirements(node=False, python=False):
|
def setup_requirements(node=False, python=False):
|
||||||
if not node:
|
if not (node or python):
|
||||||
from bench.utils import update_requirements as setup_python_packages
|
from bench.utils import update_requirements
|
||||||
setup_python_packages()
|
update_requirements()
|
||||||
|
|
||||||
if not python:
|
elif not node:
|
||||||
from bench.utils import update_node_packages as setup_node_packages
|
from bench.utils import update_python_packages
|
||||||
setup_node_packages()
|
update_python_packages()
|
||||||
|
|
||||||
|
elif not python:
|
||||||
|
from bench.utils import update_node_packages
|
||||||
|
update_node_packages()
|
||||||
|
|
||||||
|
|
||||||
@click.command("manager", help="Setup bench-manager.local site with the bench_manager app installed on it")
|
@click.command("manager", help="Setup bench-manager.local site with the bench_manager app installed on it")
|
||||||
|
@ -172,9 +172,7 @@ def prepare_sites(config, bench_path):
|
|||||||
|
|
||||||
if not dns_multitenant:
|
if not dns_multitenant:
|
||||||
message = "Port configuration list:"
|
message = "Port configuration list:"
|
||||||
port_config_index = 0
|
|
||||||
for site in sites_configs:
|
for site in sites_configs:
|
||||||
port_config_index += 1
|
|
||||||
message += "\n\nSite {0} assigned port: {1}".format(site["name"], site["port"])
|
message += "\n\nSite {0} assigned port: {1}".format(site["name"], site["port"])
|
||||||
|
|
||||||
print(message)
|
print(message)
|
||||||
|
@ -11,9 +11,9 @@ watch: bench watch
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if use_rq -%}
|
{% if use_rq -%}
|
||||||
schedule: bench schedule
|
schedule: bench schedule
|
||||||
worker_short: bench worker --queue short --quiet
|
worker_short: bench worker --queue short 1>> logs/worker.log 2>> logs/worker.error.log
|
||||||
worker_long: bench worker --queue long --quiet
|
worker_long: bench worker --queue long 1>> logs/worker.log 2>> logs/worker.error.log
|
||||||
worker_default: bench worker --queue default --quiet
|
worker_default: bench worker --queue default 1>> logs/worker.log 2>> logs/worker.error.log
|
||||||
{% else %}
|
{% else %}
|
||||||
workerbeat: sh -c 'cd sites && exec ../env/bin/python -m frappe.celery_app beat -s scheduler.schedule'
|
workerbeat: sh -c 'cd sites && exec ../env/bin/python -m frappe.celery_app beat -s scheduler.schedule'
|
||||||
worker: sh -c 'cd sites && exec ../env/bin/python -m frappe.celery_app worker -n jobs@%h -Ofair --soft-time-limit 360 --time-limit 390'
|
worker: sh -c 'cd sites && exec ../env/bin/python -m frappe.celery_app worker -n jobs@%h -Ofair --soft-time-limit 360 --time-limit 390'
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
become: yes
|
become: yes
|
||||||
become_user: root
|
become_user: root
|
||||||
yum:
|
yum:
|
||||||
name: https://centos7.iuscommunity.org/ius-release.rpm
|
name: https://repo.ius.io/ius-release-el7.rpm
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
- name: "Setup prerequisites using yum"
|
- name: "Setup prerequisites using yum"
|
||||||
|
@ -136,12 +136,14 @@ class TestBenchInit(TestBenchBase):
|
|||||||
bench_path = os.path.join(self.benches_path, "test-bench")
|
bench_path = os.path.join(self.benches_path, "test-bench")
|
||||||
app_path = os.path.join(bench_path, "apps", "frappe")
|
app_path = os.path.join(bench_path, "apps", "frappe")
|
||||||
|
|
||||||
bench.utils.exec_cmd("bench switch-to-branch version-12 frappe --upgrade", cwd=bench_path)
|
successful_switch = not bench.utils.exec_cmd("bench switch-to-branch version-12 frappe --upgrade", cwd=bench_path)
|
||||||
app_branch_after_switch = str(git.Repo(path=app_path).active_branch)
|
app_branch_after_switch = str(git.Repo(path=app_path).active_branch)
|
||||||
|
if successful_switch:
|
||||||
self.assertEqual("version-12", app_branch_after_switch)
|
self.assertEqual("version-12", app_branch_after_switch)
|
||||||
|
|
||||||
bench.utils.exec_cmd("bench switch-to-branch develop frappe --upgrade", cwd=bench_path)
|
successful_switch = not bench.utils.exec_cmd("bench switch-to-branch develop frappe --upgrade", cwd=bench_path)
|
||||||
app_branch_after_second_switch = str(git.Repo(path=app_path).active_branch)
|
app_branch_after_second_switch = str(git.Repo(path=app_path).active_branch)
|
||||||
|
if successful_switch:
|
||||||
self.assertEqual("develop", app_branch_after_second_switch)
|
self.assertEqual("develop", app_branch_after_second_switch)
|
||||||
|
|
||||||
|
|
||||||
|
190
bench/utils.py
190
bench/utils.py
@ -10,7 +10,6 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import platform
|
|
||||||
import pwd
|
import pwd
|
||||||
import re
|
import re
|
||||||
import select
|
import select
|
||||||
@ -39,7 +38,7 @@ class PatchError(Exception):
|
|||||||
class CommandFailedError(Exception):
|
class CommandFailedError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(bench.PROJECT_NAME)
|
||||||
bench_cache_file = '.bench.cmd'
|
bench_cache_file = '.bench.cmd'
|
||||||
folders_in_bench = ('apps', 'sites', 'config', 'logs', 'config/pids')
|
folders_in_bench = ('apps', 'sites', 'config', 'logs', 'config/pids')
|
||||||
sudoers_file = '/etc/sudoers.d/frappe'
|
sudoers_file = '/etc/sudoers.d/frappe'
|
||||||
@ -139,7 +138,7 @@ def init(path, apps_path=None, no_procfile=False, no_backups=False,
|
|||||||
if e.errno == errno.EEXIST:
|
if e.errno == errno.EEXIST:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
setup_logging()
|
setup_logging(bench_path=path)
|
||||||
|
|
||||||
setup_env(bench_path=path, python=python)
|
setup_env(bench_path=path, python=python)
|
||||||
|
|
||||||
@ -285,7 +284,7 @@ def clone_apps_from(bench_path, clone_from, update_app=True):
|
|||||||
subprocess.check_output(['git', 'reset', '--hard'], cwd=app_path)
|
subprocess.check_output(['git', 'reset', '--hard'], cwd=app_path)
|
||||||
subprocess.check_output(['git', 'pull', '--rebase', remote, branch], cwd=app_path)
|
subprocess.check_output(['git', 'pull', '--rebase', remote, branch], cwd=app_path)
|
||||||
|
|
||||||
install_app(app, bench_path)
|
install_app(app, bench_path, restart_bench=False)
|
||||||
|
|
||||||
with open(os.path.join(clone_from, 'sites', 'apps.txt'), 'r') as f:
|
with open(os.path.join(clone_from, 'sites', 'apps.txt'), 'r') as f:
|
||||||
apps = f.read().splitlines()
|
apps = f.read().splitlines()
|
||||||
@ -297,8 +296,13 @@ def clone_apps_from(bench_path, clone_from, update_app=True):
|
|||||||
def exec_cmd(cmd, cwd='.'):
|
def exec_cmd(cmd, cwd='.'):
|
||||||
import shlex
|
import shlex
|
||||||
print("{0}$ {1}{2}".format(color.silver, cmd, color.nc))
|
print("{0}$ {1}{2}".format(color.silver, cmd, color.nc))
|
||||||
|
cwd_info = "cd {0} && ".format(cwd) if cwd != "." else ""
|
||||||
|
cmd_log = "{0}{1}".format(cwd_info, cmd)
|
||||||
|
logger.debug(cmd_log)
|
||||||
cmd = shlex.split(cmd)
|
cmd = shlex.split(cmd)
|
||||||
return subprocess.call(cmd, cwd=cwd, universal_newlines=True)
|
return_code = subprocess.call(cmd, cwd=cwd, universal_newlines=True)
|
||||||
|
if return_code:
|
||||||
|
logger.warning("{0} executed with exit code {1}".format(cmd_log, return_code))
|
||||||
|
|
||||||
|
|
||||||
def which(executable, raise_err = False):
|
def which(executable, raise_err = False):
|
||||||
@ -372,7 +376,7 @@ def get_sites(bench_path='.'):
|
|||||||
|
|
||||||
def setup_backups(bench_path='.'):
|
def setup_backups(bench_path='.'):
|
||||||
from bench.config.common_site_config import get_config
|
from bench.config.common_site_config import get_config
|
||||||
logger.info('setting up backups')
|
logger.log('setting up backups')
|
||||||
|
|
||||||
bench_dir = os.path.abspath(bench_path)
|
bench_dir = os.path.abspath(bench_path)
|
||||||
user = get_config(bench_path=bench_dir).get('frappe_user')
|
user = get_config(bench_path=bench_dir).get('frappe_user')
|
||||||
@ -424,6 +428,13 @@ def setup_sudoers(user):
|
|||||||
|
|
||||||
|
|
||||||
def setup_logging(bench_path='.'):
|
def setup_logging(bench_path='.'):
|
||||||
|
LOG_LEVEL = 15
|
||||||
|
logging.addLevelName(LOG_LEVEL, "LOG")
|
||||||
|
def logv(self, message, *args, **kws):
|
||||||
|
if self.isEnabledFor(LOG_LEVEL):
|
||||||
|
self._log(LOG_LEVEL, message, args, **kws)
|
||||||
|
logging.Logger.log = logv
|
||||||
|
|
||||||
if os.path.exists(os.path.join(bench_path, 'logs')):
|
if os.path.exists(os.path.join(bench_path, 'logs')):
|
||||||
logger = logging.getLogger(bench.PROJECT_NAME)
|
logger = logging.getLogger(bench.PROJECT_NAME)
|
||||||
log_file = os.path.join(bench_path, 'logs', 'bench.log')
|
log_file = os.path.join(bench_path, 'logs', 'bench.log')
|
||||||
@ -433,6 +444,8 @@ def setup_logging(bench_path='.'):
|
|||||||
logger.addHandler(hdlr)
|
logger.addHandler(hdlr)
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
return logger
|
||||||
|
|
||||||
|
|
||||||
def get_process_manager():
|
def get_process_manager():
|
||||||
for proc_man in ['honcho', 'foreman', 'forego']:
|
for proc_man in ['honcho', 'foreman', 'forego']:
|
||||||
@ -459,14 +472,6 @@ def start(no_dev=False, concurrency=None, procfile=None):
|
|||||||
os.execv(program, command)
|
os.execv(program, command)
|
||||||
|
|
||||||
|
|
||||||
def check_cmd(cmd, cwd='.'):
|
|
||||||
try:
|
|
||||||
subprocess.check_call(cmd, cwd=cwd, shell=True)
|
|
||||||
return True
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def get_git_version():
|
def get_git_version():
|
||||||
'''returns git version from `git --version`
|
'''returns git version from `git --version`
|
||||||
extracts version number from string `get version 1.9.1` etc'''
|
extracts version number from string `get version 1.9.1` etc'''
|
||||||
@ -505,15 +510,6 @@ def get_cmd_output(cmd, cwd='.', _raise=True):
|
|||||||
return safe_decode(output)
|
return safe_decode(output)
|
||||||
|
|
||||||
|
|
||||||
def safe_encode(what, encoding = 'utf-8'):
|
|
||||||
try:
|
|
||||||
what = what.encode(encoding)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return what
|
|
||||||
|
|
||||||
|
|
||||||
def restart_supervisor_processes(bench_path='.', web_workers=False):
|
def restart_supervisor_processes(bench_path='.', web_workers=False):
|
||||||
from .config.common_site_config import get_config
|
from .config.common_site_config import get_config
|
||||||
conf = get_config(bench_path=bench_path)
|
conf = get_config(bench_path=bench_path)
|
||||||
@ -564,13 +560,24 @@ def update_env_pip(bench_path):
|
|||||||
|
|
||||||
def update_requirements(bench_path='.'):
|
def update_requirements(bench_path='.'):
|
||||||
from bench.app import get_apps, install_app
|
from bench.app import get_apps, install_app
|
||||||
print('Updating Python libraries...')
|
print('Installing applications...')
|
||||||
|
|
||||||
# update env pip
|
|
||||||
update_env_pip(bench_path)
|
update_env_pip(bench_path)
|
||||||
|
|
||||||
for app in get_apps():
|
for app in get_apps():
|
||||||
install_app(app, bench_path=bench_path, skip_assets=True)
|
install_app(app, bench_path=bench_path, skip_assets=True, restart_bench=False)
|
||||||
|
|
||||||
|
|
||||||
|
def update_python_packages(bench_path='.'):
|
||||||
|
from bench.app import get_apps
|
||||||
|
pip_path = os.path.join(bench_path, "env", "bin", "pip")
|
||||||
|
print('Updating Python libraries...')
|
||||||
|
|
||||||
|
update_env_pip(bench_path)
|
||||||
|
for app in get_apps():
|
||||||
|
print('\n{0}Installing python dependencies for {1}{2}'.format(color.yellow, app, color.nc))
|
||||||
|
app_path = os.path.join(bench_path, "apps", app)
|
||||||
|
exec_cmd("{0} install -q -U -e {1}".format(pip_path, app_path), cwd=bench_path)
|
||||||
|
|
||||||
|
|
||||||
def update_node_packages(bench_path='.'):
|
def update_node_packages(bench_path='.'):
|
||||||
@ -634,21 +641,6 @@ def update_npm_packages(bench_path='.'):
|
|||||||
exec_cmd('npm install', cwd=bench_path)
|
exec_cmd('npm install', cwd=bench_path)
|
||||||
|
|
||||||
|
|
||||||
def install_requirements(req_file, user=False):
|
|
||||||
if os.path.exists(req_file):
|
|
||||||
if user:
|
|
||||||
python = sys.executable
|
|
||||||
else:
|
|
||||||
python = os.path.join("env", "bin", "python")
|
|
||||||
|
|
||||||
if in_virtual_env():
|
|
||||||
user = False
|
|
||||||
|
|
||||||
user_flag = "--user" if user else ""
|
|
||||||
|
|
||||||
exec_cmd("{python} -m pip install {user_flag} -q -U -r {req_file}".format(python=python, user_flag=user_flag, req_file=req_file))
|
|
||||||
|
|
||||||
|
|
||||||
def backup_site(site, bench_path='.'):
|
def backup_site(site, bench_path='.'):
|
||||||
bench.set_frappe_version(bench_path=bench_path)
|
bench.set_frappe_version(bench_path=bench_path)
|
||||||
|
|
||||||
@ -742,19 +734,6 @@ def fix_prod_setup_perms(bench_path='.', frappe_user=None):
|
|||||||
os.chown(path, uid, gid)
|
os.chown(path, uid, gid)
|
||||||
|
|
||||||
|
|
||||||
def fix_file_perms():
|
|
||||||
for dir_path, dirs, files in os.walk('.'):
|
|
||||||
for _dir in dirs:
|
|
||||||
os.chmod(os.path.join(dir_path, _dir), 0o755)
|
|
||||||
for _file in files:
|
|
||||||
os.chmod(os.path.join(dir_path, _file), 0o644)
|
|
||||||
bin_dir = './env/bin'
|
|
||||||
if os.path.exists(bin_dir):
|
|
||||||
for _file in os.listdir(bin_dir):
|
|
||||||
if not _file.startswith('activate'):
|
|
||||||
os.chmod(os.path.join(bin_dir, _file), 0o755)
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_frappe_version(bench_path='.'):
|
def get_current_frappe_version(bench_path='.'):
|
||||||
from .app import get_current_frappe_version as fv
|
from .app import get_current_frappe_version as fv
|
||||||
return fv(bench_path=bench_path)
|
return fv(bench_path=bench_path)
|
||||||
@ -785,13 +764,6 @@ def run_frappe_cmd(*args, **kwargs):
|
|||||||
sys.exit(return_code)
|
sys.exit(return_code)
|
||||||
|
|
||||||
|
|
||||||
def get_frappe_cmd_output(*args, **kwargs):
|
|
||||||
bench_path = kwargs.get('bench_path', '.')
|
|
||||||
f = get_env_cmd('python', bench_path=bench_path)
|
|
||||||
sites_dir = os.path.join(bench_path, 'sites')
|
|
||||||
return subprocess.check_output((f, '-m', 'frappe.utils.bench_helper', 'frappe') + args, cwd=sites_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_upgrade(from_ver, to_ver, bench_path='.'):
|
def validate_upgrade(from_ver, to_ver, bench_path='.'):
|
||||||
if to_ver >= 6:
|
if to_ver >= 6:
|
||||||
if not find_executable('npm') and not (find_executable('node') or find_executable('nodejs')):
|
if not find_executable('npm') and not (find_executable('node') or find_executable('nodejs')):
|
||||||
@ -901,13 +873,6 @@ def log_line(data, stream):
|
|||||||
return sys.stdout.write(data)
|
return sys.stdout.write(data)
|
||||||
|
|
||||||
|
|
||||||
def get_output(*cmd):
|
|
||||||
s = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
||||||
out = s.stdout.read()
|
|
||||||
s.stdout.close()
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
def get_bench_name(bench_path):
|
def get_bench_name(bench_path):
|
||||||
return os.path.basename(os.path.abspath(bench_path))
|
return os.path.basename(os.path.abspath(bench_path))
|
||||||
|
|
||||||
@ -984,78 +949,10 @@ def find_benches(directory=None):
|
|||||||
return benches
|
return benches
|
||||||
|
|
||||||
|
|
||||||
def in_virtual_env():
|
|
||||||
# type: () -> bool
|
|
||||||
"""Returns a boolean, whether running in venv with no system site-packages.
|
|
||||||
pip really does the best job at this: virtualenv_no_global at https://raw.githubusercontent.com/pypa/pip/master/src/pip/_internal/utils/virtualenv.py
|
|
||||||
"""
|
|
||||||
|
|
||||||
def running_under_venv():
|
|
||||||
# handles PEP 405 compliant virtual environments.
|
|
||||||
return sys.prefix != getattr(sys, "base_prefix", sys.prefix)
|
|
||||||
|
|
||||||
def running_under_regular_virtualenv():
|
|
||||||
# pypa/virtualenv case
|
|
||||||
return hasattr(sys, 'real_prefix')
|
|
||||||
|
|
||||||
def _no_global_under_venv():
|
|
||||||
# type: () -> bool
|
|
||||||
"""Check `{sys.prefix}/pyvenv.cfg` for system site-packages inclusion
|
|
||||||
PEP 405 specifies that when system site-packages are not supposed to be
|
|
||||||
visible from a virtual environment, `pyvenv.cfg` must contain the following
|
|
||||||
line:
|
|
||||||
include-system-site-packages = false
|
|
||||||
Additionally, log a warning if accessing the file fails.
|
|
||||||
"""
|
|
||||||
def _get_pyvenv_cfg_lines():
|
|
||||||
pyvenv_cfg_file = os.path.join(sys.prefix, 'pyvenv.cfg')
|
|
||||||
try:
|
|
||||||
with open(pyvenv_cfg_file) as f:
|
|
||||||
return f.read().splitlines() # avoids trailing newlines
|
|
||||||
except IOError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
_INCLUDE_SYSTEM_SITE_PACKAGES_REGEX = re.compile(
|
|
||||||
r"include-system-site-packages\s*=\s*(?P<value>true|false)"
|
|
||||||
)
|
|
||||||
cfg_lines = _get_pyvenv_cfg_lines()
|
|
||||||
if cfg_lines is None:
|
|
||||||
# We're not in a "sane" venv, so assume there is no system
|
|
||||||
# site-packages access (since that's PEP 405's default state).
|
|
||||||
return True
|
|
||||||
|
|
||||||
for line in cfg_lines:
|
|
||||||
match = _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX.match(line)
|
|
||||||
if match is not None and match.group('value') == 'false':
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _no_global_under_regular_virtualenv():
|
|
||||||
# type: () -> bool
|
|
||||||
"""Check if "no-global-site-packages.txt" exists beside site.py
|
|
||||||
This mirrors logic in pypa/virtualenv for determining whether system
|
|
||||||
site-packages are visible in the virtual environment.
|
|
||||||
"""
|
|
||||||
site_mod_dir = os.path.dirname(os.path.abspath(site.__file__))
|
|
||||||
no_global_site_packages_file = os.path.join(site_mod_dir, 'no-global-site-packages.txt')
|
|
||||||
return os.path.exists(no_global_site_packages_file)
|
|
||||||
|
|
||||||
if running_under_regular_virtualenv():
|
|
||||||
return _no_global_under_regular_virtualenv()
|
|
||||||
|
|
||||||
if running_under_venv():
|
|
||||||
return _no_global_under_venv()
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def migrate_env(python, backup=False):
|
def migrate_env(python, backup=False):
|
||||||
from bench.config.common_site_config import get_config
|
from bench.config.common_site_config import get_config
|
||||||
from bench.app import get_apps
|
from bench.app import get_apps
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
logger.setLevel(logging.DEBUG)
|
|
||||||
|
|
||||||
nvenv = 'env'
|
nvenv = 'env'
|
||||||
path = os.getcwd()
|
path = os.getcwd()
|
||||||
python = which(python)
|
python = which(python)
|
||||||
@ -1070,12 +967,12 @@ def migrate_env(python, backup=False):
|
|||||||
|
|
||||||
redis = '{redis} -p {port}'.format(redis=which('redis-cli'), port=rredis.port)
|
redis = '{redis} -p {port}'.format(redis=which('redis-cli'), port=rredis.port)
|
||||||
|
|
||||||
logger.debug('Clearing Redis Cache...')
|
logger.log('Clearing Redis Cache...')
|
||||||
exec_cmd('{redis} FLUSHALL'.format(redis = redis))
|
exec_cmd('{redis} FLUSHALL'.format(redis = redis))
|
||||||
logger.debug('Clearing Redis DataBase...')
|
logger.log('Clearing Redis DataBase...')
|
||||||
exec_cmd('{redis} FLUSHDB'.format(redis = redis))
|
exec_cmd('{redis} FLUSHDB'.format(redis = redis))
|
||||||
except:
|
except:
|
||||||
logger.warn('Please ensure Redis Connections are running or Daemonized.')
|
logger.warning('Please ensure Redis Connections are running or Daemonized.')
|
||||||
|
|
||||||
# Backup venv: restore using `virtualenv --relocatable` if needed
|
# Backup venv: restore using `virtualenv --relocatable` if needed
|
||||||
if backup:
|
if backup:
|
||||||
@ -1086,7 +983,7 @@ def migrate_env(python, backup=False):
|
|||||||
source = os.path.join(path, 'env')
|
source = os.path.join(path, 'env')
|
||||||
target = parch
|
target = parch
|
||||||
|
|
||||||
logger.debug('Backing up Virtual Environment')
|
logger.log('Backing up Virtual Environment')
|
||||||
stamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
stamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||||
dest = os.path.join(path, str(stamp))
|
dest = os.path.join(path, str(stamp))
|
||||||
|
|
||||||
@ -1094,17 +991,18 @@ def migrate_env(python, backup=False):
|
|||||||
shutil.move(dest, target)
|
shutil.move(dest, target)
|
||||||
|
|
||||||
# Create virtualenv using specified python
|
# Create virtualenv using specified python
|
||||||
|
venv_creation, packages_setup = 1, 1
|
||||||
try:
|
try:
|
||||||
logger.debug('Setting up a New Virtual {} Environment'.format(python))
|
logger.log('Setting up a New Virtual {} Environment'.format(python))
|
||||||
exec_cmd('{virtualenv} --python {python} {pvenv}'.format(virtualenv=virtualenv, python=python, pvenv=pvenv))
|
venv_creation = exec_cmd('{virtualenv} --python {python} {pvenv}'.format(virtualenv=virtualenv, python=python, pvenv=pvenv))
|
||||||
|
|
||||||
apps = ' '.join(["-e {}".format(os.path.join("apps", app)) for app in get_apps()])
|
apps = ' '.join(["-e {}".format(os.path.join("apps", app)) for app in get_apps()])
|
||||||
exec_cmd('{0} install -q -U {1}'.format(pip, apps))
|
packages_setup = exec_cmd('{0} install -q -U {1}'.format(pip, apps))
|
||||||
|
|
||||||
logger.debug('Migration Successful to {}'.format(python))
|
logger.log('Migration Successful to {}'.format(python))
|
||||||
except:
|
except:
|
||||||
logger.debug('Migration Error')
|
if venv_creation or packages_setup:
|
||||||
raise
|
logger.warning('Migration Error')
|
||||||
|
|
||||||
|
|
||||||
def is_dist_editable(dist):
|
def is_dist_editable(dist):
|
||||||
|
@ -30,9 +30,6 @@ To manually install frappe/erpnext, you can follow this [this wiki](https://gith
|
|||||||
|
|
||||||
#### 2. Install Bench
|
#### 2. Install Bench
|
||||||
|
|
||||||
Install bench as a *non root* user,
|
Install the latest bench using pip
|
||||||
|
|
||||||
git clone https://github.com/frappe/bench ~/.bench
|
pip3 install frappe-bench
|
||||||
pip3 install --user -e ~/.bench
|
|
||||||
|
|
||||||
Note: Please do not remove the bench directory the above commands will create
|
|
||||||
|
Loading…
Reference in New Issue
Block a user