mirror of
https://github.com/frappe/bench.git
synced 2025-01-10 17:24:41 +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:
parent
54f9741385
commit
53e811fe0a
76
bench/app.py
76
bench/app.py
@ -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}")
|
||||
|
101
bench/bench.py
101
bench/bench.py
@ -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"):
|
||||
|
Loading…
Reference in New Issue
Block a user