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:
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)
|
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}")
|
||||||
|
101
bench/bench.py
101
bench/bench.py
@ -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"):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user