From 7035366663ff079d62218edf21615493cb63dafb Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 27 Apr 2021 15:48:28 +0530 Subject: [PATCH 01/13] ci: Trigger branch checks From 70c9c17f63efae44590799fae58e182e83816038 Mon Sep 17 00:00:00 2001 From: gavin Date: Tue, 27 Apr 2021 16:52:49 +0530 Subject: [PATCH 02/13] docs(README): Update broken ref link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 489d2484..1caa71b7 100755 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Bench is a command-line utility that helps you to install, update, and manage mu ## Table of Contents - [Installation](#installation) - - [Containerized Installation](#docker-installation) + - [Containerized Installation](#containerized-installation) - [Easy Install Script](#easy-install-script) - [Manual Installation](#manual-installation) - [Usage](#basic-usage) From ebc3cebd1990e2585a8cf2f8ad5e691754f5f36c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 2 Apr 2021 01:12:19 +0530 Subject: [PATCH 03/13] perf: Faster bench remove-app Biggest bottleneck is in the validation stage which checks if the app is installed on any existing sites on the bench. instead of triggering multiple list-apps for each sites, do one for all. --- bench/app.py | 56 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/bench/app.py b/bench/app.py index 872270ac..6ca99aab 100755 --- a/bench/app.py +++ b/bench/app.py @@ -3,6 +3,7 @@ from __future__ import print_function # imports - standard imports import json +from json.decoder import JSONDecodeError import logging import os import re @@ -202,13 +203,56 @@ def remove_app(app, bench_path='.'): import shutil from bench.config.common_site_config import get_config + app_path = os.path.join(bench_path, 'apps', app) + py = os.path.join(bench_path, 'env', 'bin', 'python') + + # validate app removal if app not in get_apps(bench_path): print("No app named {0}".format(app)) sys.exit(1) - app_path = os.path.join(bench_path, 'apps', app) + validate_app_installed_on_sites(app, bench_path=bench_path) + + # remove app from bench + exec_cmd("{0} -m pip uninstall -y {1}".format(py, app), cwd=bench_path) + remove_from_appstxt(app, bench_path) + shutil.rmtree(app_path) + + # re-build assets and restart processes + run_frappe_cmd("build", bench_path=bench_path) + if get_config(bench_path).get('restart_supervisor_on_update'): + restart_supervisor_processes(bench_path=bench_path) + if get_config(bench_path).get('restart_systemd_on_update'): + restart_systemd_processes(bench_path=bench_path) + + +def validate_app_installed_on_sites(app, bench_path="."): + print("Checking if app installed on active sites...") + ret = check_app_installed(app, bench_path=bench_path) + + if ret is None: + check_app_installed_legacy(app, bench_path=bench_path) + else: + return ret + + +def check_app_installed(app, bench_path="."): + out = subprocess.check_output(["bench", "--site", "all", "list-apps", "--format", "json"], cwd=bench_path).decode('utf-8') + + try: + import json + apps_sites_dict = json.loads(out) + except JSONDecodeError: + return None + + for site, apps in apps_sites_dict.items(): + if app in apps: + print("Cannot remove, app is installed on site: {0}".format(site)) + sys.exit(1) + + +def check_app_installed_legacy(app, bench_path="."): site_path = os.path.join(bench_path, 'sites') - py = os.path.join(bench_path, 'env', 'bin', 'python') for site in os.listdir(site_path): req_file = os.path.join(site_path, site, 'site_config.json') @@ -218,14 +262,6 @@ def remove_app(app, bench_path='.'): print("Cannot remove, app is installed on site: {0}".format(site)) sys.exit(1) - exec_cmd("{0} -m pip uninstall -y {1}".format(py, app), cwd=bench_path) - remove_from_appstxt(app, bench_path) - shutil.rmtree(app_path) - run_frappe_cmd("build", bench_path=bench_path) - if get_config(bench_path).get('restart_supervisor_on_update'): - restart_supervisor_processes(bench_path=bench_path) - if get_config(bench_path).get('restart_systemd_on_update'): - restart_systemd_processes(bench_path=bench_path) def pull_apps(apps=None, bench_path='.', reset=False): '''Check all apps if there no local changes, pull''' From 4cadf2c719f5ecf2580c28b4b2e687d7d8a69e6b Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 10 May 2021 14:45:46 +0530 Subject: [PATCH 04/13] fix: Unshallow clone if update without --reset If an unshallow clone has to be updated, the only way to update it without a reset will require an unshallow. This is because there may be uncommitted or committed changes in any of the apps, although unfavoured should be handled safely. The only way to update such repos is via the user configured settings..or a ff only merge. --- bench/app.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bench/app.py b/bench/app.py index 872270ac..9970655e 100755 --- a/bench/app.py +++ b/bench/app.py @@ -272,6 +272,14 @@ Here are your choices: 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)) continue + + if not get_config(bench_path).get('shallow_clone') or not reset: + is_shallow = os.path.exists(os.path.join(app_dir, ".git", "shallow")) + if is_shallow: + s = " to safely pull remote changes." if not reset else "" + print(f"Unshallowing {app}{s}") + exec_cmd(f"git fetch {remote} --unshallow", cwd=app_dir) + branch = get_current_branch(app, bench_path=bench_path) logger.log('pulling {0}'.format(app)) if reset: From 096c2cfe6b7a25d9a20c9fc81105c56855d49965 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 10 May 2021 14:51:14 +0530 Subject: [PATCH 05/13] fix: Show step titles while running operations --- bench/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bench/utils.py b/bench/utils.py index 7bb312ba..1937672e 100755 --- a/bench/utils.py +++ b/bench/utils.py @@ -221,9 +221,11 @@ def update(pull=False, apps=None, patch=False, build=False, requirements=False, apps = [app.strip() for app in re.split(",| ", apps) if app] if pull: + print('Updating apps source...') pull_apps(apps=apps, bench_path=bench_path, reset=reset) if requirements: + print('Setting up requirements...') update_requirements(bench_path=bench_path) update_node_packages(bench_path=bench_path) @@ -232,6 +234,7 @@ def update(pull=False, apps=None, patch=False, build=False, requirements=False, patch_sites(bench_path=bench_path) if build: + print('Building assets...') build_assets(bench_path=bench_path) if version_upgrade[0] or (not version_upgrade[0] and force): From f46126ce2b0e9c8607df192a3337eefb03c63c5c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 10 May 2021 14:51:52 +0530 Subject: [PATCH 06/13] chore: Remove deprecated option from config template --- bench/config/common_site_config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bench/config/common_site_config.py b/bench/config/common_site_config.py index 41d9cc9c..d7c492bf 100644 --- a/bench/config/common_site_config.py +++ b/bench/config/common_site_config.py @@ -8,7 +8,6 @@ import os default_config = { 'restart_supervisor_on_update': False, 'restart_systemd_on_update': False, - 'auto_update': False, 'serve_default_site': True, 'rebase_on_pull': False, 'frappe_user': getpass.getuser(), From 01abc561e07ab19d9c66b5f6d8b915497305d36f Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 10 May 2021 16:36:10 +0530 Subject: [PATCH 07/13] fix: Added warning for unshallow without --reset --- bench/utils.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/bench/utils.py b/bench/utils.py index 1937672e..93d2bda5 100755 --- a/bench/utils.py +++ b/bench/utils.py @@ -112,6 +112,16 @@ def get_env_cmd(cmd, bench_path='.'): return os.path.abspath(os.path.join(bench_path, 'env', 'bin', cmd)) +def pause_exec(seconds=10): + from time import sleep + + for i in range(seconds, 0, -1): + print(f"Will continue execution in {i} seconds...", end="\r") + sleep(1) + + print(" " * 40, end="\r") + + def init(path, apps_path=None, no_procfile=False, no_backups=False, frappe_path=None, frappe_branch=None, verbose=False, clone_from=None, skip_redis_config_generation=False, clone_without_update=False, ignore_exist=False, skip_assets=False, @@ -208,6 +218,16 @@ def update(pull=False, apps=None, patch=False, build=False, requirements=False, print("This update will cause a major version change in Frappe/ERPNext from {0} to {1}. \nThis would take significant time to migrate and might break custom apps.".format(*version_upgrade[1:])) click.confirm('Do you want to continue?', abort=True) + if not reset and conf.get('shallow_clone'): + log("""shallow_clone is set in your bench config. +However without passing the --reset flag, your repositories will be unshallowed. +To avoid this, cancel this operation and run `bench update --reset`. + +Consider the consequences of `git reset --hard` on your apps before you run that. +To avoid seeing this warning, set shallow_clone to false in your common_site_config.json + """, level=3) + pause_exec(seconds=10) + if version_upgrade[0] or (not version_upgrade[0] and force): validate_upgrade(version_upgrade[1], version_upgrade[2], bench_path=bench_path) conf.update({ "maintenance_mode": 1, "pause_scheduler": 1 }) From dcdb15d471f47ebfb42908d3b19bc2dac9e6d45a Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 10 May 2021 19:10:06 +0530 Subject: [PATCH 08/13] fix: Handle command execution failures --- bench/app.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bench/app.py b/bench/app.py index 6886e0a4..d3f9eff6 100755 --- a/bench/app.py +++ b/bench/app.py @@ -1,6 +1,3 @@ -# imports - compatibility imports -from __future__ import print_function - # imports - standard imports import json from json.decoder import JSONDecodeError @@ -237,10 +234,16 @@ def validate_app_installed_on_sites(app, bench_path="."): def check_app_installed(app, bench_path="."): - out = subprocess.check_output(["bench", "--site", "all", "list-apps", "--format", "json"], cwd=bench_path).decode('utf-8') + try: + out = subprocess.check_output( + ["bench", "--site", "all", "list-apps", "--format", "json"], + stderr=open(os.devnull, "wb"), + cwd=bench_path, + ).decode('utf-8') + except subprocess.CalledProcessError: + return None try: - import json apps_sites_dict = json.loads(out) except JSONDecodeError: return None From 5d563f23ab0a73e9eda79e0a1e1c786ebd9f6155 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 11 May 2021 11:28:26 +0530 Subject: [PATCH 09/13] style: Use f-strings instead of old string formatting methods These changes are valid only for Py3.6+ and will remove bench CLI support for Python 3.5 and lesser. * Converted to f-strings using flynt * Manual conversion of failed cases * Simplification of logic --- bench/app.py | 82 ++++++++--------- bench/cli.py | 4 +- bench/commands/git.py | 6 +- bench/commands/make.py | 6 +- bench/commands/setup.py | 4 +- bench/commands/utils.py | 2 +- bench/config/common_site_config.py | 2 +- bench/config/lets_encrypt.py | 25 +++--- bench/config/nginx.py | 10 +-- bench/config/production_setup.py | 34 ++++--- bench/config/redis.py | 2 +- bench/config/site_config.py | 2 +- bench/config/supervisor.py | 12 +-- bench/config/systemd.py | 16 +--- bench/patches/v5/fix_user_permissions.py | 8 +- bench/prepare_beta_release.py | 18 ++-- bench/release.py | 34 ++++--- bench/tests/test_base.py | 2 +- bench/tests/test_init.py | 2 +- bench/tests/test_setup_production.py | 58 ++++++------ bench/utils.py | 108 +++++++++++------------ 21 files changed, 207 insertions(+), 230 deletions(-) diff --git a/bench/app.py b/bench/app.py index 9970655e..f1d05af9 100755 --- a/bench/app.py +++ b/bench/app.py @@ -69,7 +69,7 @@ def add_to_excluded_apps_txt(app, bench_path='.'): if app == 'frappe': raise ValueError('Frappe app cannot be excludeed from update') if app not in os.listdir('apps'): - raise ValueError('The app {} does not exist'.format(app)) + raise ValueError(f'The app {app} does not exist') apps = get_excluded_apps(bench_path=bench_path) if app not in apps: apps.append(app) @@ -93,45 +93,42 @@ def get_app(git_url, branch=None, bench_path='.', skip_assets=False, verbose=Fal if not is_git_url(git_url): orgs = ['frappe', 'erpnext'] for org in orgs: - url = 'https://api.github.com/repos/{org}/{app}'.format(org=org, app=git_url) + url = f'https://api.github.com/repos/{org}/{git_url}' res = requests.get(url) if res.ok: data = res.json() if 'name' in data: if git_url == data['name']: - git_url = 'https://github.com/{org}/{app}'.format(org=org, app=git_url) + git_url = f'https://github.com/{org}/{git_url}' break else: - bench.utils.log("App {app} not found".format(app=git_url), level=2) + bench.utils.log(f"App {git_url} not found", level=2) sys.exit(1) # Gets repo name from URL repo_name = git_url.rstrip('/').rsplit('/', 1)[1].rsplit('.', 1)[0] shallow_clone = '--depth 1' if check_git_for_shallow_clone() else '' - branch = '--branch {branch}'.format(branch=branch) if branch else '' + branch = f'--branch {branch}' if branch else '' else: git_url = os.path.abspath(git_url) _, repo_name = os.path.split(git_url) shallow_clone = '' - branch = '--branch {branch}'.format(branch=branch) if branch else '' + branch = f'--branch {branch}' if branch else '' if os.path.isdir(os.path.join(bench_path, 'apps', repo_name)): # application directory already exists # prompt user to overwrite it - if overwrite or click.confirm('''A directory for the application "{0}" already exists. -Do you want to continue and overwrite it?'''.format(repo_name)): + if overwrite or click.confirm(f'''A directory for the application "{repo_name}" already exists. +Do you want to continue and overwrite it?'''): shutil.rmtree(os.path.join(bench_path, 'apps', repo_name)) elif click.confirm('''Do you want to reinstall the existing application?''', abort=True): app_name = get_app_name(bench_path, repo_name) install_app(app=app_name, bench_path=bench_path, verbose=verbose, skip_assets=skip_assets) sys.exit() - 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( - git_url=git_url, - shallow_clone=shallow_clone, - branch=branch), + print(f'\n{color.yellow}Getting {repo_name}{color.nc}') + logger.log(f'Getting app {repo_name}') + exec_cmd(f"git clone {git_url} {branch} {shallow_clone} --origin upstream", cwd=os.path.join(bench_path, 'apps')) app_name = get_app_name(bench_path, repo_name) @@ -162,7 +159,7 @@ def get_app_name(bench_path, repo_name): def new_app(app, bench_path='.'): # For backwards compatibility app = app.lower().replace(" ", "_").replace("-", "_") - logger.log('creating new app {}'.format(app)) + logger.log(f'creating new app {app}') apps = os.path.abspath(os.path.join(bench_path, 'apps')) run_frappe_cmd('make-app', apps, app, bench_path=bench_path) install_app(app, bench_path=bench_path) @@ -171,15 +168,15 @@ def new_app(app, bench_path='.'): def install_app(app, bench_path=".", verbose=False, no_cache=False, restart_bench=True, skip_assets=False): from bench.config.common_site_config import get_config - print('\n{0}Installing {1}{2}'.format(color.yellow, app, color.nc)) - logger.log("installing {}".format(app)) + print(f'\n{color.yellow}Installing {app}{color.nc}') + logger.log(f"installing {app}") python_path = os.path.join(bench_path, "env", "bin", "python") quiet_flag = "-q" if not verbose else "" app_path = os.path.join(bench_path, "apps", app) cache_flag = "--no-cache-dir" if no_cache else "" - exec_cmd("{py_path} -m pip install {quiet} -U -e {app} {no_cache}".format(py_path=python_path, quiet=quiet_flag, app=app_path, no_cache=cache_flag)) + exec_cmd(f"{python_path} -m pip install {quiet_flag} -U -e {app_path} {cache_flag}") if os.path.exists(os.path.join(app_path, 'package.json')): exec_cmd("yarn install", cwd=app_path) @@ -203,7 +200,7 @@ def remove_app(app, bench_path='.'): from bench.config.common_site_config import get_config if app not in get_apps(bench_path): - print("No app named {0}".format(app)) + print(f"No app named {app}") sys.exit(1) app_path = os.path.join(bench_path, 'apps', app) @@ -215,10 +212,10 @@ def remove_app(app, bench_path='.'): if os.path.exists(req_file): out = subprocess.check_output(["bench", "--site", site, "list-apps"], cwd=bench_path).decode('utf-8') if re.search(r'\b' + app + r'\b', out): - print("Cannot remove, app is installed on site: {0}".format(site)) + print(f"Cannot remove, app is installed on site: {site}") sys.exit(1) - exec_cmd("{0} -m pip uninstall -y {1}".format(py, app), cwd=bench_path) + exec_cmd(f"{py} -m pip uninstall -y {app}", cwd=bench_path) remove_from_appstxt(app, bench_path) shutil.rmtree(app_path) run_frappe_cmd("build", bench_path=bench_path) @@ -239,30 +236,30 @@ def pull_apps(apps=None, bench_path='.', reset=False): for app in apps: excluded_apps = get_excluded_apps() if app in excluded_apps: - print("Skipping reset for app {}".format(app)) + print(f"Skipping reset for app {app}") continue app_dir = get_repo_dir(app, bench_path=bench_path) if os.path.exists(os.path.join(app_dir, '.git')): out = subprocess.check_output('git status', shell=True, cwd=app_dir) out = out.decode('utf-8') if not re.search(r'nothing to commit, working (directory|tree) clean', out): - print(''' + print(f''' -Cannot proceed with update: You have local changes in app "{0}" that are not committed. +Cannot proceed with update: You have local changes in app "{app}" that are not committed. Here are your choices: -1. Merge the {0} app manually with "git pull" / "git pull --rebase" and fix conflicts. +1. Merge the {app} app manually with "git pull" / "git pull --rebase" and fix conflicts. 1. Temporarily remove your changes with "git stash" or discard them completely with "bench update --reset" or for individual repositries "git reset --hard" 2. If your changes are helpful for others, send in a pull request via GitHub and - wait for them to be merged in the core.'''.format(app)) + wait for them to be merged in the core.''') sys.exit(1) excluded_apps = get_excluded_apps() for app in apps: if app in excluded_apps: - print("Skipping pull for app {}".format(app)) + print(f"Skipping pull for app {app}") continue app_dir = get_repo_dir(app, bench_path=bench_path) if os.path.exists(os.path.join(app_dir, '.git')): @@ -270,7 +267,7 @@ Here are your choices: if not remote: # remote is False, i.e. remote doesn't exist, add the app to excluded_apps.txt 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(f"Skipping pull for app {app}, since remote doesn't exist, and adding it to excluded apps") continue if not get_config(bench_path).get('shallow_clone') or not reset: @@ -281,11 +278,11 @@ Here are your choices: exec_cmd(f"git fetch {remote} --unshallow", cwd=app_dir) branch = get_current_branch(app, bench_path=bench_path) - logger.log('pulling {0}'.format(app)) + logger.log(f'pulling {app}') if reset: - reset_cmd = "git reset --hard {remote}/{branch}".format(remote=remote, branch=branch) + reset_cmd = f"git reset --hard {remote}/{branch}" if get_config(bench_path).get('shallow_clone'): - exec_cmd("git fetch --depth=1 --no-tags {remote} {branch}".format(remote=remote, branch=branch), + exec_cmd(f"git fetch --depth=1 --no-tags {remote} {branch}", cwd=app_dir) exec_cmd(reset_cmd, cwd=app_dir) exec_cmd("git reflog expire --all", cwd=app_dir) @@ -294,8 +291,7 @@ Here are your choices: exec_cmd("git fetch --all", cwd=app_dir) exec_cmd(reset_cmd, cwd=app_dir) else: - exec_cmd("git pull {rebase} {remote} {branch}".format(rebase=rebase, - remote=remote, branch=branch), cwd=app_dir) + exec_cmd(f"git pull {rebase} {remote} {branch}", cwd=app_dir) exec_cmd('find . -name "*.pyc" -delete', cwd=app_dir) @@ -303,7 +299,7 @@ def is_version_upgrade(app='frappe', bench_path='.', branch=None): upstream_version = get_upstream_version(app=app, branch=branch, bench_path=bench_path) if not upstream_version: - raise InvalidBranchException('Specified branch of app {0} is not in upstream remote'.format(app)) + raise InvalidBranchException(f'Specified branch of app {app} is not in upstream remote') local_version = get_major_version(get_current_version(app, bench_path=bench_path)) upstream_version = get_major_version(upstream_version) @@ -374,12 +370,12 @@ def get_upstream_version(app, branch=None, bench_path='.'): branch = get_current_branch(app, bench_path=bench_path) try: - subprocess.call('git fetch --depth=1 --no-tags upstream {branch}'.format(branch=branch), shell=True, cwd=repo_dir) + subprocess.call(f'git fetch --depth=1 --no-tags upstream {branch}', shell=True, cwd=repo_dir) except CommandFailedError: - raise InvalidRemoteException('Failed to fetch from remote named upstream for {0}'.format(app)) + raise InvalidRemoteException(f'Failed to fetch from remote named upstream for {app}') try: - contents = subprocess.check_output('git show upstream/{branch}:{app}/__init__.py'.format(branch=branch, app=app), + contents = subprocess.check_output(f'git show upstream/{branch}:{app}/__init__.py', shell=True, cwd=repo_dir, stderr=subprocess.STDOUT) contents = contents.decode('utf-8') except subprocess.CalledProcessError as e: @@ -410,29 +406,29 @@ def switch_branch(branch, apps=None, bench_path='.', upgrade=False, check_upgrad app_dir = os.path.join(apps_dir, app) if not os.path.exists(app_dir): - bench.utils.log("{} does not exist!".format(app), level=2) + bench.utils.log(f"{app} does not exist!", level=2) continue repo = git.Repo(app_dir) unshallow_flag = os.path.exists(os.path.join(app_dir, ".git", "shallow")) - bench.utils.log("Fetching upstream {0}for {1}".format("unshallow " if unshallow_flag else "", app)) + bench.utils.log(f"Fetching upstream {'unshallow ' if unshallow_flag else ''}for {app}") bench.utils.exec_cmd("git remote set-branches upstream '*'", cwd=app_dir) - bench.utils.exec_cmd("git fetch --all{0} --quiet".format(" --unshallow" if unshallow_flag else ""), cwd=app_dir) + bench.utils.exec_cmd(f"git fetch --all{' --unshallow' if unshallow_flag else ''} --quiet", cwd=app_dir) if check_upgrade: version_upgrade = is_version_upgrade(app=app, bench_path=bench_path, branch=branch) if version_upgrade[0] and not upgrade: - bench.utils.log("Switching to {0} will cause upgrade from {1} to {2}. Pass --upgrade to confirm".format(branch, version_upgrade[1], version_upgrade[2]), level=2) + bench.utils.log(f"Switching to {branch} will cause upgrade from {version_upgrade[1]} to {version_upgrade[2]}. Pass --upgrade to confirm", level=2) sys.exit(1) print("Switching for "+app) - bench.utils.exec_cmd("git checkout -f {0}".format(branch), cwd=app_dir) + bench.utils.exec_cmd(f"git checkout -f {branch}", cwd=app_dir) if str(repo.active_branch) == branch: switched_apps.append(app) else: - bench.utils.log("Switching branches failed for: {}".format(app), level=2) + bench.utils.log(f"Switching branches failed for: {app}", level=2) if switched_apps: bench.utils.log("Successfully switched branches for: " + ", ".join(switched_apps), level=1) diff --git a/bench/cli.py b/bench/cli.py index 05c74d86..9fb29966 100755 --- a/bench/cli.py +++ b/bench/cli.py @@ -64,7 +64,7 @@ def cli(): except BaseException as e: return_code = getattr(e, "code", 0) if return_code: - logger.warning("{0} executed with exit code {1}".format(command, return_code)) + logger.warning(f"{command} executed with exit code {return_code}") sys.exit(return_code) @@ -140,7 +140,7 @@ def get_frappe_help(bench_path='.'): python = get_env_cmd('python', bench_path=bench_path) sites_path = os.path.join(bench_path, 'sites') try: - out = get_cmd_output("{python} -m frappe.utils.bench_helper get-frappe-help".format(python=python), cwd=sites_path) + 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] except: return "" diff --git a/bench/commands/git.py b/bench/commands/git.py index 8b52661b..41f05d24 100644 --- a/bench/commands/git.py +++ b/bench/commands/git.py @@ -19,7 +19,7 @@ def remote_set_url(git_url): @click.command('remote-reset-url', help="Reset app remote url to frappe official") @click.argument('app') def remote_reset_url(app): - git_url = "https://github.com/frappe/{}.git".format(app) + git_url = f"https://github.com/frappe/{app}.git" set_git_remote_url(git_url) @@ -30,6 +30,6 @@ def remote_urls(): if os.path.exists(os.path.join(repo_dir, '.git')): remote = get_remote(app) - remote_url = subprocess.check_output(['git', 'config', '--get', 'remote.{}.url'.format(remote)], cwd=repo_dir).strip() - print("{app} {remote_url}".format(app=app, remote_url=remote_url)) + remote_url = subprocess.check_output(['git', 'config', '--get', f'remote.{remote}.url'], cwd=repo_dir).strip() + print(f"{app}\t{remote_url}") diff --git a/bench/commands/make.py b/bench/commands/make.py index f47a72e0..80061e9c 100755 --- a/bench/commands/make.py +++ b/bench/commands/make.py @@ -35,7 +35,7 @@ def init(path, apps_path, frappe_path, frappe_branch, no_procfile, no_backups, c skip_assets=skip_assets, python=python, ) - log('Bench {} initialized'.format(path), level=1) + log(f'Bench {path} initialized', level=1) except SystemExit: pass except Exception as e: @@ -43,9 +43,9 @@ def init(path, apps_path, frappe_path, frappe_branch, no_procfile, no_backups, c # add a sleep here so that the traceback of other processes doesnt overlap with the prompts time.sleep(1) print(e) - log("There was a problem while creating {}".format(path), level=2) + log(f"There was a problem while creating {path}", level=2) if six.moves.input("Do you want to rollback these changes? [Y/n]: ").lower() == "y": - print('Rolling back Bench "{}"'.format(path)) + print(f'Rolling back Bench "{path}"') if os.path.exists(path): shutil.rmtree(path) diff --git a/bench/commands/setup.py b/bench/commands/setup.py index 8021f835..b1a6be0e 100755 --- a/bench/commands/setup.py +++ b/bench/commands/setup.py @@ -84,7 +84,7 @@ def setup_env(python="python3"): @click.option("--force") def setup_firewall(ssh_port=None, force=False): if not force: - click.confirm("Setting up the firewall will block all ports except 80, 443 and {0}\nDo you want to continue?".format(ssh_port), abort=True) + click.confirm(f"Setting up the firewall will block all ports except 80, 443 and {ssh_port}\nDo you want to continue?", abort=True) if not ssh_port: ssh_port = 22 @@ -97,7 +97,7 @@ def setup_firewall(ssh_port=None, force=False): @click.option("--force") def set_ssh_port(port, force=False): if not force: - click.confirm("This will change your SSH Port to {}\nDo you want to continue?".format(port), abort=True) + click.confirm(f"This will change your SSH Port to {port}\nDo you want to continue?", abort=True) run_playbook("roles/bench/tasks/change_ssh_port.yml", {"ssh_port": port}) diff --git a/bench/commands/utils.py b/bench/commands/utils.py index 94d87b06..3d73e940 100644 --- a/bench/commands/utils.py +++ b/bench/commands/utils.py @@ -116,7 +116,7 @@ def renew_lets_encrypt(): def backup_site(site): from bench.utils import get_sites, backup_site if site not in get_sites(bench_path='.'): - print('Site `{0}` not found'.format(site)) + print(f'Site `{site}` not found') sys.exit(1) backup_site(site, bench_path='.') diff --git a/bench/config/common_site_config.py b/bench/config/common_site_config.py index d7c492bf..d787824a 100644 --- a/bench/config/common_site_config.py +++ b/bench/config/common_site_config.py @@ -61,7 +61,7 @@ def update_config_for_frappe(config, bench_path): for key in ('redis_cache', 'redis_queue', 'redis_socketio'): if key not in config: - config[key] = "redis://localhost:{0}".format(ports[key]) + config[key] = f"redis://localhost:{ports[key]}" for key in ('webserver_port', 'socketio_port', 'file_watcher_port'): if key not in config: diff --git a/bench/config/lets_encrypt.py b/bench/config/lets_encrypt.py index 752d361c..59d29f7d 100755 --- a/bench/config/lets_encrypt.py +++ b/bench/config/lets_encrypt.py @@ -24,11 +24,11 @@ def setup_letsencrypt(site, custom_domain, bench_path, interactive): domains = get_domains(site, bench_path) for d in domains: if (isinstance(d, dict) and d['domain']==custom_domain): - print("SSL for Domain {0} already exists".format(custom_domain)) + print(f"SSL for Domain {custom_domain} already exists") return if not custom_domain in domains: - print("No custom domain named {0} set for site".format(custom_domain)) + print(f"No custom domain named {custom_domain} set for site") return if interactive: @@ -47,7 +47,7 @@ def setup_letsencrypt(site, custom_domain, bench_path, interactive): def create_config(site, custom_domain): config = bench.config.env().get_template('letsencrypt.cfg').render(domain=custom_domain or site) - config_path = '/etc/letsencrypt/configs/{site}.cfg'.format(site=custom_domain or site) + config_path = f'/etc/letsencrypt/configs/{custom_domain or site}.cfg' create_dir_if_missing(config_path) with open(config_path, 'w') as f: @@ -60,13 +60,13 @@ def run_certbot_and_setup_ssl(site, custom_domain, bench_path, interactive=True) try: interactive = '' if interactive else '-n' - exec_cmd("{path} {interactive} --config /etc/letsencrypt/configs/{site}.cfg certonly".format(path=get_certbot_path(), interactive=interactive, site=custom_domain or site)) + exec_cmd(f"{get_certbot_path()} {interactive} --config /etc/letsencrypt/configs/{custom_domain or site}.cfg certonly") except CommandFailedError: service('nginx', 'start') print("There was a problem trying to setup SSL for your site") return - ssl_path = "/etc/letsencrypt/live/{site}/".format(site=custom_domain or site) + ssl_path = f"/etc/letsencrypt/live/{custom_domain or site}/" ssl_config = { "ssl_certificate": os.path.join(ssl_path, "fullchain.pem"), "ssl_certificate_key": os.path.join(ssl_path, "privkey.pem") } @@ -88,7 +88,7 @@ def setup_crontab(): job_command = '/opt/certbot-auto renew -a nginx --post-hook "systemctl reload nginx"' job_comment = 'Renew lets-encrypt every month' - print("Setting Up cron job to {0}".format(job_comment)) + print(f"Setting Up cron job to {job_comment}") system_crontab = CronTab(user='root') @@ -129,7 +129,7 @@ def renew_certs(): setup_crontab() service('nginx', 'stop') - exec_cmd("{path} renew".format(path=get_certbot_path())) + exec_cmd(f"{get_certbot_path()} renew") service('nginx', 'start') @@ -140,7 +140,7 @@ def setup_wildcard_ssl(domain, email, bench_path, exclude_base_domain): if not domain.startswith('*.'): # add wildcard caracter to domain if missing - domain_list.append('*.{0}'.format(domain)) + domain_list.append(f'*.{domain}') else: # include base domain based on flag domain_list.append(domain.replace('*.', '')) @@ -159,19 +159,18 @@ def setup_wildcard_ssl(domain, email, bench_path, exclude_base_domain): email_param = '' if email: - email_param = '--email {0}'.format(email) + email_param = f'--email {email}' try: - exec_cmd("{path} certonly --manual --preferred-challenges=dns {email_param} \ + exec_cmd(f"{get_certbot_path()} certonly --manual --preferred-challenges=dns {email_param} \ --server https://acme-v02.api.letsencrypt.org/directory \ - --agree-tos -d {domain}".format(path=get_certbot_path(), domain=' -d '.join(domain_list), - email_param=email_param)) + --agree-tos -d {' -d '.join(domain_list)}") except CommandFailedError: print("There was a problem trying to setup SSL") return - ssl_path = "/etc/letsencrypt/live/{domain}/".format(domain=domain) + ssl_path = f"/etc/letsencrypt/live/{domain}/" ssl_config = { "wildcard": { "domain": domain, diff --git a/bench/config/nginx.py b/bench/config/nginx.py index 4ed4233d..2210a46d 100644 --- a/bench/config/nginx.py +++ b/bench/config/nginx.py @@ -165,15 +165,15 @@ def prepare_sites(config, bench_path): for port_number in ports_in_use: if len(ports_in_use[port_number]) > 1: port_conflict_index += 1 - message += "\n{0} - Port {1} is shared among sites:".format(port_conflict_index,port_number) + message += f"\n{port_conflict_index} - Port {port_number} is shared among sites:" for site_name in ports_in_use[port_number]: - message += " {0}".format(site_name) + message += f" {site_name}" raise Exception(message) if not dns_multitenant: message = "Port configuration list:" for site in sites_configs: - message += "\n\nSite {0} assigned port: {1}".format(site["name"], site["port"]) + message += f"\n\nSite {site['name']} assigned port: {site['port']}" print(message) @@ -196,13 +196,13 @@ def get_sites_with_config(bench_path): except Exception as e: strict_nginx = get_config(bench_path).get('strict_nginx') if strict_nginx: - print("\n\nERROR: The site config for the site {} is broken.".format(site), + print(f"\n\nERROR: The site config for the site {site} is broken.", "If you want this command to pass, instead of just throwing an error,", "You may remove the 'strict_nginx' flag from common_site_config.json or set it to 0", "\n\n") raise (e) else: - print("\n\nWARNING: The site config for the site {} is broken.".format(site), + print(f"\n\nWARNING: The site config for the site {site} is broken.", "If you want this command to fail, instead of just showing a warning,", "You may add the 'strict_nginx' flag to common_site_config.json and set it to 1", "\n\n") diff --git a/bench/config/production_setup.py b/bench/config/production_setup.py index 13640e6f..07f39e4b 100755 --- a/bench/config/production_setup.py +++ b/bench/config/production_setup.py @@ -18,7 +18,7 @@ logger = logging.getLogger(bench.PROJECT_NAME) def setup_production_prerequisites(): """Installs ansible, fail2banc, NGINX and supervisor""" if not which("ansible"): - exec_cmd("sudo {0} -m pip install ansible".format(sys.executable)) + exec_cmd(f"sudo {sys.executable} -m pip install ansible") if not which("fail2ban-client"): exec_cmd("bench setup role fail2ban") if not which("nginx"): @@ -47,13 +47,12 @@ def setup_production(user, bench_path='.', yes=False): remove_default_nginx_configs() bench_name = get_bench_name(bench_path) - nginx_conf = '/etc/nginx/conf.d/{bench_name}.conf'.format(bench_name=bench_name) + nginx_conf = f'/etc/nginx/conf.d/{bench_name}.conf' print("Setting Up symlinks and reloading services...") if get_config(bench_path).get('restart_supervisor_on_update'): supervisor_conf_extn = "ini" if is_centos7() else "conf" - supervisor_conf = os.path.join(get_supervisor_confdir(), '{bench_name}.{extn}'.format( - bench_name=bench_name, extn=supervisor_conf_extn)) + supervisor_conf = os.path.join(get_supervisor_confdir(), f'{bench_name}.{supervisor_conf_extn}') # Check if symlink exists, If not then create it. if not os.path.islink(supervisor_conf): @@ -76,8 +75,7 @@ def disable_production(bench_path='.'): # supervisorctl supervisor_conf_extn = "ini" if is_centos7() else "conf" - supervisor_conf = os.path.join(get_supervisor_confdir(), '{bench_name}.{extn}'.format( - bench_name=bench_name, extn=supervisor_conf_extn)) + supervisor_conf = os.path.join(get_supervisor_confdir(), f'{bench_name}.{supervisor_conf_extn}') if os.path.islink(supervisor_conf): os.unlink(supervisor_conf) @@ -86,7 +84,7 @@ def disable_production(bench_path='.'): reload_supervisor() # nginx - nginx_conf = '/etc/nginx/conf.d/{bench_name}.conf'.format(bench_name=bench_name) + nginx_conf = f'/etc/nginx/conf.d/{bench_name}.conf' if os.path.islink(nginx_conf): os.unlink(nginx_conf) @@ -96,23 +94,23 @@ def disable_production(bench_path='.'): def service(service_name, service_option): if os.path.basename(which('systemctl') or '') == 'systemctl' and is_running_systemd(): - systemctl_cmd = "sudo {service_manager} {service_option} {service_name}" - exec_cmd(systemctl_cmd.format(service_manager='systemctl', service_option=service_option, service_name=service_name)) + exec_cmd(f"sudo systemctl {service_option} {service_name}") elif os.path.basename(which('service') or '') == 'service': - service_cmd = "sudo {service_manager} {service_name} {service_option}" - exec_cmd(service_cmd.format(service_manager='service', service_name=service_name, service_option=service_option)) + exec_cmd(f"sudo service {service_name} {service_option}") else: # look for 'service_manager' and 'service_manager_command' in environment service_manager = os.environ.get("BENCH_SERVICE_MANAGER") if service_manager: - service_manager_command = (os.environ.get("BENCH_SERVICE_MANAGER_COMMAND") - or "{service_manager} {service_option} {service}").format(service_manager=service_manager, service=service, service_option=service_option) + service_manager_command = ( + os.environ.get("BENCH_SERVICE_MANAGER_COMMAND") + or f"{service_manager} {service_option} {service}" + ) exec_cmd(service_manager_command) else: - log("No service manager found: '{0} {1}' failed to execute".format(service_name, service_option), level=2) + log(f"No service manager found: '{service_name} {service_option}' failed to execute", level=2) def get_supervisor_confdir(): @@ -149,15 +147,15 @@ def reload_supervisor(): try: # first try reread/update - exec_cmd('{0} reread'.format(supervisorctl)) - exec_cmd('{0} update'.format(supervisorctl)) + exec_cmd(f'{supervisorctl} reread') + exec_cmd(f'{supervisorctl} update') return except CommandFailedError: pass try: # something is wrong, so try reloading - exec_cmd('{0} reload'.format(supervisorctl)) + exec_cmd(f'{supervisorctl} reload') return except CommandFailedError: pass @@ -178,7 +176,7 @@ def reload_supervisor(): def reload_nginx(): try: - exec_cmd('sudo {0} -t'.format(which('nginx'))) + exec_cmd(f"sudo {which('nginx')} -t") except: raise diff --git a/bench/config/redis.py b/bench/config/redis.py index 7af1bbf7..16226099 100644 --- a/bench/config/redis.py +++ b/bench/config/redis.py @@ -69,7 +69,7 @@ def get_redis_version(): return None version = semantic_version.Version(version[0], partial=True) - return float('{major}.{minor}'.format(major=version.major, minor=version.minor)) + return float(f'{version.major}.{version.minor}') def get_max_redis_memory(): try: diff --git a/bench/config/site_config.py b/bench/config/site_config.py index 9ea67a86..cbd759b3 100644 --- a/bench/config/site_config.py +++ b/bench/config/site_config.py @@ -49,7 +49,7 @@ def add_domain(site, domain, ssl_certificate, ssl_certificate_key, bench_path='. domains = get_domains(site, bench_path) for d in domains: if (isinstance(d, dict) and d['domain']==domain) or d==domain: - print("Domain {0} already exists".format(domain)) + print(f"Domain {domain} already exists") return if ssl_certificate_key and ssl_certificate: diff --git a/bench/config/supervisor.py b/bench/config/supervisor.py index 0cea009e..1658731a 100644 --- a/bench/config/supervisor.py +++ b/bench/config/supervisor.py @@ -78,7 +78,7 @@ def update_supervisord_config(user=None, yes=False): section = "unix_http_server" updated_values = { "chmod": "0760", - "chown": "{user}:{user}".format(user=user) + "chown": f"{user}:{user}" } supervisord_conf_changes = "" @@ -91,7 +91,7 @@ def update_supervisord_config(user=None, yes=False): if section not in config.sections(): config.add_section(section) - action = "Section {0} Added".format(section) + action = f"Section {section} Added" logger.log(action) supervisord_conf_changes += '\n' + action @@ -103,7 +103,7 @@ def update_supervisord_config(user=None, yes=False): if current_value.strip() != value: config.set(section, key, value) - action = "Updated supervisord.conf: '{0}' changed from '{1}' to '{2}'".format(key, current_value, value) + action = f"Updated supervisord.conf: '{key}' changed from '{current_value}' to '{value}'" logger.log(action) supervisord_conf_changes += '\n' + action @@ -112,14 +112,14 @@ def update_supervisord_config(user=None, yes=False): return if not yes: - click.confirm("{0} will be updated with the following values:\n{1}\nDo you want to continue?".format(supervisord_conf, supervisord_conf_changes), abort=True) + click.confirm(f"{supervisord_conf} will be updated with the following values:\n{supervisord_conf_changes}\nDo you want to continue?", abort=True) try: with open(supervisord_conf, "w") as f: config.write(f) - logger.log("Updated supervisord.conf at '{0}'".format(supervisord_conf)) + logger.log(f"Updated supervisord.conf at '{supervisord_conf}'") except Exception as e: - logger.log("Updating supervisord.conf failed due to '{0}'".format(e)) + logger.log(f"Updating supervisord.conf failed due to '{e}'") # Reread supervisor configuration, reload supervisord and supervisorctl, restart services that were started service('supervisor', 'reload') diff --git a/bench/config/systemd.py b/bench/config/systemd.py index 1501d455..a25f6e51 100644 --- a/bench/config/systemd.py +++ b/bench/config/systemd.py @@ -25,7 +25,7 @@ def generate_systemd_config(bench_path, user=None, yes=False, bench_name = get_bench_name(bench_path) if stop: - exec_cmd('sudo systemctl stop -- $(systemctl show -p Requires {bench_name}.target | cut -d= -f2)'.format(bench_name=bench_name)) + exec_cmd(f'sudo systemctl stop -- $(systemctl show -p Requires {bench_name}.target | cut -d= -f2)') return if create_symlinks: @@ -185,25 +185,15 @@ def _create_symlinks(bench_path): unit_files = get_unit_files(bench_dir) for unit_file in unit_files: filename = "".join(unit_file) - exec_cmd('sudo ln -s {config_path}/{unit_file} {etc_systemd_system}/{unit_file_init}'.format( - config_path=config_path, - etc_systemd_system=etc_systemd_system, - unit_file=filename, - unit_file_init="".join(unit_file) - )) + exec_cmd(f'sudo ln -s {config_path}/{filename} {etc_systemd_system}/{"".join(unit_file)}') exec_cmd('sudo systemctl daemon-reload') def _delete_symlinks(bench_path): bench_dir = os.path.abspath(bench_path) etc_systemd_system = os.path.join('/', 'etc', 'systemd', 'system') - config_path = os.path.join(bench_dir, 'config', 'systemd') unit_files = get_unit_files(bench_dir) for unit_file in unit_files: - exec_cmd('sudo rm {etc_systemd_system}/{unit_file_init}'.format( - config_path=config_path, - etc_systemd_system=etc_systemd_system, - unit_file_init="".join(unit_file) - )) + exec_cmd(f'sudo rm {etc_systemd_system}/{"".join(unit_file)}') exec_cmd('sudo systemctl daemon-reload') def get_unit_files(bench_path): diff --git a/bench/patches/v5/fix_user_permissions.py b/bench/patches/v5/fix_user_permissions.py index bc0d4e93..85dea8e6 100644 --- a/bench/patches/v5/fix_user_permissions.py +++ b/bench/patches/v5/fix_user_permissions.py @@ -34,13 +34,13 @@ def is_production_set(bench_path): bench_name = get_bench_name(bench_path) supervisor_conf_extn = "ini" if is_centos7() else "conf" - supervisor_conf_file_name = '{bench_name}.{extn}'.format(bench_name=bench_name, extn=supervisor_conf_extn) + supervisor_conf_file_name = f'{bench_name}.{supervisor_conf_extn}' supervisor_conf = os.path.join(get_supervisor_confdir(), supervisor_conf_file_name) if os.path.exists(supervisor_conf): production_setup = production_setup or True - nginx_conf = '/etc/nginx/conf.d/{bench_name}.conf'.format(bench_name=bench_name) + nginx_conf = f'/etc/nginx/conf.d/{bench_name}.conf' if os.path.exists(nginx_conf): production_setup = production_setup or True @@ -54,7 +54,7 @@ def execute(bench_path): if is_sudoers_set(): if is_production_set(bench_path): - exec_cmd("sudo bench setup supervisor --yes --user {user}".format(user=user)) + exec_cmd(f"sudo bench setup supervisor --yes --user {user}") service("supervisord", "restart") - exec_cmd("sudo bench setup sudoers {user}".format(user=user)) + exec_cmd(f"sudo bench setup sudoers {user}") diff --git a/bench/prepare_beta_release.py b/bench/prepare_beta_release.py index 04c4c109..52054b9c 100644 --- a/bench/prepare_beta_release.py +++ b/bench/prepare_beta_release.py @@ -15,7 +15,7 @@ def prepare_beta_release(bench_path, app, owner='frappe', remote='upstream'): beta_master = click.prompt('Branch name for beta release', type=str) if click.confirm("Do you want to setup hotfix for beta ?"): - beta_hotfix = click.prompt('Branch name for beta hotfix ({}_hotifx)'.format(beta_master), type=str) + beta_hotfix = click.prompt(f'Branch name for beta hotfix ({beta_master}_hotifx)', type=str) validate(bench_path) repo_path = os.path.join(bench_path, 'apps', app) @@ -26,7 +26,7 @@ def prepare_beta_release(bench_path, app, owner='frappe', remote='upstream'): if beta_hotfix: prepare_beta_hotfix(repo_path, beta_hotfix, remote) - + tag_name = merge_beta_release_to_develop(repo_path, beta_master, remote, version) push_branches(repo_path, beta_master, beta_hotfix, remote) create_github_release(repo_path, tag_name, '', owner, remote) @@ -68,8 +68,8 @@ def set_beta_version(repo_path, version): repo = git.Repo(repo_path) app_name = os.path.basename(repo_path) repo.index.add([os.path.join(app_name, 'hooks.py')]) - repo.index.commit('bumped to version {}'.format(version)) - + repo.index.commit(f'bumped to version {version}') + def prepare_beta_hotfix(repo_path, beta_hotfix, remote): g = git.Repo(repo_path).git @@ -83,7 +83,7 @@ def merge_beta_release_to_develop(repo_path, beta_master, remote, version): g = repo.git tag_name = 'v' + version - repo.create_tag(tag_name, message='Release {}'.format(version)) + repo.create_tag(tag_name, message=f'Release {version}') g.checkout('develop') @@ -100,12 +100,12 @@ def push_branches(repo_path, beta_master, beta_hotfix, remote): args = [ 'develop:develop', - '{beta_master}:{beta_master}'.format(beta_master=beta_master), + f'{beta_master}:{beta_master}', ] if beta_hotfix: - args.append('{beta_hotfix}:{beta_hotfix}'.format(beta_hotfix=beta_hotfix)) - + args.append(f'{beta_hotfix}:{beta_hotfix}') + args.append('--tags') print("Pushing branches") @@ -114,5 +114,5 @@ def push_branches(repo_path, beta_master, beta_hotfix, remote): def create_github_release(repo_path, tag_name, message, owner, remote): from .release import create_github_release - create_github_release(repo_path, tag_name, message, remote=remote, owner=owner, + create_github_release(repo_path, tag_name, message, remote=remote, owner=owner, repo_name=None, gh_username=github_username, gh_password=github_password) \ No newline at end of file diff --git a/bench/release.py b/bench/release.py index 81efdc02..98911cd7 100755 --- a/bench/release.py +++ b/bench/release.py @@ -93,7 +93,7 @@ def bump(bench_path, app, bump_type, from_branch, to_branch, remote, owner, repo push_release(repo_path, from_branch=from_branch, to_branch=to_branch, remote=remote) prerelease = True if 'beta' in new_version else False create_github_release(repo_path, tag_name, message, remote=remote, owner=owner, repo_name=repo_name, prerelease=prerelease) - print('Released {tag} for {repo_path}'.format(tag=tag_name, repo_path=repo_path)) + print(f'Released {tag_name} for {repo_path}') def update_branches_and_check_for_changelog(repo_path, from_branch, to_branch, remote='upstream'): @@ -125,8 +125,7 @@ def get_release_message(repo_path, from_branch, to_branch, remote='upstream'): repo = git.Repo(repo_path) g = repo.git - log = g.log('{remote}/{to_branch}..{remote}/{from_branch}'.format( - remote=remote, to_branch=to_branch, from_branch=from_branch), '--format=format:%s', '--no-merges') + log = g.log(f'{remote}/{to_branch}..{remote}/{from_branch}', '--format=format:%s', '--no-merges') if log: return "* " + log.replace('\n', '\n* ') @@ -246,7 +245,7 @@ def commit_changes(repo_path, new_version, to_branch): else: repo.index.add([os.path.join(app_name, 'hooks.py')]) - repo.index.commit('bumped to version {}'.format(new_version)) + repo.index.commit(f'bumped to version {new_version}') def create_release(repo_path, new_version, from_branch, to_branch, frontport=True): print('creating release for version', new_version) @@ -259,7 +258,7 @@ def create_release(repo_path, new_version, from_branch, to_branch, frontport=Tru handle_merge_error(e, source=from_branch, target=to_branch) tag_name = 'v' + new_version - repo.create_tag(tag_name, message='Release {}'.format(new_version)) + repo.create_tag(tag_name, message=f'Release {new_version}') g.checkout(from_branch) try: @@ -269,8 +268,8 @@ def create_release(repo_path, new_version, from_branch, to_branch, frontport=Tru if frontport: for branch in branches_to_update[from_branch]: - print ("Front porting changes to {}".format(branch)) - print('merging {0} into'.format(to_branch), branch) + print (f"Front porting changes to {branch}") + print(f'merging {to_branch} into', branch) g.checkout(branch) try: g.merge(to_branch) @@ -281,7 +280,7 @@ def create_release(repo_path, new_version, from_branch, to_branch, frontport=Tru def handle_merge_error(e, source, target): print('-'*80) - print('Error when merging {source} into {target}'.format(source=source, target=target)) + print(f'Error when merging {source} into {target}') print(e) print('You can open a new terminal, try to manually resolve the conflict/error and continue') print('-'*80) @@ -292,13 +291,13 @@ def push_release(repo_path, from_branch, to_branch, remote='upstream'): repo = git.Repo(repo_path) g = repo.git args = [ - '{to_branch}:{to_branch}'.format(to_branch=to_branch), - '{from_branch}:{from_branch}'.format(from_branch=from_branch) + f'{to_branch}:{to_branch}', + f'{from_branch}:{from_branch}' ] for branch in branches_to_update[from_branch]: - print('pushing {0} branch of'.format(branch), repo_path) - args.append('{branch}:{branch}'.format(branch=branch)) + print(f'pushing {branch} branch of', repo_path) + args.append(f'{branch}:{branch}') args.append('--tags') @@ -330,8 +329,7 @@ def create_github_release(repo_path, tag_name, message, remote='upstream', owner } for i in range(3): try: - r = requests.post('https://api.github.com/repos/{owner}/{repo_name}/releases'.format( - owner=owner, repo_name=repo_name), + r = requests.post(f'https://api.github.com/repos/{owner}/{repo_name}/releases', auth=HTTPBasicAuth(gh_username, gh_password), data=json.dumps(data)) r.raise_for_status() break @@ -350,9 +348,9 @@ def push_branch_for_old_major_version(bench_path, bump_type, app, repo_path, fro return current_version = get_current_version(repo_path) - old_major_version_branch = "v{major}.x.x".format(major=current_version.split('.')[0]) + old_major_version_branch = f"v{current_version.split('.')[0]}.x.x" - click.confirm('Do you want to push {branch}?'.format(branch=old_major_version_branch), abort=True) + click.confirm(f'Do you want to push {old_major_version_branch}?', abort=True) update_branch(repo_path, to_branch, remote=remote) @@ -360,8 +358,8 @@ def push_branch_for_old_major_version(bench_path, bump_type, app, repo_path, fro g.checkout(b=old_major_version_branch) args = [ - '{old_major_version_branch}:{old_major_version_branch}'.format(old_major_version_branch=old_major_version_branch), + f'{old_major_version_branch}:{old_major_version_branch}', ] - print("Pushing {old_major_version_branch} ".format(old_major_version_branch=old_major_version_branch)) + print(f"Pushing {old_major_version_branch} ") print(g.push(remote, *args)) diff --git a/bench/tests/test_base.py b/bench/tests/test_base.py index edda52a3..bb948dfc 100644 --- a/bench/tests/test_base.py +++ b/bench/tests/test_base.py @@ -84,7 +84,7 @@ class TestBenchBase(unittest.TestCase): frappe_tmp_path = "/tmp/frappe" if not os.path.exists(frappe_tmp_path): - bench.utils.exec_cmd("git clone https://github.com/frappe/frappe -b {branch} --depth 1 --origin upstream {location}".format(branch=FRAPPE_BRANCH, location=frappe_tmp_path)) + bench.utils.exec_cmd(f"git clone https://github.com/frappe/frappe -b {FRAPPE_BRANCH} --depth 1 --origin upstream {frappe_tmp_path}") kwargs.update(dict( python=sys.executable, diff --git a/bench/tests/test_init.py b/bench/tests/test_init.py index 8ac7f548..cbc6a0ea 100755 --- a/bench/tests/test_init.py +++ b/bench/tests/test_init.py @@ -121,7 +121,7 @@ class TestBenchInit(TestBenchBase): # create and install app on site self.new_site(site_name, bench_name) - installed_app = not bench.utils.exec_cmd("bench --site {0} install-app frappe_theme".format(site_name), cwd=bench_path) + installed_app = not bench.utils.exec_cmd(f"bench --site {site_name} install-app frappe_theme", cwd=bench_path) app_installed_on_site = subprocess.check_output(["bench", "--site", site_name, "list-apps"], cwd=bench_path).decode('utf8') diff --git a/bench/tests/test_setup_production.py b/bench/tests/test_setup_production.py index 41ff9a99..47358dc5 100644 --- a/bench/tests/test_setup_production.py +++ b/bench/tests/test_setup_production.py @@ -19,13 +19,13 @@ class TestSetupProduction(TestBenchBase): for bench_name in ("test-bench-1", "test-bench-2"): bench_path = os.path.join(os.path.abspath(self.benches_path), bench_name) self.init_bench(bench_name) - bench.utils.exec_cmd("sudo bench setup production {0} --yes".format(user), cwd=bench_path) + bench.utils.exec_cmd(f"sudo bench setup production {user} --yes", cwd=bench_path) self.assert_nginx_config(bench_name) self.assert_supervisor_config(bench_name) self.assert_supervisor_process(bench_name) self.assert_nginx_process() - bench.utils.exec_cmd("sudo bench setup sudoers {0}".format(user)) + bench.utils.exec_cmd(f"sudo bench setup sudoers {user}") self.assert_sudoers(user) for bench_name in self.benches: @@ -42,7 +42,7 @@ class TestSetupProduction(TestBenchBase): def assert_nginx_config(self, bench_name): conf_src = os.path.join(os.path.abspath(self.benches_path), bench_name, 'config', 'nginx.conf') - conf_dest = "/etc/nginx/conf.d/{bench_name}.conf".format(bench_name=bench_name) + conf_dest = f"/etc/nginx/conf.d/{bench_name}.conf" self.assertTrue(self.file_exists(conf_src)) self.assertTrue(self.file_exists(conf_dest)) @@ -55,10 +55,10 @@ class TestSetupProduction(TestBenchBase): f = f.read() for key in ( - "upstream {bench_name}-frappe", - "upstream {bench_name}-socketio-server" + f"upstream {bench_name}-frappe", + f"upstream {bench_name}-socketio-server" ): - self.assertTrue(key.format(bench_name=bench_name) in f) + self.assertTrue(key in f) def assert_nginx_process(self): @@ -79,15 +79,15 @@ class TestSetupProduction(TestBenchBase): with open(sudoers_file, 'r') as f: sudoers = f.read() - self.assertTrue('{user} ALL = (root) NOPASSWD: {service} nginx *'.format(service=service, user=user) in sudoers) - self.assertTrue('{user} ALL = (root) NOPASSWD: {nginx}'.format(nginx=nginx, user=user) in sudoers) + self.assertTrue(f'{user} ALL = (root) NOPASSWD: {service} nginx *' in sudoers) + self.assertTrue(f'{user} ALL = (root) NOPASSWD: {nginx}' in sudoers) def assert_supervisor_config(self, bench_name, use_rq=True): conf_src = os.path.join(os.path.abspath(self.benches_path), bench_name, 'config', 'supervisor.conf') supervisor_conf_dir = get_supervisor_confdir() - conf_dest = "{supervisor_conf_dir}/{bench_name}.conf".format(supervisor_conf_dir=supervisor_conf_dir, bench_name=bench_name) + conf_dest = f"{supervisor_conf_dir}/{bench_name}.conf" self.assertTrue(self.file_exists(conf_src)) self.assertTrue(self.file_exists(conf_dest)) @@ -100,38 +100,36 @@ class TestSetupProduction(TestBenchBase): f = f.read() tests = [ - "program:{bench_name}-frappe-web", - "program:{bench_name}-redis-cache", - "program:{bench_name}-redis-queue", - "program:{bench_name}-redis-socketio", - "group:{bench_name}-web", - "group:{bench_name}-workers", - "group:{bench_name}-redis" + f"program:{bench_name}-frappe-web", + f"program:{bench_name}-redis-cache", + f"program:{bench_name}-redis-queue", + f"program:{bench_name}-redis-socketio", + f"group:{bench_name}-web", + f"group:{bench_name}-workers", + f"group:{bench_name}-redis" ] if not os.environ.get("CI"): - tests.append("program:{bench_name}-node-socketio") + tests.append(f"program:{bench_name}-node-socketio") if use_rq: tests.extend([ - "program:{bench_name}-frappe-schedule", - "program:{bench_name}-frappe-default-worker", - "program:{bench_name}-frappe-short-worker", - "program:{bench_name}-frappe-long-worker" + f"program:{bench_name}-frappe-schedule", + f"program:{bench_name}-frappe-default-worker", + f"program:{bench_name}-frappe-short-worker", + f"program:{bench_name}-frappe-long-worker" ]) else: tests.extend([ - "program:{bench_name}-frappe-workerbeat", - "program:{bench_name}-frappe-worker", - "program:{bench_name}-frappe-longjob-worker", - "program:{bench_name}-frappe-async-worker" + f"program:{bench_name}-frappe-workerbeat", + f"program:{bench_name}-frappe-worker", + f"program:{bench_name}-frappe-longjob-worker", + f"program:{bench_name}-frappe-async-worker" ]) for key in tests: - if key.format(bench_name=bench_name) not in f: - print(key.format(bench_name=bench_name)) - self.assertTrue(key.format(bench_name=bench_name) in f) + self.assertTrue(key in f) def assert_supervisor_process(self, bench_name, use_rq=True, disable_production=False): @@ -170,9 +168,9 @@ class TestSetupProduction(TestBenchBase): for key in tests: if disable_production: - self.assertFalse(re.search(key.format(bench_name=bench_name), out)) + self.assertFalse(re.search(key, out)) else: - self.assertTrue(re.search(key.format(bench_name=bench_name), out)) + self.assertTrue(re.search(key, out)) if __name__ == '__main__': diff --git a/bench/utils.py b/bench/utils.py index 93d2bda5..c8c38d6a 100755 --- a/bench/utils.py +++ b/bench/utils.py @@ -97,7 +97,7 @@ def check_latest_version(): local_version = Version(bench.VERSION) if pypi_version > local_version: - log("A newer version of bench is available: {0} → {1}".format(local_version, pypi_version)) + log(f"A newer version of bench is available: {local_version} → {pypi_version}") def get_frappe(bench_path='.'): @@ -134,7 +134,7 @@ def init(path, apps_path=None, no_procfile=False, no_backups=False, from bench.patches import set_all_patches_executed if os.path.exists(path) and not ignore_exist: - log('Path {path} already exists!'.format(path=path)) + log(f'Path {path} already exists!') sys.exit(0) elif not os.path.exists(path): # only create dir if it does not exist @@ -213,9 +213,11 @@ def update(pull=False, apps=None, patch=False, build=False, requirements=False, if version_upgrade[0]: if force: - print("Force flag has been used for a major version change in Frappe and it's apps. \nThis will take significant time to migrate and might break custom apps.") + log("""Force flag has been used for a major version change in Frappe and it's apps. +This will take significant time to migrate and might break custom apps.""", level=3) else: - print("This update will cause a major version change in Frappe/ERPNext from {0} to {1}. \nThis would take significant time to migrate and might break custom apps.".format(*version_upgrade[1:])) + print(f"""This update will cause a major version change in Frappe/ERPNext from {version_upgrade[1]} to {version_upgrade[2]}. +This would take significant time to migrate and might break custom apps.""") click.confirm('Do you want to continue?', abort=True) if not reset and conf.get('shallow_clone'): @@ -288,12 +290,12 @@ def copy_patches_txt(bench_path): def clone_apps_from(bench_path, clone_from, update_app=True): from bench.app import install_app - print('Copying apps from {0}...'.format(clone_from)) + print(f'Copying apps from {clone_from}...') subprocess.check_output(['cp', '-R', os.path.join(clone_from, 'apps'), bench_path]) node_modules_path = os.path.join(clone_from, 'node_modules') if os.path.exists(node_modules_path): - print('Copying node_modules from {0}...'.format(clone_from)) + print(f'Copying node_modules from {clone_from}...') subprocess.check_output(['cp', '-R', node_modules_path, bench_path]) def setup_app(app): @@ -309,7 +311,7 @@ def clone_apps_from(bench_path, clone_from, update_app=True): remote = 'upstream' else: remote = remotes[0] - print('Cleaning up {0}'.format(app)) + print(f'Cleaning up {app}') branch = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], cwd=app_path).strip() subprocess.check_output(['git', 'reset', '--hard'], cwd=app_path) subprocess.check_output(['git', 'pull', '--rebase', remote, branch], cwd=app_path) @@ -325,14 +327,14 @@ def clone_apps_from(bench_path, clone_from, update_app=True): def exec_cmd(cmd, cwd='.'): import shlex - 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) + print(f"{color.silver}$ {cmd}{color.nc}") + cwd_info = f"cd {cwd} && " if cwd != "." else "" + cmd_log = f"{cwd_info}{cmd}" logger.debug(cmd_log) cmd = shlex.split(cmd) 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)) + logger.warning(f"{cmd_log} executed with exit code {return_code}") def which(executable, raise_err=False): @@ -341,9 +343,7 @@ def which(executable, raise_err=False): exec_ = find_executable(executable) if not exec_ and raise_err: - raise ValueError('{executable} not found.'.format( - executable = executable - )) + raise ValueError(f'{executable} not found.') return exec_ @@ -356,7 +356,7 @@ def get_venv_path(): with open(os.devnull, "wb") as devnull: is_venv_installed = not subprocess.call([current_python, "-m", "venv", "--help"], stdout=devnull) if is_venv_installed: - venv = "{} -m venv".format(current_python) + venv = f"{current_python} -m venv" return venv or log("virtualenv cannot be found", level=2) @@ -365,10 +365,10 @@ def setup_env(bench_path='.', python='python3'): py = os.path.join(bench_path, "env", "bin", "python") virtualenv = get_venv_path() - exec_cmd('{} -q env -p {}'.format(virtualenv, python), cwd=bench_path) + exec_cmd(f'{virtualenv} -q env -p {python}', cwd=bench_path) if os.path.exists(frappe): - exec_cmd('{} -m pip install -q -U -e {}'.format(py, frappe), cwd=bench_path) + exec_cmd(f'{py} -m pip install -q -U -e {frappe}', cwd=bench_path) def setup_socketio(bench_path='.'): @@ -387,7 +387,7 @@ def patch_sites(bench_path='.'): def build_assets(bench_path='.', app=None): command = 'bench build' if app: - command += ' --app {}'.format(app) + command += f' --app {app}' exec_cmd(command, cwd=bench_path) @@ -406,8 +406,8 @@ def setup_backups(bench_path='.'): user = get_config(bench_path=bench_dir).get('frappe_user') logfile = os.path.join(bench_dir, 'logs', 'backup.log') system_crontab = CronTab(user=user) - backup_command = "cd {bench_dir} && {bench} --verbose --site all backup".format(bench_dir=bench_dir, bench=sys.argv[0]) - job_command = "{backup_command} >> {logfile} 2>&1".format(backup_command=backup_command, logfile=logfile) + backup_command = f"cd {bench_dir} && {sys.argv[0]} --verbose --site all backup" + job_command = f"{backup_command} >> {logfile} 2>&1" if job_command not in str(system_crontab): job = system_crontab.new(command=job_command, comment="bench auto backups set for every 6 hours") @@ -442,7 +442,7 @@ def setup_sudoers(user): f.write(frappe_sudoers) os.chmod(sudoers_file, 0o440) - log("Sudoers was set up for user {}".format(user), level=1) + log(f"Sudoers was set up for user {user}", level=1) def setup_logging(bench_path='.'): @@ -547,39 +547,38 @@ def restart_supervisor_processes(bench_path='.', web_workers=False): supervisor_status = get_cmd_output('supervisorctl status', cwd=bench_path) supervisor_status = safe_decode(supervisor_status) - if web_workers and '{bench_name}-web:'.format(bench_name=bench_name) in supervisor_status: - group = '{bench_name}-web: '.format(bench_name=bench_name) + if web_workers and f'{bench_name}-web:' in supervisor_status: + group = f'{bench_name}-web:\t' - elif '{bench_name}-workers:'.format(bench_name=bench_name) in supervisor_status: - group = '{bench_name}-workers: {bench_name}-web:'.format(bench_name=bench_name) + elif f'{bench_name}-workers:' in supervisor_status: + group = f'{bench_name}-workers: {bench_name}-web:' # backward compatibility - elif '{bench_name}-processes:'.format(bench_name=bench_name) in supervisor_status: - group = '{bench_name}-processes:'.format(bench_name=bench_name) + elif f'{bench_name}-processes:' in supervisor_status: + group = f'{bench_name}-processes:' # backward compatibility else: group = 'frappe:' - exec_cmd('supervisorctl restart {group}'.format(group=group), cwd=bench_path) + exec_cmd(f'supervisorctl restart {group}', cwd=bench_path) def restart_systemd_processes(bench_path='.', web_workers=False): bench_name = get_bench_name(bench_path) - exec_cmd('sudo systemctl stop -- $(systemctl show -p Requires {bench_name}.target | cut -d= -f2)'.format(bench_name=bench_name)) - exec_cmd('sudo systemctl start -- $(systemctl show -p Requires {bench_name}.target | cut -d= -f2)'.format(bench_name=bench_name)) + exec_cmd(f'sudo systemctl stop -- $(systemctl show -p Requires {bench_name}.target | cut -d= -f2)') + exec_cmd(f'sudo systemctl start -- $(systemctl show -p Requires {bench_name}.target | cut -d= -f2)') def set_default_site(site, bench_path='.'): if site not in get_sites(bench_path=bench_path): raise Exception("Site not in bench") - exec_cmd("{frappe} --use {site}".format(frappe=get_frappe(bench_path=bench_path), site=site), - cwd=os.path.join(bench_path, 'sites')) + exec_cmd(f"{get_frappe(bench_path)} --use {site}", cwd=os.path.join(bench_path, 'sites')) def update_env_pip(bench_path): env_py = os.path.join(bench_path, 'env', 'bin', 'python') - exec_cmd("{env_py} -m pip install -q -U pip".format(env_py=env_py)) + exec_cmd(f"{env_py} -m pip install -q -U pip") def update_requirements(bench_path='.'): @@ -599,9 +598,9 @@ def update_python_packages(bench_path='.'): 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)) + print(f'\n{color.yellow}Installing python dependencies for {app}{color.nc}') app_path = os.path.join(bench_path, "apps", app) - exec_cmd("{0} -m pip install -q -U -e {1}".format(env_py, app_path), cwd=bench_path) + exec_cmd(f"{env_py} -m pip install -q -U -e {app_path}", cwd=bench_path) def update_node_packages(bench_path='.'): @@ -629,7 +628,7 @@ def update_yarn_packages(bench_path='.'): for app in os.listdir(apps_dir): app_path = os.path.join(apps_dir, app) if os.path.exists(os.path.join(app_path, 'package.json')): - print('\n{0}Installing node dependencies for {1}{2}'.format(color.yellow, app, color.nc)) + print(f'\n{color.yellow}Installing node dependencies for {app}{color.nc}') exec_cmd('yarn install', cwd=app_path) @@ -689,15 +688,15 @@ def set_mariadb_host(host, bench_path='.'): def set_redis_cache_host(host, bench_path='.'): - update_common_site_config({'redis_cache': "redis://{}".format(host)}, bench_path=bench_path) + update_common_site_config({'redis_cache': f"redis://{host}"}, bench_path=bench_path) def set_redis_queue_host(host, bench_path='.'): - update_common_site_config({'redis_queue': "redis://{}".format(host)}, bench_path=bench_path) + update_common_site_config({'redis_queue': f"redis://{host}"}, bench_path=bench_path) def set_redis_socketio_host(host, bench_path='.'): - update_common_site_config({'redis_socketio': "redis://{}".format(host)}, bench_path=bench_path) + update_common_site_config({'redis_socketio': f"redis://{host}"}, bench_path=bench_path) def update_common_site_config(ddict, bench_path='.'): @@ -794,7 +793,7 @@ def post_upgrade(from_ver, to_ver, bench_path='.'): from bench.config.supervisor import generate_supervisor_config from bench.config.nginx import make_nginx_conf conf = get_config(bench_path=bench_path) - print("-" * 80 + "Your bench was upgraded to version {0}".format(to_ver)) + print("-" * 80 + f"Your bench was upgraded to version {to_ver}") if conf.get('restart_supervisor_on_update'): redis.generate_config(bench_path=bench_path) @@ -855,7 +854,7 @@ def update_translations(app, lang): translations_dir = os.path.join('apps', app, app, 'translations') csv_file = os.path.join(translations_dir, lang + '.csv') - url = "https://translate.erpnext.com/files/{}-{}.csv".format(app, lang) + url = f"https://translate.erpnext.com/files/{app}-{lang}.csv" r = requests.get(url, stream=True) r.raise_for_status() @@ -925,12 +924,12 @@ def set_git_remote_url(git_url, bench_path='.'): app = git_url.rsplit('/', 1)[1].rsplit('.', 1)[0] if app not in bench.app.get_apps(bench_path): - print("No app named {0}".format(app)) + print(f"No app named {app}") sys.exit(1) app_dir = bench.app.get_repo_dir(app, bench_path=bench_path) if os.path.exists(os.path.join(app_dir, '.git')): - exec_cmd("git remote set-url upstream {}".format(git_url), cwd=app_dir) + exec_cmd(f"git remote set-url upstream {git_url}", cwd=app_dir) def run_playbook(playbook_name, extra_vars=None, tag=None): @@ -961,7 +960,7 @@ def find_benches(directory=None): if os.path.curdir == directory: print("You are in a bench directory!") else: - print("{0} is a bench directory!".format(directory)) + print(f"{directory} is a bench directory!") return benches = [] @@ -969,7 +968,7 @@ def find_benches(directory=None): sub = os.path.join(directory, sub) if os.path.isdir(sub) and not os.path.islink(sub): if is_bench_directory(sub): - print("{} found!".format(sub)) + print(f"{sub} found!") benches.append(sub) else: benches.extend(find_benches(sub)) @@ -993,13 +992,12 @@ def migrate_env(python, backup=False): try: config = get_config(bench_path=os.getcwd()) rredis = urlparse(config['redis_cache']) - - redis = '{redis} -p {port}'.format(redis=which('redis-cli'), port=rredis.port) + redis = f"{which('redis-cli')} -p {rredis.port}" logger.log('Clearing Redis Cache...') - exec_cmd('{redis} FLUSHALL'.format(redis = redis)) + exec_cmd(f'{redis} FLUSHALL') logger.log('Clearing Redis DataBase...') - exec_cmd('{redis} FLUSHDB'.format(redis = redis)) + exec_cmd(f'{redis} FLUSHDB') except: logger.warning('Please ensure Redis Connections are running or Daemonized.') @@ -1024,13 +1022,13 @@ def migrate_env(python, backup=False): # Create virtualenv using specified python venv_creation, packages_setup = 1, 1 try: - logger.log('Setting up a New Virtual {} Environment'.format(python)) - venv_creation = exec_cmd('{virtualenv} --python {python} {pvenv}'.format(virtualenv=virtualenv, python=python, pvenv=pvenv)) + logger.log(f'Setting up a New Virtual {python} Environment') + venv_creation = exec_cmd(f'{virtualenv} --python {python} {pvenv}') - apps = ' '.join(["-e {}".format(os.path.join("apps", app)) for app in get_apps()]) - packages_setup = exec_cmd('{0} -m pip install -q -U {1}'.format(pvenv, apps)) + apps = ' '.join([f"-e {os.path.join('apps', app)}" for app in get_apps()]) + packages_setup = exec_cmd(f'{pvenv} -m pip install -q -U {apps}') - logger.log('Migration Successful to {}'.format(python)) + logger.log(f'Migration Successful to {python}') except: if venv_creation or packages_setup: logger.warning('Migration Error') @@ -1071,7 +1069,7 @@ def generate_command_cache(bench_path='.'): os.remove(bench_cache_file) try: - output = get_cmd_output("{0} -m frappe.utils.bench_helper get-frappe-commands".format(python), cwd=sites_path) + output = get_cmd_output(f"{python} -m frappe.utils.bench_helper get-frappe-commands", cwd=sites_path) with open(bench_cache_file, 'w') as f: json.dump(eval(output), f) return json.loads(output) From 54d48f61a339718d173c52138c0652473fde1140 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 11 May 2021 11:55:46 +0530 Subject: [PATCH 10/13] fix: Remove PY2 compatibility code * Set Python requires Python 3.6+ but < Py4 * Remove six from dependencies * Use click.confirm instead of self implemented confirm code * Fix imports for 3.6+ compatibility References for updated imports: * https://docs.python.org/3/library/configparser.html * https://docs.python.org/3/library/urllib.request.html#legacy-interface * https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse * https://docs.python.org/3/library/importlib.html#importlib.reload --- bench/app.py | 8 +++----- bench/commands/make.py | 4 ++-- bench/commands/setup.py | 7 +------ bench/config/common_site_config.py | 2 +- bench/config/lets_encrypt.py | 4 ++-- bench/config/nginx.py | 3 +-- bench/config/redis.py | 2 +- bench/config/supervisor.py | 3 +-- bench/tests/test_base.py | 5 +---- bench/utils.py | 6 ++---- install.py | 3 +-- requirements.txt | 1 - setup.py | 1 + 13 files changed, 17 insertions(+), 32 deletions(-) diff --git a/bench/app.py b/bench/app.py index f1d05af9..76e03b73 100755 --- a/bench/app.py +++ b/bench/app.py @@ -1,6 +1,3 @@ -# imports - compatibility imports -from __future__ import print_function - # imports - standard imports import json import logging @@ -390,8 +387,9 @@ def get_repo_dir(app, bench_path='.'): def switch_branch(branch, apps=None, bench_path='.', upgrade=False, check_upgrade=True): import git - from six.moves import reload_module + import importlib from bench.utils import update_requirements, update_node_packages, backup_all_sites, patch_sites, build_assets, post_upgrade + apps_dir = os.path.join(bench_path, 'apps') version_upgrade = (False,) switched_apps = [] @@ -437,7 +435,7 @@ def switch_branch(branch, apps=None, bench_path='.', upgrade=False, check_upgrad if version_upgrade[0] and upgrade: update_requirements() update_node_packages() - reload_module(bench.utils) + importlib.reload(bench.utils) backup_all_sites() patch_sites() build_assets() diff --git a/bench/commands/make.py b/bench/commands/make.py index 80061e9c..20c93659 100755 --- a/bench/commands/make.py +++ b/bench/commands/make.py @@ -39,12 +39,12 @@ def init(path, apps_path, frappe_path, frappe_branch, no_procfile, no_backups, c except SystemExit: pass except Exception as e: - import os, shutil, time, six + import os, shutil, time # add a sleep here so that the traceback of other processes doesnt overlap with the prompts time.sleep(1) print(e) log(f"There was a problem while creating {path}", level=2) - if six.moves.input("Do you want to rollback these changes? [Y/n]: ").lower() == "y": + if click.confirm("Do you want to rollback these changes?"): print(f'Rolling back Bench "{path}"') if os.path.exists(path): shutil.rmtree(path) diff --git a/bench/commands/setup.py b/bench/commands/setup.py index b1a6be0e..51f87030 100755 --- a/bench/commands/setup.py +++ b/bench/commands/setup.py @@ -154,7 +154,6 @@ def setup_requirements(node=False, python=False): @click.option("--port", help="Port on which you want to run bench manager", default=23624) @click.option("--domain", help="Domain on which you want to run bench manager") def setup_manager(yes=False, port=23624, domain=None): - from six.moves import input from bench.utils import get_sites from bench.config.common_site_config import get_config from bench.config.nginx import make_bench_manager_nginx_conf @@ -162,11 +161,7 @@ def setup_manager(yes=False, port=23624, domain=None): create_new_site = True if "bench-manager.local" in os.listdir("sites"): - ans = input("Site already exists. Overwrite existing site? [Y/n]: ").lower() - while ans not in ("y", "n", ""): - ans = input("Please enter 'y' or 'n'. Site already exists. Overwrite existing site? [Y/n]: ").lower() - if ans == "n": - create_new_site = False + create_new_site = click.confirm("Site already exists. Overwrite existing site?") if create_new_site: exec_cmd("bench new-site --force bench-manager.local") diff --git a/bench/config/common_site_config.py b/bench/config/common_site_config.py index d787824a..6c621080 100644 --- a/bench/config/common_site_config.py +++ b/bench/config/common_site_config.py @@ -71,7 +71,7 @@ def update_config_for_frappe(config, bench_path): # TODO Optionally we need to add the host or domain name in case dns_multitenant is false def make_ports(bench_path): - from six.moves.urllib.parse import urlparse + from urllib.parse import urlparse benches_path = os.path.dirname(os.path.abspath(bench_path)) diff --git a/bench/config/lets_encrypt.py b/bench/config/lets_encrypt.py index 59d29f7d..38d52921 100755 --- a/bench/config/lets_encrypt.py +++ b/bench/config/lets_encrypt.py @@ -106,13 +106,13 @@ def create_dir_if_missing(path): def get_certbot(): - from six.moves.urllib.request import urlretrieve + from urllib.request import urlretrieve certbot_path = get_certbot_path() create_dir_if_missing(certbot_path) if not os.path.isfile(certbot_path): - urlretrieve ("https://dl.eff.org/certbot-auto", certbot_path) + urlretrieve("https://dl.eff.org/certbot-auto", certbot_path) os.chmod(certbot_path, 0o744) diff --git a/bench/config/nginx.py b/bench/config/nginx.py index 2210a46d..074dd56e 100644 --- a/bench/config/nginx.py +++ b/bench/config/nginx.py @@ -6,7 +6,6 @@ import string # imports - third party imports import click -from six import string_types # imports - module imports import bench @@ -218,7 +217,7 @@ def get_sites_with_config(bench_path): if dns_multitenant and site_config.get('domains'): for domain in site_config.get('domains'): # domain can be a string or a dict with 'domain', 'ssl_certificate', 'ssl_certificate_key' - if isinstance(domain, string_types): + if isinstance(domain, str): domain = { 'domain': domain } domain['name'] = site diff --git a/bench/config/redis.py b/bench/config/redis.py index 16226099..753a4251 100644 --- a/bench/config/redis.py +++ b/bench/config/redis.py @@ -9,7 +9,7 @@ from bench.config.common_site_config import get_config def generate_config(bench_path): - from six.moves.urllib.parse import urlparse + from urllib.parse import urlparse config = get_config(bench_path) diff --git a/bench/config/supervisor.py b/bench/config/supervisor.py index 1658731a..8c036624 100644 --- a/bench/config/supervisor.py +++ b/bench/config/supervisor.py @@ -67,8 +67,7 @@ def get_supervisord_conf(): def update_supervisord_config(user=None, yes=False): """From bench v5.x, we're moving to supervisor running as user""" - from six.moves import configparser - + import configparser from bench.config.production_setup import service if not user: diff --git a/bench/tests/test_base.py b/bench/tests/test_base.py index bb948dfc..298d081c 100644 --- a/bench/tests/test_base.py +++ b/bench/tests/test_base.py @@ -8,14 +8,11 @@ import sys import traceback import unittest -# imports - third party imports -from six import PY2 - # imports - module imports import bench import bench.utils -if PY2: +if sys.version_info.major == 2: FRAPPE_BRANCH = "version-12" else: FRAPPE_BRANCH = "develop" diff --git a/bench/utils.py b/bench/utils.py index c8c38d6a..39d0ca56 100755 --- a/bench/utils.py +++ b/bench/utils.py @@ -641,11 +641,9 @@ def update_npm_packages(bench_path='.'): if os.path.exists(package_json_path): with open(package_json_path, "r") as f: - from six import iteritems - app_package_json = json.loads(f.read()) # package.json is usually a dict in a dict - for key, value in iteritems(app_package_json): + for key, value in app_package_json.items(): if not key in package_json: package_json[key] = value else: @@ -978,7 +976,7 @@ def find_benches(directory=None): def migrate_env(python, backup=False): import shutil - from six.moves.urllib.parse import urlparse + from urllib.parse import urlparse from bench.config.common_site_config import get_config from bench.app import get_apps diff --git a/install.py b/install.py index 44d53892..f9e1c3dc 100644 --- a/install.py +++ b/install.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -from __future__ import print_function import os import sys import subprocess @@ -234,7 +233,7 @@ def install_bench(args): # create user if not exists extra_vars = vars(args) extra_vars.update(frappe_user=args.user) - + extra_vars.update(user_directory=get_user_home_directory(args.user)) if os.path.exists(tmp_bench_repo): diff --git a/requirements.txt b/requirements.txt index 92df9331..66c2c947 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,5 +6,4 @@ python-crontab==2.4.0 requests==2.22.0 semantic-version==2.8.2 setuptools -six virtualenv diff --git a/setup.py b/setup.py index 055f0422..be99d6c2 100644 --- a/setup.py +++ b/setup.py @@ -11,6 +11,7 @@ setup( author_email='info@frappe.io', version=VERSION, packages=find_packages(), + python_requires='~=3.6', zip_safe=False, include_package_data=True, install_requires=install_requires, From 72e6795f503e1687ec4cff9c61092092d1263194 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 11 May 2021 19:25:10 +0530 Subject: [PATCH 11/13] chore: Drop Circle CI config --- .circleci/config.yml | 32 ------------------------ install_scripts/contrib-apps.json | 24 ------------------ install_scripts/erpnext-apps-master.json | 7 ------ install_scripts/erpnext-apps.json | 6 ----- 4 files changed, 69 deletions(-) delete mode 100644 .circleci/config.yml delete mode 100644 install_scripts/contrib-apps.json delete mode 100644 install_scripts/erpnext-apps-master.json delete mode 100644 install_scripts/erpnext-apps.json diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index c067e4ef..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: 2 -jobs: - build: - machine: true - working_directory: ~/repo - steps: - - checkout - - run: - name: Setup - command: | - sudo pip install --ignore-installed setuptools - sudo pip install urllib3 pyOpenSSL ndg-httpsclient pyasn1 - sudo cp -r ~/.ssh/* /root/.ssh - mkdir -p ~/.bench - mkdir -p /tmp/.bench - cp -r ~/repo/* ~/.bench - cp -r ~/repo/* /tmp/.bench - - - run: - name: Install Bench (Production) - command: sudo python ~/repo/playbooks/install.py --user travis --run-travis --production - - - run: - name: Setup Tests - command: | - cd ~ - sudo pip install --upgrade pip - sudo pip install -e ~/.bench - - - run: - name: Run Tests - command: sudo -E python -m unittest -v bench.tests.test_setup_production diff --git a/install_scripts/contrib-apps.json b/install_scripts/contrib-apps.json deleted file mode 100644 index 192f4cbe..00000000 --- a/install_scripts/contrib-apps.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "jasper_erpnext_report": { - "app_url": "http://localhost", - "app_name": "jasper_erpnext_report", - "app_icon": "icon-file-text", - "app_color": "black", - "app_description": "Make your own reports in jasper and print them in pdf, docx, xlsx and other formats.", - "app_publisher": "Luis Fernandes", - "repo_url": "https://github.com/saguas/jasper_erpnext_report.git", - "app_title": "Jasper Erpnext Report", - "app_version": "0.1.0" - }, - "base_vat": { - "app_url": "http://localhost", - "app_name": "base_vat", - "app_icon": "icon-credit-card", - "app_color": "#C0C0C0", - "app_description": "Check the VAT number depending of the country.", - "app_publisher": "Luis Fernandes", - "repo_url": "https://github.com/saguas/frappe_base_vat.git", - "app_title": "Base VAT", - "app_version": "0.0.1" - } -} diff --git a/install_scripts/erpnext-apps-master.json b/install_scripts/erpnext-apps-master.json deleted file mode 100644 index c54e609e..00000000 --- a/install_scripts/erpnext-apps-master.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "url":"https://github.com/frappe/erpnext", - "name":"erpnext", - "branch": "master" - } -] diff --git a/install_scripts/erpnext-apps.json b/install_scripts/erpnext-apps.json deleted file mode 100644 index 73c70527..00000000 --- a/install_scripts/erpnext-apps.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "url":"https://github.com/frappe/erpnext", - "name":"erpnext" - } -] From 753e36d433b57ae742b2e02ec75d9615b0ebb358 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 11 May 2021 19:57:24 +0530 Subject: [PATCH 12/13] fix: Revert print compatibility import --- install.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/install.py b/install.py index f9e1c3dc..d42da5b8 100644 --- a/install.py +++ b/install.py @@ -1,4 +1,7 @@ #!/usr/bin/env python3 + +from __future__ import print_function + import os import sys import subprocess From 973b8010be1f06f820bd070e3acf2be286a99ec9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 13 May 2021 15:45:43 +0530 Subject: [PATCH 13/13] chore: Bumped to Version 5.4.0 To match with current version on v5.x branch --- bench/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench/__init__.py b/bench/__init__.py index 2135ad5b..54408217 100644 --- a/bench/__init__.py +++ b/bench/__init__.py @@ -1,4 +1,4 @@ -VERSION = "5.3.0" +VERSION = "5.4.0" PROJECT_NAME = "frappe-bench" FRAPPE_VERSION = None