2
0
mirror of https://github.com/frappe/bench.git synced 2024-09-28 06:49:06 +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)
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):
'''Check all apps if there no local changes, pull'''
from bench.utils.app import get_remote, get_current_branch
bench = Bench(bench_path)
rebase = '--rebase' if bench.conf.get('rebase_on_pull') else ''
apps = apps or bench.apps
excluded_apps = bench.excluded_apps
# check for local changes
if not reset:
for app in apps:
excluded_apps = get_excluded_apps()
if app in excluded_apps:
print(f"Skipping reset for app {app}")
continue
@ -391,7 +322,6 @@ Here are your choices:
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(f"Skipping pull for app {app}")

View File

@ -1,31 +1,67 @@
import os
import shutil
import sys
import typing
import logging
from typing import MutableSequence
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.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)
class Base:
def run(self, cmd):
return exec_cmd(cmd, cwd=self.cwd)
def run(self, cmd, cwd=None):
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):
self.name = path
self.cwd = os.path.abspath(path)
self.exists = os.path.exists(self.name)
self.setup = BenchSetup(self)
self.teardown = BenchTearDown(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
def sites(self):
return [
@ -59,17 +95,33 @@ class Bench(Base):
from bench.app import App
app = App(app, branch=branch)
# get app?
# install app to env
# add to apps.txt
return
self.apps.append(app)
self.sync()
def uninstall(self, app):
# remove from apps.txt
# uninstall app from env
# remove app?
return
from bench.app import App
self.validate_app_uninstall(app)
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):
@ -79,9 +131,10 @@ class BenchApps(MutableSequence):
def initialize_apps(self):
try:
self.apps = open(
os.path.join(self.bench.name, "sites", "apps.txt")
).read().splitlines()
self.apps = [x for x in os.listdir(
os.path.join(self.bench.name, "apps")
) if is_frappe_app(os.path.join(self.bench.name, "apps", x))]
self.apps.sort()
except FileNotFoundError:
self.apps = []
@ -108,6 +161,20 @@ class BenchApps(MutableSequence):
# TODO: fetch and install app to bench
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):
return self.__str__()
@ -123,7 +190,7 @@ class BenchSetup(Base):
def dirs(self):
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)
def env(self, python="python3"):