mirror of
https://github.com/frappe/bench.git
synced 2025-02-03 19:38:24 +00:00
265 lines
8.8 KiB
Python
265 lines
8.8 KiB
Python
import os
|
|
import sys
|
|
import subprocess
|
|
import getpass
|
|
import logging
|
|
import json
|
|
from distutils.spawn import find_executable
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
default_config = {
|
|
'restart_supervisor_on_update': False,
|
|
'auto_update': False,
|
|
'serve_default_site': True,
|
|
'rebase_on_pull': False,
|
|
'update_bench_on_update': True,
|
|
'shallow_clone': True
|
|
}
|
|
|
|
def get_frappe(bench='.'):
|
|
frappe = os.path.abspath(os.path.join(bench, 'env', 'bin', 'frappe'))
|
|
if not os.path.exists(frappe):
|
|
print 'frappe app is not installed. Run the following command to install frappe'
|
|
print 'bench get-app frappe https://github.com/frappe/frappe.git'
|
|
return frappe
|
|
|
|
def init(path, apps_path=None, no_procfile=False, no_backups=False,
|
|
no_auto_update=False, frappe_path=None, frappe_branch=None, wheel_cache_dir=None):
|
|
from .app import get_app, install_apps_from_path
|
|
if os.path.exists(path):
|
|
print 'Directory {} already exists!'.format(path)
|
|
sys.exit(1)
|
|
|
|
os.mkdir(path)
|
|
for dirname in ('apps', 'sites', 'config', 'logs'):
|
|
os.mkdir(os.path.join(path, dirname))
|
|
|
|
setup_logging()
|
|
|
|
setup_env(bench=path)
|
|
put_config(default_config, bench=path)
|
|
if wheel_cache_dir:
|
|
update_config({"wheel_cache_dir":wheel_cache_dir}, bench=path)
|
|
prime_wheel_cache(bench=path)
|
|
if not frappe_path:
|
|
frappe_path = 'https://github.com/frappe/frappe.git'
|
|
get_app('frappe', frappe_path, branch=frappe_branch, bench=path)
|
|
if not no_procfile:
|
|
setup_procfile(bench=path)
|
|
if not no_backups:
|
|
setup_backups(bench=path)
|
|
if not no_auto_update:
|
|
setup_auto_update(bench=path)
|
|
if apps_path:
|
|
install_apps_from_path(apps_path, bench=path)
|
|
|
|
def exec_cmd(cmd, cwd='.'):
|
|
try:
|
|
subprocess.check_call(cmd, cwd=cwd, shell=True)
|
|
except subprocess.CalledProcessError, e:
|
|
print "Error:", getattr(e, "output", None) or getattr(e, "error", None)
|
|
raise
|
|
|
|
def setup_env(bench='.'):
|
|
exec_cmd('virtualenv -q {} -p {}'.format('env', sys.executable), cwd=bench)
|
|
exec_cmd('./env/bin/pip -q install wheel', cwd=bench)
|
|
|
|
def setup_procfile(bench='.'):
|
|
with open(os.path.join(bench, 'Procfile'), 'w') as f:
|
|
f.write("""web: ./env/bin/frappe --serve --sites_path sites
|
|
worker: sh -c 'cd sites && exec ../env/bin/python -m frappe.celery_app worker'
|
|
workerbeat: sh -c 'cd sites && exec ../env/bin/python -m frappe.celery_app beat -s scheduler.schedule'""")
|
|
|
|
def new_site(site, bench='.'):
|
|
logger.info('creating new site {}'.format(site))
|
|
exec_cmd("{frappe} --install {site} {site}".format(frappe=get_frappe(bench=bench), site=site), cwd=os.path.join(bench, 'sites'))
|
|
if len(get_sites(bench=bench)) == 1:
|
|
exec_cmd("{frappe} --use {site}".format(frappe=get_frappe(bench=bench), site=site), cwd=os.path.join(bench, 'sites'))
|
|
|
|
def patch_sites(bench='.'):
|
|
exec_cmd("{frappe} --latest all".format(frappe=get_frappe(bench=bench)), cwd=os.path.join(bench, 'sites'))
|
|
|
|
def build_assets(bench='.'):
|
|
exec_cmd("{frappe} --build".format(frappe=get_frappe(bench=bench)), cwd=os.path.join(bench, 'sites'))
|
|
|
|
def get_sites(bench='.'):
|
|
sites_dir = os.path.join(bench, "sites")
|
|
sites = [site for site in os.listdir(sites_dir)
|
|
if os.path.isdir(os.path.join(sites_dir, site)) and site not in ('assets',)]
|
|
return sites
|
|
|
|
def get_sites_dir(bench='.'):
|
|
return os.path.abspath(os.path.join(bench, 'sites'))
|
|
|
|
def get_bench_dir(bench='.'):
|
|
return os.path.abspath(bench)
|
|
|
|
def setup_auto_update(bench='.'):
|
|
# disabling auto update till Frappe version 5 is stable
|
|
return
|
|
logger.info('setting up auto update')
|
|
add_to_crontab('0 10 * * * cd {bench_dir} && {bench} update --auto >> {logfile} 2>&1'.format(bench_dir=get_bench_dir(bench=bench),
|
|
bench=os.path.join(get_bench_dir(bench=bench), 'env', 'bin', 'bench'),
|
|
logfile=os.path.join(get_bench_dir(bench=bench), 'logs', 'auto_update_log.log')))
|
|
|
|
def setup_backups(bench='.'):
|
|
logger.info('setting up backups')
|
|
add_to_crontab('0 */6 * * * cd {sites_dir} && {frappe} --backup all >> {logfile} 2>&1'.format(sites_dir=get_sites_dir(bench=bench),
|
|
frappe=get_frappe(bench=bench),
|
|
logfile=os.path.join(get_bench_dir(bench=bench), 'logs', 'backup.log')))
|
|
|
|
def add_to_crontab(line):
|
|
current_crontab = read_crontab()
|
|
if not line in current_crontab:
|
|
s = subprocess.Popen("crontab", stdin=subprocess.PIPE)
|
|
s.stdin.write(current_crontab)
|
|
s.stdin.write(line + '\n')
|
|
s.stdin.close()
|
|
|
|
def read_crontab():
|
|
s = subprocess.Popen(["crontab", "-l"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
out = s.stdout.read()
|
|
s.stdout.close()
|
|
return out
|
|
|
|
def update_bench():
|
|
logger.info('setting up sudoers')
|
|
cwd = os.path.dirname(os.path.abspath(__file__))
|
|
exec_cmd("git pull", cwd=cwd)
|
|
|
|
def setup_sudoers(user):
|
|
with open('/etc/sudoers.d/frappe', 'w') as f:
|
|
f.write("{user} ALL=(ALL) NOPASSWD: {supervisorctl} restart frappe\:\n".format(
|
|
user=user,
|
|
supervisorctl=subprocess.check_output('which supervisorctl', shell=True).strip()))
|
|
|
|
def setup_logging(bench='.'):
|
|
if os.path.exists(os.path.join(bench, 'logs')):
|
|
logger = logging.getLogger('bench')
|
|
log_file = os.path.join(bench, 'logs', 'bench.log')
|
|
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
|
|
hdlr = logging.FileHandler(log_file)
|
|
hdlr.setFormatter(formatter)
|
|
logger.addHandler(hdlr)
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
def get_config(bench='.'):
|
|
config_path = os.path.join(bench, 'config.json')
|
|
if not os.path.exists(config_path):
|
|
return {}
|
|
with open(config_path) as f:
|
|
return json.load(f)
|
|
|
|
def put_config(config, bench='.'):
|
|
with open(os.path.join(bench, 'config.json'), 'w') as f:
|
|
return json.dump(config, f, indent=1)
|
|
|
|
def update_config(new_config, bench='.'):
|
|
config = get_config(bench=bench)
|
|
config.update(new_config)
|
|
put_config(config, bench=bench)
|
|
|
|
def get_process_manager():
|
|
programs = ['foreman', 'forego', 'honcho']
|
|
program = None
|
|
for p in programs:
|
|
program = find_executable(p)
|
|
if program:
|
|
break
|
|
return program
|
|
|
|
def start():
|
|
program = get_process_manager()
|
|
if not program:
|
|
raise Exception("No process manager found")
|
|
os.execv(program, [program, 'start'])
|
|
|
|
def check_cmd(cmd, cwd='.'):
|
|
try:
|
|
subprocess.check_call(cmd, cwd=cwd, shell=True)
|
|
return True
|
|
except subprocess.CalledProcessError, e:
|
|
return False
|
|
|
|
def get_git_version():
|
|
version = get_cmd_output("git --version")
|
|
return version.strip().split()[-1]
|
|
|
|
def check_git_for_shallow_clone():
|
|
git_version = get_git_version()
|
|
if git_version.startswith('1.9') or git_version.startswith('2'):
|
|
return True
|
|
return False
|
|
|
|
def get_cmd_output(cmd, cwd='.'):
|
|
try:
|
|
return subprocess.check_output(cmd, cwd=cwd, shell=True)
|
|
except subprocess.CalledProcessError, e:
|
|
print "Error:", e.output
|
|
raise
|
|
|
|
def restart_supervisor_processes(bench='.'):
|
|
conf = get_config(bench=bench)
|
|
cmd = conf.get('supervisor_restart_cmd', 'sudo supervisorctl restart frappe:')
|
|
exec_cmd(cmd, cwd=bench)
|
|
|
|
def get_site_config(site, bench='.'):
|
|
config_path = os.path.join(bench, 'sites', site, 'site_config.json')
|
|
if not os.path.exists(config_path):
|
|
return {}
|
|
with open(config_path) as f:
|
|
return json.load(f)
|
|
|
|
def put_site_config(site, config, bench='.'):
|
|
config_path = os.path.join(bench, 'sites', site, 'site_config.json')
|
|
with open(config_path, 'w') as f:
|
|
return json.dump(config, f, indent=1)
|
|
|
|
def update_site_config(site, new_config, bench='.'):
|
|
config = get_site_config(site, bench=bench)
|
|
config.update(new_config)
|
|
put_site_config(site, config, bench=bench)
|
|
|
|
def set_nginx_port(site, port, bench='.', gen_config=True):
|
|
from .config import generate_nginx_config
|
|
if site not in get_sites(bench=bench):
|
|
raise Exception("No such site")
|
|
update_site_config(site, {"nginx_port": port}, bench=bench)
|
|
if gen_config:
|
|
generate_nginx_config()
|
|
|
|
def set_default_site(site, bench='.'):
|
|
if not site in get_sites(bench=bench):
|
|
raise Exception("Site not in bench")
|
|
exec_cmd("{frappe} --use {site}".format(frappe=get_frappe(bench=bench), site=site),
|
|
cwd=os.path.join(bench, 'sites'))
|
|
|
|
def update_requirements(bench='.'):
|
|
pip = os.path.join(bench, 'env', 'bin', 'pip')
|
|
apps_dir = os.path.join(bench, 'apps')
|
|
for app in os.listdir(apps_dir):
|
|
req_file = os.path.join(apps_dir, app, 'requirements.txt')
|
|
if os.path.exists(req_file):
|
|
exec_cmd("{pip} install -q -r {req_file}".format(pip=pip, req_file=req_file))
|
|
|
|
def backup_site(site, bench='.'):
|
|
exec_cmd("{frappe} --backup {site}".format(frappe=get_frappe(bench=bench), site=site),
|
|
cwd=os.path.join(bench, 'sites'))
|
|
|
|
def backup_all_sites(bench='.'):
|
|
for site in get_sites(bench=bench):
|
|
backup_site(site, bench=bench)
|
|
|
|
def prime_wheel_cache(bench='.'):
|
|
conf = get_config(bench=bench)
|
|
wheel_cache_dir = conf.get('wheel_cache_dir')
|
|
if not wheel_cache_dir:
|
|
raise Exception("Wheel cache dir not configured")
|
|
requirements = os.path.join(os.path.dirname(__file__), 'templates', 'cached_requirements.txt')
|
|
cmd = "{pip} wheel --find-links {wheelhouse} --wheel-dir {wheelhouse} -r {requirements}".format(
|
|
pip=os.path.join(bench, 'env', 'bin', 'pip'),
|
|
wheelhouse=wheel_cache_dir,
|
|
requirements=requirements)
|
|
exec_cmd(cmd)
|