2
0
mirror of https://github.com/frappe/bench.git synced 2025-01-24 15:38:25 +00:00

feat: New methods in Bench class

New methods added in Bench class:
* excluded_apps property
* shallow_clone property
* apps property - check if frappe app & sort
* sync: Sync the apps.txt file from the installed set of Frappe apps
* build: Build bench assets
* reload: Reload the Frappe processes - supervisor restart

New methods added in BenchApps class:
* add/append: Adding an App object to this, installs the app on the
  Bench
* remove: Removing an App from here, uninstalls and archives the app
This commit is contained in:
Gavin D'souza 2021-11-11 10:16:19 +05:30
parent 54f9741385
commit 53e811fe0a
2 changed files with 87 additions and 90 deletions

View File

@ -289,87 +289,18 @@ def install_app(app, bench_path=".", verbose=False, no_cache=False, restart_benc
restart_systemd_processes(bench_path=bench_path) restart_systemd_processes(bench_path=bench_path)
def remove_app(app, bench_path='.'):
import shutil
bench = Bench(bench_path)
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 bench.apps:
print(f"No app named {app}")
sys.exit(1)
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 bench.conf.get('restart_supervisor_on_update'):
restart_supervisor_processes(bench_path=bench_path)
if bench.conf.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="."):
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:
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')
for site in os.listdir(site_path):
req_file = os.path.join(site_path, site, 'site_config.json')
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(f"Cannot remove, app is installed on site: {site}")
sys.exit(1)
def pull_apps(apps=None, bench_path='.', reset=False): def pull_apps(apps=None, bench_path='.', reset=False):
'''Check all apps if there no local changes, pull''' '''Check all apps if there no local changes, pull'''
from bench.utils.app import get_remote, get_current_branch
bench = Bench(bench_path) bench = Bench(bench_path)
rebase = '--rebase' if bench.conf.get('rebase_on_pull') else '' rebase = '--rebase' if bench.conf.get('rebase_on_pull') else ''
apps = apps or bench.apps apps = apps or bench.apps
excluded_apps = bench.excluded_apps
# check for local changes # check for local changes
if not reset: if not reset:
for app in apps: for app in apps:
excluded_apps = get_excluded_apps()
if app in excluded_apps: if app in excluded_apps:
print(f"Skipping reset for app {app}") print(f"Skipping reset for app {app}")
continue continue
@ -391,7 +322,6 @@ Here are your choices:
wait for them to be merged in the core.''') wait for them to be merged in the core.''')
sys.exit(1) sys.exit(1)
excluded_apps = get_excluded_apps()
for app in apps: for app in apps:
if app in excluded_apps: if app in excluded_apps:
print(f"Skipping pull for app {app}") print(f"Skipping pull for app {app}")

View File

@ -1,31 +1,67 @@
import os import os
import shutil import shutil
import sys import sys
import typing
import logging import logging
from typing import MutableSequence from typing import MutableSequence
import bench import bench
from bench.utils import remove_backups_crontab, folders_in_bench, get_venv_path, exec_cmd, get_env_cmd from bench.exceptions import ValidationError
from bench.config.common_site_config import setup_config from bench.config.common_site_config import setup_config
from bench.utils import paths_in_bench, get_venv_path, exec_cmd, get_env_cmd, is_frappe_app, get_git_version, run_frappe_cmd
from bench.utils.bench import validate_app_installed_on_sites, restart_supervisor_processes, restart_systemd_processes, remove_backups_crontab
if typing.TYPE_CHECKING:
from bench.app import App
logger = logging.getLogger(bench.PROJECT_NAME) logger = logging.getLogger(bench.PROJECT_NAME)
class Base: class Base:
def run(self, cmd): def run(self, cmd, cwd=None):
return exec_cmd(cmd, cwd=self.cwd) return exec_cmd(cmd, cwd=cwd or self.cwd)
class Bench(Base): class Validator:
def validate_app_uninstall(self, app):
if app not in self.apps:
raise ValidationError(f"No app named {app}")
validate_app_installed_on_sites(app, bench_path=self.name)
class Bench(Base, Validator):
def __init__(self, path): def __init__(self, path):
self.name = path self.name = path
self.cwd = os.path.abspath(path) self.cwd = os.path.abspath(path)
self.exists = os.path.exists(self.name) self.exists = os.path.exists(self.name)
self.setup = BenchSetup(self) self.setup = BenchSetup(self)
self.teardown = BenchTearDown(self) self.teardown = BenchTearDown(self)
self.apps = BenchApps(self) self.apps = BenchApps(self)
self.apps_txt = os.path.join(self.name, 'sites', 'apps.txt')
self.excluded_apps_txt = os.path.join(self.name, 'sites', 'excluded_apps.txt')
@property
def shallow_clone(self):
config = self.conf
if config:
if config.get('release_bench') or not config.get('shallow_clone'):
return False
if get_git_version() > 1.9:
return True
@property
def excluded_apps(self):
try:
with open(self.excluded_apps_txt) as f:
return f.read().strip().split('\n')
except Exception:
return []
@property @property
def sites(self): def sites(self):
return [ return [
@ -59,17 +95,33 @@ class Bench(Base):
from bench.app import App from bench.app import App
app = App(app, branch=branch) app = App(app, branch=branch)
self.apps.append(app)
# get app? self.sync()
# install app to env
# add to apps.txt
return
def uninstall(self, app): def uninstall(self, app):
# remove from apps.txt from bench.app import App
# uninstall app from env
# remove app? self.validate_app_uninstall(app)
return self.apps.remove(App(app, bench=self))
self.sync()
self.build()
self.reload()
def build(self):
# build assets & stuff
run_frappe_cmd("build", bench_path=self.name)
def reload(self):
conf = self.conf
if conf.get('restart_supervisor_on_update'):
restart_supervisor_processes(bench_path=self.name)
if conf.get('restart_systemd_on_update'):
restart_systemd_processes(bench_path=self.name)
def sync(self):
self.apps.initialize_apps()
with open(self.apps_txt, "w") as f:
return f.write("\n".join(self.apps))
class BenchApps(MutableSequence): class BenchApps(MutableSequence):
@ -79,9 +131,10 @@ class BenchApps(MutableSequence):
def initialize_apps(self): def initialize_apps(self):
try: try:
self.apps = open( self.apps = [x for x in os.listdir(
os.path.join(self.bench.name, "sites", "apps.txt") os.path.join(self.bench.name, "apps")
).read().splitlines() ) if is_frappe_app(os.path.join(self.bench.name, "apps", x))]
self.apps.sort()
except FileNotFoundError: except FileNotFoundError:
self.apps = [] self.apps = []
@ -108,6 +161,20 @@ class BenchApps(MutableSequence):
# TODO: fetch and install app to bench # TODO: fetch and install app to bench
self.apps.insert(key, value) self.apps.insert(key, value)
def add(self, app: "App"):
app.get()
app.install()
super().append(app.repo)
self.apps.sort()
def remove(self, app: "App"):
app.uninstall()
app.remove()
super().remove(app.repo)
def append(self, app : "App"):
return self.add(app)
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()
@ -123,7 +190,7 @@ class BenchSetup(Base):
def dirs(self): def dirs(self):
os.makedirs(self.bench.name, exist_ok=True) os.makedirs(self.bench.name, exist_ok=True)
for dirname in folders_in_bench: for dirname in paths_in_bench:
os.makedirs(os.path.join(self.bench.name, dirname), exist_ok=True) os.makedirs(os.path.join(self.bench.name, dirname), exist_ok=True)
def env(self, python="python3"): def env(self, python="python3"):