mirror of
https://github.com/frappe/bench.git
synced 2025-01-23 23:18:24 +00:00
first commit
This commit is contained in:
parent
c5791c26b2
commit
a395b15537
0
bench/__init__.py
Normal file
0
bench/__init__.py
Normal file
37
bench/app.py
Normal file
37
bench/app.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import os
|
||||||
|
from .utils import exec_cmd, get_frappe
|
||||||
|
|
||||||
|
|
||||||
|
def get_apps(bench='.'):
|
||||||
|
try:
|
||||||
|
with open(os.path.join(bench, 'sites', 'apps.txt')) as f:
|
||||||
|
return f.read().strip().split('\n')
|
||||||
|
except IOError:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def add_to_appstxt(app, bench='.'):
|
||||||
|
apps = get_apps(bench=bench)
|
||||||
|
if app not in apps:
|
||||||
|
apps.append(app)
|
||||||
|
with open(os.path.join(bench, 'sites', 'apps.txt'), 'w') as f:
|
||||||
|
return f.write('\n'.join(apps))
|
||||||
|
|
||||||
|
def get_app(app, git_url, bench='.'):
|
||||||
|
exec_cmd("git clone {} --origin upstream {}".format(git_url, app), cwd=os.path.join(bench, 'apps'))
|
||||||
|
install_app(app, bench=bench)
|
||||||
|
|
||||||
|
def new_app(app, bench='.'):
|
||||||
|
exec_cmd("{frappe} --make_app {apps}".format(frappe=get_frappe(bench=bench), apps=os.path.join(bench, 'apps')))
|
||||||
|
install_app(app, bench=bench)
|
||||||
|
|
||||||
|
def install_app(app, bench='.'):
|
||||||
|
exec_cmd("{pip} install -e {app}".format(pip=os.path.join(bench, 'env', 'bin', 'pip'), app=os.path.join('apps', app)))
|
||||||
|
add_to_appstxt(app, bench=bench)
|
||||||
|
|
||||||
|
def pull_all_apps(bench='.'):
|
||||||
|
apps_dir = os.path.join(bench, 'apps')
|
||||||
|
apps = [app for app in os.listdir(apps_dir) if os.path.isdir(os.path.join(apps_dir, app))]
|
||||||
|
for app in apps:
|
||||||
|
app_dir = os.path.join(apps_dir, app)
|
||||||
|
if os.path.exists(os.path.join(app_dir, '.git')):
|
||||||
|
exec_cmd("git pull --rebase upstream HEAD", cwd=app_dir)
|
103
bench/cli.py
Normal file
103
bench/cli.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import click
|
||||||
|
from .utils import init as _init
|
||||||
|
from .utils import setup_env as _setup_env
|
||||||
|
from .utils import new_site as _new_site
|
||||||
|
from .utils import build_assets, patch_sites, get_sites_dir, get_bench_dir, get_frappe, exec_cmd
|
||||||
|
from .app import get_app as _get_app
|
||||||
|
from .app import new_app as _new_app
|
||||||
|
from .app import pull_all_apps
|
||||||
|
from .config import generate_config
|
||||||
|
import os
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
def bench():
|
||||||
|
pass
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.argument('path')
|
||||||
|
def init(path):
|
||||||
|
_init(path)
|
||||||
|
click.echo('Bench {} initialized'.format(path))
|
||||||
|
|
||||||
|
@click.command('get-app')
|
||||||
|
@click.argument('name')
|
||||||
|
@click.argument('git-url')
|
||||||
|
def get_app(name, git_url):
|
||||||
|
_get_app(name, git_url)
|
||||||
|
|
||||||
|
@click.command('new-app')
|
||||||
|
@click.argument('app-name')
|
||||||
|
def new_app(app_name):
|
||||||
|
_new_app(app_name)
|
||||||
|
|
||||||
|
@click.command('new-site')
|
||||||
|
@click.argument('site')
|
||||||
|
def new_site(site):
|
||||||
|
_new_site(site)
|
||||||
|
|
||||||
|
@click.command('update')
|
||||||
|
@click.option('--pull', flag_value=True, type=bool)
|
||||||
|
@click.option('--patch',flag_value=True, type=bool)
|
||||||
|
@click.option('--build',flag_value=True, type=bool)
|
||||||
|
def update(pull=False, patch=False, build=False):
|
||||||
|
if not (pull or patch or build):
|
||||||
|
pull, patch, build = True, True, True
|
||||||
|
if pull:
|
||||||
|
pull_all_apps()
|
||||||
|
if patch:
|
||||||
|
patch_sites()
|
||||||
|
if build:
|
||||||
|
build_assets()
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
@click.group()
|
||||||
|
def setup():
|
||||||
|
pass
|
||||||
|
|
||||||
|
@click.command('sudoers')
|
||||||
|
def setup_sudoers():
|
||||||
|
pass
|
||||||
|
|
||||||
|
@click.command('nginx')
|
||||||
|
def setup_nginx():
|
||||||
|
generate_config('nginx', 'nginx.conf')
|
||||||
|
|
||||||
|
@click.command('supervisor')
|
||||||
|
def setup_supervisor():
|
||||||
|
generate_config('supervisor', 'supervisor.conf')
|
||||||
|
|
||||||
|
@click.command('auto-update')
|
||||||
|
def setup_auto_update():
|
||||||
|
exec_cmd('echo \"`crontab -l`\" | uniq | sed -e \"a0 10 * * * cd {bench_dir} && {bench} update\" | grep -v "^$" | uniq | crontab'.format(bench_dir=get_bench_dir(),
|
||||||
|
bench=os.path.join(get_bench_dir(), 'env', 'bin', 'bench')))
|
||||||
|
|
||||||
|
@click.command('backups')
|
||||||
|
def setup_backups():
|
||||||
|
exec_cmd('echo \"`crontab -l`\" | uniq | sed -e \"a0 */6 * * * cd {sites_dir} && {frappe} --backup all\" | grep -v "^$" | uniq | crontab'.format(sites_dir=get_sites_dir(),
|
||||||
|
frappe=get_frappe()))
|
||||||
|
|
||||||
|
@click.command('dnsmasq')
|
||||||
|
def setup_dnsmasq():
|
||||||
|
pass
|
||||||
|
|
||||||
|
@click.command('env')
|
||||||
|
def setup_env():
|
||||||
|
_setup_env()
|
||||||
|
pass
|
||||||
|
|
||||||
|
setup.add_command(setup_nginx)
|
||||||
|
setup.add_command(setup_sudoers)
|
||||||
|
setup.add_command(setup_supervisor)
|
||||||
|
setup.add_command(setup_auto_update)
|
||||||
|
setup.add_command(setup_dnsmasq)
|
||||||
|
setup.add_command(setup_backups)
|
||||||
|
setup.add_command(setup_env)
|
||||||
|
|
||||||
|
#Bench commands
|
||||||
|
|
||||||
|
bench.add_command(init)
|
||||||
|
bench.add_command(get_app)
|
||||||
|
bench.add_command(new_app)
|
||||||
|
bench.add_command(new_site)
|
||||||
|
bench.add_command(setup)
|
||||||
|
bench.add_command(update)
|
25
bench/config.py
Normal file
25
bench/config.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import os
|
||||||
|
import getpass
|
||||||
|
from jinja2 import Environment, PackageLoader
|
||||||
|
from .utils import get_sites
|
||||||
|
|
||||||
|
env = Environment(loader=PackageLoader('bench', 'templates'), trim_blocks=True)
|
||||||
|
|
||||||
|
def generate_config(application, template_name, bench='.'):
|
||||||
|
template = env.get_template(template_name)
|
||||||
|
bench_dir = os.path.abspath(bench)
|
||||||
|
sites_dir = os.path.join(bench_dir, "sites")
|
||||||
|
sites = get_sites(bench=bench)
|
||||||
|
user = getpass.getuser()
|
||||||
|
with open("sites/currentsite.txt") as f:
|
||||||
|
default_site = f.read().strip()
|
||||||
|
|
||||||
|
config = template.render(**{
|
||||||
|
"bench_dir": bench_dir,
|
||||||
|
"sites_dir": sites_dir,
|
||||||
|
"user": user,
|
||||||
|
"default_site": default_site,
|
||||||
|
"sites": sites
|
||||||
|
})
|
||||||
|
with open("config/{}.conf".format(application), 'w') as f:
|
||||||
|
f.write(config)
|
69
bench/templates/nginx.conf
Normal file
69
bench/templates/nginx.conf
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
|
||||||
|
upstream frappe {
|
||||||
|
server 127.0.0.1:8000 fail_timeout=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
client_max_body_size 4G;
|
||||||
|
server_name
|
||||||
|
{% for sitename in sites %}
|
||||||
|
{{ sitename }}
|
||||||
|
{% endfor %}
|
||||||
|
;
|
||||||
|
keepalive_timeout 5;
|
||||||
|
sendfile on;
|
||||||
|
root {{ sites_dir }};
|
||||||
|
|
||||||
|
location /private/ {
|
||||||
|
internal;
|
||||||
|
try_files /$uri =424;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /assets {
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files /$host/public/$uri @magic;
|
||||||
|
}
|
||||||
|
|
||||||
|
location @magic {
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Use-X-Accel-Redirect True;
|
||||||
|
proxy_read_timeout 120;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_pass http://frappe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80 default;
|
||||||
|
client_max_body_size 4G;
|
||||||
|
server_name localhost;
|
||||||
|
keepalive_timeout 5;
|
||||||
|
sendfile on;
|
||||||
|
root {{ sites_dir }};
|
||||||
|
|
||||||
|
location /private/ {
|
||||||
|
internal;
|
||||||
|
try_files /$uri =424;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /assets {
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files /{{ default_site }}/public/$uri @magic;
|
||||||
|
}
|
||||||
|
|
||||||
|
location @magic {
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Use-X-Accel-Redirect True;
|
||||||
|
proxy_set_header Host {{ default_site }};
|
||||||
|
proxy_read_timeout 120;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_pass http://frappe;
|
||||||
|
}
|
||||||
|
}
|
32
bench/templates/supervisor.conf
Normal file
32
bench/templates/supervisor.conf
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
[program:frappe-web]
|
||||||
|
environment=SITES_PATH='{{ bench_dir }}/sites'
|
||||||
|
command={{ bench_dir }}/env/bin/gunicorn -b 127.0.0.1:8000 -w 2 -t 120 frappe.app:application
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stopsignal=QUIT
|
||||||
|
stdout_logfile={{ bench_dir }}/logs/web.log
|
||||||
|
stderr_logfile={{ bench_dir }}/logs/web.error.log
|
||||||
|
user={{ user }}
|
||||||
|
|
||||||
|
[program:frappe-worker]
|
||||||
|
command={{ bench_dir }}/env/bin/python -m frappe.celery_app worker
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stopsignal=QUIT
|
||||||
|
stdout_logfile={{ bench_dir }}/logs/worker.log
|
||||||
|
stderr_logfile={{ bench_dir }}/logs/worker.error.log
|
||||||
|
user={{ user }}
|
||||||
|
directory={{ sites_dir }}
|
||||||
|
|
||||||
|
[program:frappe-workerbeat]
|
||||||
|
command={{ bench_dir }}/env/bin/python -m frappe.celery_app beat -s test.schedule
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stopsignal=QUIT
|
||||||
|
stdout_logfile={{ bench_dir }}/logs/workerbeat.log
|
||||||
|
stderr_logfile={{ bench_dir }}/logs/workerbeat.error.log
|
||||||
|
user={{ user }}
|
||||||
|
directory={{ sites_dir }}
|
||||||
|
|
||||||
|
[group:frappe]
|
||||||
|
programs=frappe-web,frappe-worker,frappe-workerbeat
|
53
bench/utils.py
Normal file
53
bench/utils.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
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):
|
||||||
|
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_env(bench=path)
|
||||||
|
|
||||||
|
def exec_cmd(cmd, cwd='.'):
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd, cwd=cwd, shell=True)
|
||||||
|
except subprocess.CalledProcessError, e:
|
||||||
|
print "Error:", e.output
|
||||||
|
raise
|
||||||
|
|
||||||
|
def setup_env(bench='.'):
|
||||||
|
exec_cmd('virtualenv {} -p {}'.format('env', sys.executable), cwd=bench)
|
||||||
|
|
||||||
|
def new_site(site, bench='.'):
|
||||||
|
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)
|
Loading…
x
Reference in New Issue
Block a user