From e218a7aa09eb5c0160d086d7a86cb752c939e042 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 2 Apr 2020 15:06:24 +0530 Subject: [PATCH 1/7] chore: renamed project to match with PyPI --- bench/__init__.py | 7 ++++--- bench/app.py | 2 +- bench/cli.py | 23 +++++++++++++++++------ bench/commands/__init__.py | 1 - bench/utils.py | 2 +- setup.py | 11 +++-------- 6 files changed, 26 insertions(+), 20 deletions(-) diff --git a/bench/__init__.py b/bench/__init__.py index e120c37b..8f1ef651 100644 --- a/bench/__init__.py +++ b/bench/__init__.py @@ -1,10 +1,11 @@ from jinja2 import Environment, PackageLoader -__version__ = "4.1.0" - +VERSION = "5.0.0" +PROJECT_NAME = "frappe-bench" +FRAPPE_VERSION = None +__version__ = VERSION env = Environment(loader=PackageLoader('bench.config')) -FRAPPE_VERSION = None def set_frappe_version(bench_path='.'): from .app import get_current_frappe_version diff --git a/bench/app.py b/bench/app.py index a8c71b52..db3aff26 100755 --- a/bench/app.py +++ b/bench/app.py @@ -406,7 +406,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(utils) + reload_module(bench.utils) backup_all_sites() patch_sites() build_assets() diff --git a/bench/cli.py b/bench/cli.py index f5891d0a..d238f193 100755 --- a/bench/cli.py +++ b/bench/cli.py @@ -1,15 +1,27 @@ +# imports - standard imports +import json +import logging +import os +import pwd +import subprocess +import sys + +# imports - third party imports import click -import os, sys, logging, json, pwd, subprocess -from bench.utils import is_root, PatchError, drop_privileges, get_env_cmd, get_cmd_output, get_frappe, log, is_dist_editable, find_parent_bench + +# imports - module imports +import bench from bench.app import get_apps -from bench.config.common_site_config import get_config from bench.commands import bench_command +from bench.config.common_site_config import get_config +from bench.utils import PatchError, drop_privileges, find_parent_bench, get_cmd_output, get_env_cmd, get_frappe, is_dist_editable, is_root, log -logger = logging.getLogger('bench') +logger = logging.getLogger(bench.PROJECT_NAME) from_command_line = False change_uid_msg = "You should not run this command as root" + def cli(): global from_command_line from_command_line = True @@ -19,7 +31,7 @@ def cli(): change_dir() change_uid() - if is_dist_editable("bench"): + if is_dist_editable(bench.PROJECT_NAME): log("bench is installed in editable mode!\n\nThis is not the recommended mode of installation for production. Instead, install the package from PyPI with: `pip install frappe-bench`\n", level=3) if len(sys.argv) > 2 and sys.argv[1] == "frappe": @@ -41,7 +53,6 @@ def cli(): else: try: - # NOTE: this is the main bench command bench_command() except PatchError: sys.exit(1) diff --git a/bench/commands/__init__.py b/bench/commands/__init__.py index 090013c2..59806be7 100755 --- a/bench/commands/__init__.py +++ b/bench/commands/__init__.py @@ -13,7 +13,6 @@ def print_bench_version(ctx, param, value): @click.group() @click.option('--version', is_flag=True, is_eager=True, callback=print_bench_version, expose_value=False) def bench_command(bench_path='.'): - """Bench manager for Frappe""" import bench from bench.utils import setup_logging diff --git a/bench/utils.py b/bench/utils.py index 6fb985fc..1e4faf33 100755 --- a/bench/utils.py +++ b/bench/utils.py @@ -412,7 +412,7 @@ def setup_sudoers(user): def setup_logging(bench_path='.'): if os.path.exists(os.path.join(bench_path, 'logs')): - logger = logging.getLogger('bench') + logger = logging.getLogger(bench.PROJECT_NAME) log_file = os.path.join(bench_path, 'logs', 'bench.log') formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') hdlr = logging.FileHandler(log_file) diff --git a/setup.py b/setup.py index 6cbeddbc..8bba0fa4 100644 --- a/setup.py +++ b/setup.py @@ -1,22 +1,17 @@ from setuptools import setup, find_packages import re, ast -# get version from __version__ variable in bench/__init__.py -_version_re = re.compile(r'__version__\s+=\s+(.*)') +from bench import PROJECT_NAME, VERSION with open('requirements.txt') as f: install_requires = f.read().strip().split('\n') -with open('bench/__init__.py', 'rb') as f: - version = str(ast.literal_eval(_version_re.search( - f.read().decode('utf-8')).group(1))) - setup( - name='bench', + name=PROJECT_NAME, description='Metadata driven, full-stack web framework', author='Frappe Technologies', author_email='info@frappe.io', - version=version, + version=VERSION, packages=find_packages(), zip_safe=False, include_package_data=True, From 1f4994d4347a92ee7cbfb7a0b9015f4837d7f1a5 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 2 Apr 2020 17:35:12 +0530 Subject: [PATCH 2/7] fix: move bench.env to bench.config.env changed namespace to allow usage of __init__ file without checking for jinja2 dependencies --- bench/__init__.py | 3 --- bench/config/__init__.py | 2 ++ bench/config/lets_encrypt.py | 2 +- bench/config/nginx.py | 15 ++++----------- bench/config/procfile.py | 2 +- bench/config/redis.py | 2 +- bench/config/supervisor.py | 2 +- bench/config/systemd.py | 26 +++++++++++++------------- bench/utils.py | 4 +--- 9 files changed, 24 insertions(+), 34 deletions(-) diff --git a/bench/__init__.py b/bench/__init__.py index 8f1ef651..12060392 100644 --- a/bench/__init__.py +++ b/bench/__init__.py @@ -1,10 +1,7 @@ -from jinja2 import Environment, PackageLoader - VERSION = "5.0.0" PROJECT_NAME = "frappe-bench" FRAPPE_VERSION = None __version__ = VERSION -env = Environment(loader=PackageLoader('bench.config')) def set_frappe_version(bench_path='.'): diff --git a/bench/config/__init__.py b/bench/config/__init__.py index e69de29b..5b6b5bcb 100644 --- a/bench/config/__init__.py +++ b/bench/config/__init__.py @@ -0,0 +1,2 @@ +from jinja2 import Environment, PackageLoader +env = Environment(loader=PackageLoader('bench.config')) diff --git a/bench/config/lets_encrypt.py b/bench/config/lets_encrypt.py index 4beac157..17f35dd0 100755 --- a/bench/config/lets_encrypt.py +++ b/bench/config/lets_encrypt.py @@ -44,7 +44,7 @@ def setup_letsencrypt(site, custom_domain, bench_path, interactive): def create_config(site, custom_domain): - config = bench.env.get_template('letsencrypt.cfg').render(domain=custom_domain or site) + 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) create_dir_if_missing(config_path) diff --git a/bench/config/nginx.py b/bench/config/nginx.py index 9f10c6f7..d2154112 100644 --- a/bench/config/nginx.py +++ b/bench/config/nginx.py @@ -9,6 +9,7 @@ import click from six import string_types # imports - module imports +import bench from bench.utils import get_bench_name, get_sites @@ -19,14 +20,11 @@ def make_nginx_conf(bench_path, yes=False): if not click.confirm('nginx.conf already exists and this will overwrite it. Do you want to continue?'): return - from bench import env - from bench.config.common_site_config import get_config - - template = env.get_template('nginx.conf') + template = bench.config.env.get_template('nginx.conf') bench_path = os.path.abspath(bench_path) sites_path = os.path.join(bench_path, "sites") - config = get_config(bench_path) + config = bench.config.common_site_config.get_config(bench_path) sites = prepare_sites(config, bench_path) bench_name = get_bench_name(bench_path) @@ -58,17 +56,15 @@ def make_nginx_conf(bench_path, yes=False): f.write(nginx_conf) def make_bench_manager_nginx_conf(bench_path, yes=False, port=23624, domain=None): - from bench import env from bench.config.site_config import get_site_config from bench.config.common_site_config import get_config - template = env.get_template('bench_manager_nginx.conf') + template = bench.config.env.get_template('bench_manager_nginx.conf') bench_path = os.path.abspath(bench_path) sites_path = os.path.join(bench_path, "sites") config = get_config(bench_path) site_config = get_site_config(domain, bench_path=bench_path) - sites = prepare_sites(config, bench_path) bench_name = get_bench_name(bench_path) template_vars = { @@ -153,9 +149,6 @@ def prepare_sites(config, bench_path): while site["port"] in ports_in_use: site["port"] += 1 -# if site["port"] in ports_in_use: -# raise Exception("Port {0} is being used by another site {1}".format(site["port"], ports_in_use[site["port"]])) - if site["port"] in ports_in_use and not site["name"] in ports_in_use[site["port"]]: shared_port_exception_found = True ports_in_use[site["port"]].append(site["name"]) diff --git a/bench/config/procfile.py b/bench/config/procfile.py index a6982c83..cf3899b6 100755 --- a/bench/config/procfile.py +++ b/bench/config/procfile.py @@ -10,7 +10,7 @@ def setup_procfile(bench_path, yes=False, skip_redis=False): click.confirm('A Procfile already exists and this will overwrite it. Do you want to continue?', abort=True) - procfile = bench.env.get_template('Procfile').render( + procfile = bench.config.env.get_template('Procfile').render( node=find_executable("node") or find_executable("nodejs"), use_rq=use_rq(bench_path), webserver_port=config.get('webserver_port'), diff --git a/bench/config/redis.py b/bench/config/redis.py index ae9b5b0f..42005b07 100644 --- a/bench/config/redis.py +++ b/bench/config/redis.py @@ -52,7 +52,7 @@ def generate_config(bench_path): os.makedirs(pid_path) def write_redis_config(template_name, context, bench_path): - template = bench.env.get_template(template_name) + template = bench.config.env.get_template(template_name) if "pid_path" not in context: context["pid_path"] = os.path.abspath(os.path.join(bench_path, "config", "pids")) diff --git a/bench/config/supervisor.py b/bench/config/supervisor.py index 43560cf0..9e9b7a89 100644 --- a/bench/config/supervisor.py +++ b/bench/config/supervisor.py @@ -20,7 +20,7 @@ def generate_supervisor_config(bench_path, user=None, yes=False): update_supervisord_conf(user=user) - template = bench.env.get_template('supervisor.conf') + template = bench.config.env.get_template('supervisor.conf') config = get_config(bench_path=bench_path) bench_dir = os.path.abspath(bench_path) diff --git a/bench/config/systemd.py b/bench/config/systemd.py index 72bed1f2..dbf28c06 100644 --- a/bench/config/systemd.py +++ b/bench/config/systemd.py @@ -78,7 +78,7 @@ def setup_systemd_directory(bench_path): def setup_main_config(bench_info, bench_path): # Main config - bench_template = bench.env.get_template('systemd/frappe-bench.target') + bench_template = bench.config.env.get_template('systemd/frappe-bench.target') bench_config = bench_template.render(**bench_info) bench_config_path = os.path.join(bench_path, 'config', 'systemd' , bench_info.get("bench_name") + '.target') @@ -87,11 +87,11 @@ def setup_main_config(bench_info, bench_path): def setup_workers_config(bench_info, bench_path): # Worker Group - bench_workers_target_template = bench.env.get_template('systemd/frappe-bench-workers.target') - bench_default_worker_template = bench.env.get_template('systemd/frappe-bench-frappe-default-worker.service') - bench_short_worker_template = bench.env.get_template('systemd/frappe-bench-frappe-short-worker.service') - bench_long_worker_template = bench.env.get_template('systemd/frappe-bench-frappe-long-worker.service') - bench_schedule_worker_template = bench.env.get_template('systemd/frappe-bench-frappe-schedule.service') + bench_workers_target_template = bench.config.env.get_template('systemd/frappe-bench-workers.target') + bench_default_worker_template = bench.config.env.get_template('systemd/frappe-bench-frappe-default-worker.service') + bench_short_worker_template = bench.config.env.get_template('systemd/frappe-bench-frappe-short-worker.service') + bench_long_worker_template = bench.config.env.get_template('systemd/frappe-bench-frappe-long-worker.service') + bench_schedule_worker_template = bench.config.env.get_template('systemd/frappe-bench-frappe-schedule.service') bench_workers_target_config = bench_workers_target_template.render(**bench_info) bench_default_worker_config = bench_default_worker_template.render(**bench_info) @@ -122,9 +122,9 @@ def setup_workers_config(bench_info, bench_path): def setup_web_config(bench_info, bench_path): # Web Group - bench_web_target_template = bench.env.get_template('systemd/frappe-bench-web.target') - bench_web_service_template = bench.env.get_template('systemd/frappe-bench-frappe-web.service') - bench_node_socketio_template = bench.env.get_template('systemd/frappe-bench-node-socketio.service') + bench_web_target_template = bench.config.env.get_template('systemd/frappe-bench-web.target') + bench_web_service_template = bench.config.env.get_template('systemd/frappe-bench-frappe-web.service') + bench_node_socketio_template = bench.config.env.get_template('systemd/frappe-bench-node-socketio.service') bench_web_target_config = bench_web_target_template.render(**bench_info) bench_web_service_config = bench_web_service_template.render(**bench_info) @@ -145,10 +145,10 @@ def setup_web_config(bench_info, bench_path): def setup_redis_config(bench_info, bench_path): # Redis Group - bench_redis_target_template = bench.env.get_template('systemd/frappe-bench-redis.target') - bench_redis_cache_template = bench.env.get_template('systemd/frappe-bench-redis-cache.service') - bench_redis_queue_template = bench.env.get_template('systemd/frappe-bench-redis-queue.service') - bench_redis_socketio_template = bench.env.get_template('systemd/frappe-bench-redis-socketio.service') + bench_redis_target_template = bench.config.env.get_template('systemd/frappe-bench-redis.target') + bench_redis_cache_template = bench.config.env.get_template('systemd/frappe-bench-redis-cache.service') + bench_redis_queue_template = bench.config.env.get_template('systemd/frappe-bench-redis-queue.service') + bench_redis_socketio_template = bench.config.env.get_template('systemd/frappe-bench-redis-socketio.service') bench_redis_target_config = bench_redis_target_template.render(**bench_info) bench_redis_cache_config = bench_redis_cache_template.render(**bench_info) diff --git a/bench/utils.py b/bench/utils.py index 1e4faf33..750f6a71 100755 --- a/bench/utils.py +++ b/bench/utils.py @@ -379,8 +379,6 @@ def read_crontab(): def setup_sudoers(user): - from bench import env - if not os.path.exists('/etc/sudoers.d'): os.makedirs('/etc/sudoers.d') @@ -394,7 +392,7 @@ def setup_sudoers(user): if set_permissions: os.chmod('/etc/sudoers', 0o440) - template = env.get_template('frappe_sudoers') + template = bench.config.env.get_template('frappe_sudoers') frappe_sudoers = template.render(**{ 'user': user, 'service': find_executable('service'), From 20152f2dce2bbe0f8e0a7c7f682ca1f78e104653 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 2 Apr 2020 22:24:21 +0530 Subject: [PATCH 3/7] style: sort and optimize imports --- bench/commands/setup.py | 1 - bench/config/__init__.py | 2 ++ bench/config/lets_encrypt.py | 24 ++++++++++++++---------- bench/config/procfile.py | 12 ++++++++++-- bench/config/production_setup.py | 3 +-- bench/config/site_config.py | 11 ++++++++--- bench/config/systemd.py | 15 +++++++++++---- 7 files changed, 46 insertions(+), 22 deletions(-) diff --git a/bench/commands/setup.py b/bench/commands/setup.py index 96360bea..2e516496 100755 --- a/bench/commands/setup.py +++ b/bench/commands/setup.py @@ -14,7 +14,6 @@ import bench.config.production_setup import bench.config.redis import bench.config.site_config import bench.config.supervisor - import bench.utils from bench.utils import exec_cmd, run_playbook diff --git a/bench/config/__init__.py b/bench/config/__init__.py index 5b6b5bcb..7dd76eb3 100644 --- a/bench/config/__init__.py +++ b/bench/config/__init__.py @@ -1,2 +1,4 @@ +# imports - third party imports from jinja2 import Environment, PackageLoader + env = Environment(loader=PackageLoader('bench.config')) diff --git a/bench/config/lets_encrypt.py b/bench/config/lets_encrypt.py index 17f35dd0..280554ab 100755 --- a/bench/config/lets_encrypt.py +++ b/bench/config/lets_encrypt.py @@ -1,15 +1,20 @@ -import bench, os, click, errno -from bench.utils import exec_cmd, CommandFailedError, update_common_site_config -from bench.config.site_config import update_site_config, remove_domain, get_domains +# imports - standard imports +import errno +import os + +# imports - third party imports +import click +from crontab import CronTab +from six.moves.urllib.request import urlretrieve + +# imports - module imports +import bench +from bench.config.common_site_config import get_config from bench.config.nginx import make_nginx_conf from bench.config.production_setup import service -from bench.config.common_site_config import get_config -from crontab import CronTab +from bench.config.site_config import get_domains, remove_domain, update_site_config +from bench.utils import CommandFailedError, exec_cmd, update_common_site_config -try: - from urllib.request import urlretrieve -except ImportError: - from urllib import urlretrieve def setup_letsencrypt(site, custom_domain, bench_path, interactive): @@ -171,4 +176,3 @@ def setup_wildcard_ssl(domain, email, bench_path, exclude_base_domain): make_nginx_conf(bench_path) print("Restrting Nginx service") service('nginx', 'restart') - diff --git a/bench/config/procfile.py b/bench/config/procfile.py index cf3899b6..1b3f20ed 100755 --- a/bench/config/procfile.py +++ b/bench/config/procfile.py @@ -1,7 +1,15 @@ -import bench, os, click -from bench.utils import find_executable +# imports - standard imports +import os + +# imports - third party imports +import click + +# imports - module imports +import bench from bench.app import use_rq from bench.config.common_site_config import get_config +from bench.utils import find_executable + def setup_procfile(bench_path, yes=False, skip_redis=False): config = get_config(bench_path=bench_path) diff --git a/bench/config/production_setup.py b/bench/config/production_setup.py index dff6a402..4b5fc774 100755 --- a/bench/config/production_setup.py +++ b/bench/config/production_setup.py @@ -1,14 +1,13 @@ # imports - standard imports import os import sys -from distutils.spawn import find_executable # imports - module imports from bench.config.common_site_config import get_config from bench.config.nginx import make_nginx_conf from bench.config.supervisor import generate_supervisor_config from bench.config.systemd import generate_systemd_config -from bench.utils import CommandFailedError, exec_cmd, fix_prod_setup_perms, get_bench_name, get_cmd_output +from bench.utils import CommandFailedError, exec_cmd, find_executable, fix_prod_setup_perms, get_bench_name, get_cmd_output def setup_production_prerequisites(): diff --git a/bench/config/site_config.py b/bench/config/site_config.py index 6db83ea3..696185c8 100644 --- a/bench/config/site_config.py +++ b/bench/config/site_config.py @@ -1,8 +1,13 @@ -import os, json -from bench.utils import get_sites -from bench.config.nginx import make_nginx_conf +# imports - standard imports +import json +import os from collections import defaultdict +# imports - module imports +from bench.config.nginx import make_nginx_conf +from bench.utils import get_sites + + def get_site_config(site, bench_path='.'): config_path = os.path.join(bench_path, 'sites', site, 'site_config.json') if not os.path.exists(config_path): diff --git a/bench/config/systemd.py b/bench/config/systemd.py index dbf28c06..4a414825 100644 --- a/bench/config/systemd.py +++ b/bench/config/systemd.py @@ -1,9 +1,16 @@ -import os, getpass, click +# imports - standard imports +import getpass +import os + +# imports - third partyimports +import click + +# imports - module imports import bench -from bench.utils import exec_cmd from bench.app import get_current_frappe_version, use_rq -from bench.utils import get_bench_name, find_executable -from bench.config.common_site_config import get_config, update_config, get_gunicorn_workers +from bench.config.common_site_config import get_config, get_gunicorn_workers, update_config +from bench.utils import exec_cmd, find_executable, get_bench_name + def generate_systemd_config(bench_path, user=None, yes=False, stop=False, create_symlinks=False, From 84fb7e296a6e163100e9031d007214db1c1d50b6 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 3 Apr 2020 14:42:22 +0530 Subject: [PATCH 4/7] chore: codacy import and docstring fixes --- bench/config/__init__.py | 2 ++ bench/config/lets_encrypt.py | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bench/config/__init__.py b/bench/config/__init__.py index 7dd76eb3..301b515b 100644 --- a/bench/config/__init__.py +++ b/bench/config/__init__.py @@ -1,3 +1,5 @@ +"""Module for setting up system and respective bench configurations""" + # imports - third party imports from jinja2 import Environment, PackageLoader diff --git a/bench/config/lets_encrypt.py b/bench/config/lets_encrypt.py index 280554ab..5f0396b9 100755 --- a/bench/config/lets_encrypt.py +++ b/bench/config/lets_encrypt.py @@ -1,5 +1,4 @@ # imports - standard imports -import errno import os # imports - third party imports From 03e512fdab3c6b8240a4f290a554f72c686585f5 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 4 May 2020 17:43:32 +0530 Subject: [PATCH 5/7] fix: get-app sets up node requirements --- bench/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bench/app.py b/bench/app.py index 48f8427e..d57fb9d3 100755 --- a/bench/app.py +++ b/bench/app.py @@ -177,8 +177,8 @@ def install_app(app, bench_path=".", verbose=False, no_cache=False, postprocess= app_path = os.path.join(bench_path, "apps", app) cache_flag = "--no-cache-dir" if no_cache else "" - exec_cmd("{pip} install {quiet} -U -e {app} {no_cache}".format(pip=pip_path, - quiet=quiet_flag, app=app_path, no_cache=cache_flag)) + exec_cmd("{pip} install {quiet} -U -e {app} {no_cache}".format(pip=pip_path, quiet=quiet_flag, app=app_path, no_cache=cache_flag)) + exec_cmd("yarn install", cwd=app_path) add_to_appstxt(app, bench_path=bench_path) if postprocess: From b3842d9b305da87d689bea379049beee3dd1cbbb Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 4 May 2020 17:44:46 +0530 Subject: [PATCH 6/7] fix: handle app not found under GH frappe erpnext orgs --- bench/app.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bench/app.py b/bench/app.py index d57fb9d3..7115ee32 100755 --- a/bench/app.py +++ b/bench/app.py @@ -111,6 +111,9 @@ def get_app(git_url, branch=None, bench_path='.', skip_assets=False, verbose=Fal if git_url == data['name']: git_url = 'https://github.com/{org}/{app}'.format(org=org, app=git_url) break + else: + bench.utils.log("App {app} not found".format(app=git_url), level=2) + sys.exit(1) # Gets repo name from URL repo_name = git_url.rsplit('/', 1)[1].rsplit('.', 1)[0] @@ -407,7 +410,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(utils) + reload_module(bench.utils) backup_all_sites() patch_sites() build_assets() From 67fb0a4042d76eb8c988fba6b41b516fb22719e9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 6 May 2020 11:54:34 +0530 Subject: [PATCH 7/7] fix: install node modules if package.json exists --- bench/app.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bench/app.py b/bench/app.py index 7115ee32..8b165b2b 100755 --- a/bench/app.py +++ b/bench/app.py @@ -181,7 +181,10 @@ def install_app(app, bench_path=".", verbose=False, no_cache=False, postprocess= cache_flag = "--no-cache-dir" if no_cache else "" exec_cmd("{pip} install {quiet} -U -e {app} {no_cache}".format(pip=pip_path, quiet=quiet_flag, app=app_path, no_cache=cache_flag)) - exec_cmd("yarn install", cwd=app_path) + + if os.path.exists(os.path.join(app_path, 'package.json')): + exec_cmd("yarn install", cwd=app_path) + add_to_appstxt(app, bench_path=bench_path) if postprocess: