svn commit: r393308 - in head/www: . py-frappe-bench py-frappe-bench/files
Fukang Chen
loader at FreeBSD.org
Fri Jul 31 14:06:00 UTC 2015
Author: loader (doc committer)
Date: Fri Jul 31 14:05:58 2015
New Revision: 393308
URL: https://svnweb.freebsd.org/changeset/ports/393308
Log:
[NEW] www/py-frappe-bench: Frappe / ERPNext apps setup tool
The bench allows you to setup Frappe / ERPNext apps on
your local machine or a production server. You can use
the bench to serve multiple frappe sites.
WWW: https://github.com/frappe/bench
Approved by: koobs
Differential Revision: https://reviews.freebsd.org/D3120
Added:
head/www/py-frappe-bench/
head/www/py-frappe-bench/Makefile (contents, props changed)
head/www/py-frappe-bench/distinfo (contents, props changed)
head/www/py-frappe-bench/files/
head/www/py-frappe-bench/files/patch-a93acec (contents, props changed)
head/www/py-frappe-bench/files/patch-setup.py (contents, props changed)
head/www/py-frappe-bench/pkg-descr (contents, props changed)
Modified:
head/www/Makefile
Modified: head/www/Makefile
==============================================================================
--- head/www/Makefile Fri Jul 31 13:57:21 2015 (r393307)
+++ head/www/Makefile Fri Jul 31 14:05:58 2015 (r393308)
@@ -1586,6 +1586,7 @@
SUBDIR += py-flup
SUBDIR += py-formalchemy
SUBDIR += py-formencode
+ SUBDIR += py-frappe-bench
SUBDIR += py-frozen-flask
SUBDIR += py-funkload
SUBDIR += py-gandi.cli
Added: head/www/py-frappe-bench/Makefile
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ head/www/py-frappe-bench/Makefile Fri Jul 31 14:05:58 2015 (r393308)
@@ -0,0 +1,33 @@
+# Created by: loader <loader at FreeBSD.org>
+# $FreeBSD$
+
+PORTNAME= frappe-bench
+PORTVERSION= 0.92
+DISTVERSIONPREFIX= v
+CATEGORIES= www python
+PKGNAMEPREFIX= ${PYTHON_PKGNAMEPREFIX}
+
+MAINTAINER= loader at FreeBSD.org
+COMMENT= Frappe / ERPNext apps setup tool
+
+LICENSE= GPLv3
+LICENSE_FILE= ${WRKSRC}/LICENSE.md
+
+RUN_DEPENDS= ${PYTHON_PKGNAMEPREFIX}click>0:${PORTSDIR}/devel/py-click \
+ ${PYTHON_PKGNAMEPREFIX}Jinja2>0:${PORTSDIR}/devel/py-Jinja2 \
+ ${PYTHON_PKGNAMEPREFIX}virtualenv>0:${PORTSDIR}/devel/py-virtualenv \
+ ${PYTHON_PKGNAMEPREFIX}requests>0:${PORTSDIR}/www/py-requests \
+ ${PYTHON_PKGNAMEPREFIX}honcho>0:${PORTSDIR}/sysutils/py-honcho \
+ ${PYTHON_PKGNAMEPREFIX}semantic_version>0:${PORTSDIR}/devel/py-semantic_version \
+ ${PYTHON_PKGNAMEPREFIX}GitPython>=1.0.1:${PORTSDIR}/devel/py-gitpython \
+ ${PYTHON_PKGNAMEPREFIX}pip>0:${PORTSDIR}/devel/py-pip \
+ git:${PORTSDIR}/devel/git
+
+USE_GITHUB= yes
+GH_ACCOUNT= frappe
+GH_PROJECT= bench
+
+USES= python
+USE_PYTHON= autoplist distutils concurrent
+
+.include <bsd.port.mk>
Added: head/www/py-frappe-bench/distinfo
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ head/www/py-frappe-bench/distinfo Fri Jul 31 14:05:58 2015 (r393308)
@@ -0,0 +1,2 @@
+SHA256 (frappe-bench-v0.92_GH0.tar.gz) = 4095b752bd777166d48054df6fb198ad1349e0bdf60d112e632ea622a9b99587
+SIZE (frappe-bench-v0.92_GH0.tar.gz) = 30835
Added: head/www/py-frappe-bench/files/patch-a93acec
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ head/www/py-frappe-bench/files/patch-a93acec Fri Jul 31 14:05:58 2015 (r393308)
@@ -0,0 +1,1763 @@
+--- README.md.orig 2014-11-19 06:36:44 UTC
++++ README.md
+@@ -5,7 +5,7 @@ The bench allows you to setup Frappe / E
+
+ To do this install, you must have basic information on how Linux works and should be able to use the command-line. If you are looking easier ways to get started and evaluate ERPNext, [download the Virtual Machine or take a free trial at FrappeCloud.com](https://erpnext.com/use).
+
+-For questions, please join the [developer forum](https://groups.google.com/group/erpnext-developer-forum).
++For questions, please join the [developer forum](https://discuss.frappe.io/).
+
+ Installation
+ ============
+@@ -37,6 +37,15 @@ Install pre-requisites,
+ * Redis
+ * [wkhtmltopdf](http://wkhtmltopdf.org/downloads.html) (optional, required for pdf generation)
+ * Memcached
++
++For installing MaraiDB on OSX, use:
++```
++brew install mariadb
++mysql_install_db
++mysql.server start
++mysqladmin -uroot password ROOTPASSWORD
++```
++
+
+ Install bench as a *non root* user,
+
+@@ -105,11 +114,9 @@ To setup a bench that runs ERPNext, run
+ cd ~
+ bench init frappe-bench
+ cd frappe-bench
+-bench get-app erpnext https://github.com/frappe/erpnext # Add ERPNext to your bench apps
+-bench get-app shopping_cart https://github.com/frappe/shopping-cart # Add Shopping cart to your bench apps
+-bench new-site site1.local # Create a new site
+-bench frappe --install_app erpnext site1.local # Install ERPNext for the site
+-bench frappe --install_app shopping_cart site1.local # Install Shopping cart for the site
++bench get-app erpnext https://github.com/frappe/erpnext # Add ERPNext to your bench apps
++bench new-site site1.local # Create a new site
++bench install-app erpnext # Install ERPNext for the site
+ ```
+
+ You can now either use `bench start` or setup the bench for production use.
+@@ -162,7 +169,7 @@ Frappe Processes
+ * WSGI Server
+
+ * The WSGI server is responsible for responding to the HTTP requests to
+- frappe. In development scenario (`frappe --serve` or `bench start`), the
++ frappe. In development scenario (`bench serve` or `bench start`), the
+ Werkzeug WSGI server is used and in production, gunicorn (automatically
+ configured in supervisor) is used.
+
+--- bench/app.py.orig 2014-11-19 06:36:44 UTC
++++ bench/app.py
+@@ -1,12 +1,22 @@
+ import os
+-from .utils import exec_cmd, get_frappe, check_git_for_shallow_clone, get_config, build_assets, restart_supervisor_processes, get_cmd_output
++from .utils import exec_cmd, get_frappe, check_git_for_shallow_clone, get_config, build_assets, restart_supervisor_processes, get_cmd_output, run_frappe_cmd
+
+ import logging
+ import requests
++import semantic_version
+ import json
++import re
++import subprocess
++
+
+ logger = logging.getLogger(__name__)
+
++class MajorVersionUpgradeException(Exception):
++ def __init__(self, message, upstream_version, local_version):
++ super(MajorVersionUpgradeException, self).__init__(message)
++ self.upstream_version = upstream_version
++ self.local_version = local_version
++
+ def get_apps(bench='.'):
+ try:
+ with open(os.path.join(bench, 'sites', 'apps.txt')) as f:
+@@ -18,10 +28,19 @@ 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))
++ return write_appstxt(apps, bench=bench)
+
+-def get_app(app, git_url, branch=None, bench='.'):
++def remove_from_appstxt(app, bench='.'):
++ apps = get_apps(bench=bench)
++ if app in apps:
++ apps.remove(app)
++ return write_appstxt(apps, bench=bench)
++
++def write_appstxt(apps, bench='.'):
++ with open(os.path.join(bench, 'sites', 'apps.txt'), 'w') as f:
++ return f.write('\n'.join(apps))
++
++def get_app(app, git_url, branch=None, bench='.', build_asset_files=True):
+ logger.info('getting app {}'.format(app))
+ shallow_clone = '--depth 1' if check_git_for_shallow_clone() and get_config().get('shallow_clone') else ''
+ branch = '--branch {branch}'.format(branch=branch) if branch else ''
+@@ -33,14 +52,20 @@ def get_app(app, git_url, branch=None, b
+ cwd=os.path.join(bench, 'apps'))
+ print 'installing', app
+ install_app(app, bench=bench)
+- build_assets(bench=bench)
++ if build_asset_files:
++ build_assets(bench=bench)
+ conf = get_config()
+ if conf.get('restart_supervisor_on_update'):
+ restart_supervisor_processes(bench=bench)
+
+ def new_app(app, bench='.'):
+ logger.info('creating new app {}'.format(app))
+- exec_cmd("{frappe} --make_app {apps}".format(frappe=get_frappe(bench=bench), apps=os.path.join(bench, 'apps')))
++ apps = os.path.abspath(os.path.join(bench, 'apps'))
++ if FRAPPE_VERSION == 4:
++ exec_cmd("{frappe} --make_app {apps} {app}".format(frappe=get_frappe(bench=bench),
++ apps=apps, app=app))
++ else:
++ run_frappe_cmd('make-app', apps, app, bench=bench)
+ install_app(app, bench=bench)
+
+ def install_app(app, bench='.'):
+@@ -57,19 +82,112 @@ 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))]
+ rebase = '--rebase' if get_config().get('rebase_on_pull') else ''
++ frappe_dir = os.path.join(apps_dir, 'frappe')
++
+ for app in apps:
+ app_dir = os.path.join(apps_dir, app)
+ if os.path.exists(os.path.join(app_dir, '.git')):
+ logger.info('pulling {0}'.format(app))
+ exec_cmd("git pull {rebase} upstream {branch}".format(rebase=rebase, branch=get_current_branch(app_dir)), cwd=app_dir)
+
++def is_version_upgrade(bench='.', branch=None):
++ apps_dir = os.path.join(bench, 'apps')
++ frappe_dir = os.path.join(apps_dir, 'frappe')
++
++ fetch_upstream(frappe_dir)
++ upstream_version = get_upstream_version(frappe_dir, branch=branch)
++
++ if not upstream_version:
++ raise Exception("Current branch of 'frappe' not in upstream")
++
++ local_version = get_major_version(get_current_version(frappe_dir))
++ upstream_version = get_major_version(upstream_version)
++
++ if upstream_version - local_version > 0:
++ return (local_version, upstream_version)
++ return False
++
++def get_current_frappe_version(bench='.'):
++ apps_dir = os.path.join(bench, 'apps')
++ frappe_dir = os.path.join(apps_dir, 'frappe')
++
++ try:
++ return get_major_version(get_current_version(frappe_dir))
++ except IOError:
++ return ''
++
+ def get_current_branch(repo_dir):
+ return get_cmd_output("basename $(git symbolic-ref -q HEAD)", cwd=repo_dir)
+
++def fetch_upstream(repo_dir):
++ return exec_cmd("git fetch upstream", cwd=repo_dir)
++
++def get_current_version(repo_dir):
++ with open(os.path.join(repo_dir, 'setup.py')) as f:
++ return get_version_from_string(f.read())
++
++def get_upstream_version(repo_dir, branch=None):
++ if not branch:
++ branch = get_current_branch(repo_dir)
++ try:
++ contents = subprocess.check_output(['git', 'show', 'upstream/{branch}:setup.py'.format(branch=branch)], cwd=repo_dir, stderr=subprocess.STDOUT)
++ except subprocess.CalledProcessError, e:
++ if "Invalid object" in e.output:
++ return None
++ else:
++ raise
++ return get_version_from_string(contents)
++
++def switch_branch(branch, apps=None, bench='.', upgrade=False):
++ from .utils import update_requirements, backup_all_sites, patch_sites, build_assets, pre_upgrade, post_upgrade
++ import utils
++ apps_dir = os.path.join(bench, 'apps')
++ version_upgrade = is_version_upgrade(bench=bench, branch=branch)
++ if version_upgrade and not upgrade:
++ raise MajorVersionUpgradeException("Switching to {0} will cause upgrade from {1} to {2}. Pass --upgrade to confirm".format(branch, version_upgrade[0], version_upgrade[1]), version_upgrade[0], version_upgrade[1])
++
++ if not apps:
++ apps = ('frappe', 'erpnext', 'shopping_cart')
++ for app in apps:
++ app_dir = os.path.join(apps_dir, app)
++ if os.path.exists(app_dir):
++ unshallow = "--unshallow" if os.path.exists(os.path.join(app_dir, ".git", "shallow")) else ""
++ exec_cmd("git config --unset-all remote.upstream.fetch", cwd=app_dir)
++ exec_cmd("git config --add remote.upstream.fetch '+refs/heads/*:refs/remotes/upstream/*'", cwd=app_dir)
++ exec_cmd("git fetch upstream {unshallow}".format(unshallow=unshallow), cwd=app_dir)
++ exec_cmd("git checkout {branch}".format(branch=branch), cwd=app_dir)
++ exec_cmd("git merge upstream/{branch}".format(branch=branch), cwd=app_dir)
++
++ if version_upgrade and upgrade:
++ update_requirements()
++ pre_upgrade(version_upgrade[0], version_upgrade[1])
++ reload(utils)
++ backup_all_sites()
++ patch_sites()
++ build_assets()
++ post_upgrade(version_upgrade[0], version_upgrade[1])
++
++def switch_to_master(apps=None, bench='.', upgrade=False):
++ switch_branch('master', apps=apps, bench=bench, upgrade=upgrade)
++
++def switch_to_develop(apps=None, bench='.', upgrade=False):
++ switch_branch('develop', apps=apps, bench=bench, upgrade=upgrade)
++
++def switch_to_v4(apps=None, bench='.', upgrade=False):
++ switch_branch('v4.x.x', apps=apps, bench=bench, upgrade=upgrade)
++
++def get_version_from_string(contents):
++ match = re.search(r"^(\s*%s\s*=\s*['\\\"])(.+?)(['\"])(?sm)" % 'version',
++ contents)
++ return match.group(2)
++
++def get_major_version(version):
++ return semantic_version.Version(version).major
++
+ def install_apps_from_path(path, bench='.'):
+ apps = get_apps_json(path)
+ for app in apps:
+- get_app(app['name'], app['url'], branch=app.get('branch'), bench=bench)
++ get_app(app['name'], app['url'], branch=app.get('branch'), bench=bench, build_asset_files=False)
+
+ def get_apps_json(path):
+ if path.startswith('http'):
+@@ -78,3 +196,5 @@ def get_apps_json(path):
+ else:
+ with open(path) as f:
+ return json.load(f)
++
++FRAPPE_VERSION = get_current_frappe_version()
+--- bench/cli.py.orig 2014-11-19 06:36:44 UTC
++++ bench/cli.py
+@@ -8,32 +8,54 @@ from .utils import setup_sudoers as _set
+ from .utils import start as _start
+ from .utils import setup_procfile as _setup_procfile
+ from .utils import set_nginx_port as _set_nginx_port
+-from .utils import set_nginx_port as _set_nginx_port
++from .utils import set_url_root as _set_url_root
+ from .utils import set_default_site as _set_default_site
+-from .utils import (build_assets, patch_sites, exec_cmd, update_bench, get_frappe, setup_logging,
++from .utils import (build_assets, patch_sites, exec_cmd, update_bench, get_env_cmd, get_frappe, setup_logging,
+ get_config, update_config, restart_supervisor_processes, put_config, default_config, update_requirements,
+- backup_all_sites, backup_site, get_sites, prime_wheel_cache, is_root, set_mariadb_host, drop_privileges)
++ backup_all_sites, backup_site, get_sites, prime_wheel_cache, is_root, set_mariadb_host, drop_privileges,
++ fix_file_perms, fix_prod_setup_perms, set_ssl_certificate, set_ssl_certificate_key, get_cmd_output, post_upgrade,
++ pre_upgrade, PatchError, download_translations_p)
+ 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_nginx_config, generate_supervisor_config
++from .app import pull_all_apps, get_apps, get_current_frappe_version, is_version_upgrade, switch_to_v4, switch_to_master, switch_to_develop
++from .config import generate_nginx_config, generate_supervisor_config, generate_redis_config
+ from .production_setup import setup_production as _setup_production
++from .migrate_to_v5 import migrate_to_v5
+ import os
+ import sys
+ import logging
+ import copy
++import json
+ import pwd
+ import grp
++import subprocess
+
+ logger = logging.getLogger('bench')
+
++global FRAPPE_VERSION
++
+ def cli():
+ check_uid()
+ change_dir()
+ change_uid()
+ if len(sys.argv) > 2 and sys.argv[1] == "frappe":
+- return frappe()
+- return bench()
++ return old_frappe_cli()
++ elif len(sys.argv) > 1 and sys.argv[1] in get_frappe_commands():
++ return frappe_cmd()
++ elif len(sys.argv) > 1 and sys.argv[1] in ("--site", "--verbose", "--force", "--profile"):
++ return frappe_cmd()
++ elif len(sys.argv) > 1 and sys.argv[1]=="--help":
++ print click.Context(bench).get_help()
++ print
++ print get_frappe_help()
++ return
++ elif len(sys.argv) > 1 and sys.argv[1] in get_apps():
++ return app_cmd()
++ else:
++ try:
++ bench()
++ except PatchError:
++ sys.exit(1)
+
+ def cmd_requires_root():
+ if len(sys.argv) > 2 and sys.argv[2] in ('production', 'sudoers'):
+@@ -57,17 +79,51 @@ def change_uid():
+ sys.exit(1)
+
+ def change_dir():
++ if os.path.exists('config.json') or "init" in sys.argv:
++ return
+ dir_path_file = '/etc/frappe_bench_dir'
+ if os.path.exists(dir_path_file):
+ with open(dir_path_file) as f:
+ dir_path = f.read().strip()
+- os.chdir(dir_path)
++ if os.path.exists(dir_path):
++ os.chdir(dir_path)
+
+-def frappe(bench='.'):
++def old_frappe_cli(bench='.'):
+ f = get_frappe(bench=bench)
+ os.chdir(os.path.join(bench, 'sites'))
+ os.execv(f, [f] + sys.argv[2:])
+
++def app_cmd(bench='.'):
++ f = get_env_cmd('python', bench=bench)
++ os.chdir(os.path.join(bench, 'sites'))
++ os.execv(f, [f] + ['-m', 'frappe.utils.bench_helper'] + sys.argv[1:])
++
++def frappe_cmd(bench='.'):
++ f = get_env_cmd('python', bench=bench)
++ os.chdir(os.path.join(bench, 'sites'))
++ os.execv(f, [f] + ['-m', 'frappe.utils.bench_helper', 'frappe'] + sys.argv[1:])
++
++def get_frappe_commands(bench='.'):
++ python = get_env_cmd('python', bench=bench)
++ sites_path = os.path.join(bench, 'sites')
++ if not os.path.exists(sites_path):
++ return []
++ try:
++ return json.loads(get_cmd_output("{python} -m frappe.utils.bench_helper get-frappe-commands".format(python=python), cwd=sites_path))
++ except subprocess.CalledProcessError:
++ return []
++
++def get_frappe_help(bench='.'):
++ python = get_env_cmd('python', bench=bench)
++ sites_path = os.path.join(bench, 'sites')
++ if not os.path.exists(sites_path):
++ return []
++ try:
++ out = get_cmd_output("{python} -m frappe.utils.bench_helper get-frappe-help".format(python=python), cwd=sites_path)
++ return "Framework commands:\n" + out.split('Commands:')[1]
++ except subprocess.CalledProcessError:
++ return ""
++
+ @click.command()
+ def shell(bench='.'):
+ if not os.environ.get('SHELL'):
+@@ -86,6 +142,8 @@ def shell(bench='.'):
+ def bench(bench='.'):
+ "Bench manager for Frappe"
+ # TODO add bench path context
++ global FRAPPE_VERSION
++ FRAPPE_VERSION = get_current_frappe_version()
+ setup_logging(bench=bench)
+
+ @click.command()
+@@ -134,8 +192,9 @@ def new_site(site, mariadb_root_password
+ @click.option('--requirements',flag_value=True, type=bool, help="Update requirements")
+ @click.option('--restart-supervisor',flag_value=True, type=bool, help="restart supervisor processes after update")
+ @click.option('--auto',flag_value=True, type=bool)
++ at click.option('--upgrade',flag_value=True, type=bool)
+ @click.option('--no-backup',flag_value=True, type=bool)
+-def update(pull=False, patch=False, build=False, bench=False, auto=False, restart_supervisor=False, requirements=False, no_backup=False):
++def update(pull=False, patch=False, build=False, bench=False, auto=False, restart_supervisor=False, requirements=False, no_backup=False, upgrade=False):
+ "Update bench"
+
+ if not (pull or patch or build or bench or requirements):
+@@ -155,12 +214,36 @@ def update(pull=False, patch=False, buil
+ 'build': build,
+ 'requirements': requirements,
+ 'no-backup': no_backup,
+- 'restart-supervisor': restart_supervisor
++ 'restart-supervisor': restart_supervisor,
++ 'upgrade': upgrade
+ })
++
++ version_upgrade = is_version_upgrade()
++
++ if version_upgrade and not upgrade:
++ print
++ print
++ print "This update will cause a major version change in Frappe/ERPNext from {0} to {1} (beta).".format(*version_upgrade)
++ print "This would take significant time to migrate and might break custom apps. Please run `bench update --upgrade` to confirm."
++ print
++ # print "You can also pin your bench to {0} by running `bench swtich-to-v{0}`".format(version_upgrade[0])
++ print "You can stay on the latest stable release by running `bench switch-to-master` or pin your bench to {0} by running `bench swtich-to-v{0}`".format(version_upgrade[0])
++ sys.exit(1)
++ elif not version_upgrade and upgrade:
++ upgrade = False
++
+ if pull:
+ pull_all_apps()
++
+ if requirements:
+ update_requirements()
++
++ if upgrade:
++ pre_upgrade(version_upgrade[0], version_upgrade[1])
++ import utils, app
++ reload(utils)
++ reload(app)
++
+ if patch:
+ if not no_backup:
+ backup_all_sites()
+@@ -169,14 +252,23 @@ def update(pull=False, patch=False, buil
+ build_assets()
+ if restart_supervisor or conf.get('restart_supervisor_on_update'):
+ restart_supervisor_processes()
++ if upgrade:
++ post_upgrade(version_upgrade[0], version_upgrade[1])
+
+ print "_"*80
+ print "https://frappe.io/buy - Donate to help make better free and open source tools"
+ print
+
++ at click.command('retry-upgrade')
++ at click.option('--version', default=5)
++def retry_upgrade(version):
++ pull_all_apps()
++ patch_sites()
++ build_assets()
++ post_upgrade(version-1, version)
++
+ def restart_update(kwargs):
+ args = ['--'+k for k, v in kwargs.items() if v]
+- print 'restarting '
+ os.execv(sys.argv[0], sys.argv[:2] + args)
+
+ @click.command('restart')
+@@ -198,6 +290,33 @@ def migrate_3to4(path):
+ migrate_3to4=os.path.join(os.path.dirname(__file__), 'migrate3to4.py'),
+ site=path))
+
++ at click.command('switch-to-master')
++ at click.option('--upgrade',flag_value=True, type=bool)
++def _switch_to_master(upgrade=False):
++ "Switch frappe and erpnext to master branch"
++ switch_to_master(upgrade=upgrade)
++ print
++ print 'Switched to master'
++ print 'Please run `bench update --patch` to be safe from any differences in database schema'
++
++ at click.command('switch-to-develop')
++ at click.option('--upgrade',flag_value=True, type=bool)
++def _switch_to_develop(upgrade=False):
++ "Switch frappe and erpnext to develop branch"
++ switch_to_develop(upgrade=upgrade)
++ print
++ print 'Switched to develop'
++ print 'Please run `bench update --patch` to be safe from any differences in database schema'
++
++ at click.command('switch-to-v4')
++ at click.option('--upgrade',flag_value=True, type=bool)
++def _switch_to_v4(upgrade=False):
++ "Switch frappe and erpnext to v4 branch"
++ switch_to_v4(upgrade=upgrade)
++ print
++ print 'Switched to v4'
++ print 'Please run `bench update --patch` to be safe from any differences in database schema'
++
+ @click.command('set-nginx-port')
+ @click.argument('site')
+ @click.argument('port', type=int)
+@@ -205,6 +324,27 @@ def set_nginx_port(site, port):
+ "Set nginx port for site"
+ _set_nginx_port(site, port)
+
++ at click.command('set-ssl-certificate')
++ at click.argument('site')
++ at click.argument('ssl-certificate-path')
++def _set_ssl_certificate(site, ssl_certificate_path):
++ "Set ssl certificate path for site"
++ set_ssl_certificate(site, ssl_certificate_path)
++
++ at click.command('set-ssl-key')
++ at click.argument('site')
++ at click.argument('ssl-certificate-key-path')
++def _set_ssl_certificate_key(site, ssl_certificate_key_path):
++ "Set ssl certificate private key path for site"
++ set_ssl_certificate_key(site, ssl_certificate_key_path)
++
++ at click.command('set-url-root')
++ at click.argument('site')
++ at click.argument('url-root')
++def set_url_root(site, url_root):
++ "Set url root for site"
++ _set_url_root(site, url_root)
++
+ @click.command('set-mariadb-host')
+ @click.argument('host')
+ def _set_mariadb_host(host):
+@@ -239,11 +379,13 @@ def _prime_wheel_cache():
+ @click.command('release')
+ @click.argument('app', type=click.Choice(['frappe', 'erpnext', 'shopping_cart']))
+ @click.argument('bump-type', type=click.Choice(['major', 'minor', 'patch']))
+-def _release(app, bump_type):
++ at click.option('--develop', default='develop')
++ at click.option('--master', default='master')
++def _release(app, bump_type, develop, master):
+ "Release app (internal to the Frappe team)"
+ from .release import release
+ repo = os.path.join('apps', app)
+- release(repo, bump_type)
++ release(repo, bump_type, develop, master)
+
+ ## Setup
+ @click.group()
+@@ -267,6 +409,11 @@ def setup_supervisor():
+ "generate config for supervisor"
+ generate_supervisor_config()
+
++ at click.command('redis-cache')
++def setup_redis_cache():
++ "generate config for redis cache"
++ generate_redis_config()
++
+ @click.command('production')
+ @click.argument('user')
+ def setup_production(user):
+@@ -305,6 +452,7 @@ def setup_config():
+ setup.add_command(setup_nginx)
+ setup.add_command(setup_sudoers)
+ setup.add_command(setup_supervisor)
++setup.add_command(setup_redis_cache)
+ setup.add_command(setup_auto_update)
+ setup.add_command(setup_dnsmasq)
+ setup.add_command(setup_backups)
+@@ -380,40 +528,32 @@ config.add_command(config_http_timeout)
+ def patch():
+ pass
+
+- at click.command('fix-perms')
+-def _fix_perms():
++ at click.command('fix-prod-perms')
++def _fix_prod_perms():
++ "Fix permissions if supervisor processes were run as root"
+ if os.path.exists("config/supervisor.conf"):
+ exec_cmd("supervisorctl stop frappe:")
+
+- "Fix permissions if supervisor processes were run as root"
+- files = [
+- "logs/web.error.log",
+- "logs/web.log",
+- "logs/workerbeat.error.log",
+- "logs/workerbeat.log",
+- "logs/worker.error.log",
+- "logs/worker.log",
+- "config/nginx.conf",
+- "config/supervisor.conf",
+- ]
+-
+- frappe_user = get_config().get('frappe_user')
+- if not frappe_user:
+- print "frappe user not set"
+- sys.exit(1)
+-
+- for path in files:
+- if os.path.exists(path):
+- uid = pwd.getpwnam(frappe_user).pw_uid
+- gid = grp.getgrnam(frappe_user).gr_gid
+- os.chown(path, uid, gid)
++ fix_prod_setup_perms()
+
+ if os.path.exists("config/supervisor.conf"):
+ exec_cmd("{bench} setup supervisor".format(bench=sys.argv[0]))
+ exec_cmd("supervisorctl reload")
+
+
+-patch.add_command(_fix_perms)
++ at click.command('fix-file-perms')
++def _fix_file_perms():
++ "Fix file permissions"
++ fix_file_perms()
++
++patch.add_command(_fix_file_perms)
++patch.add_command(_fix_prod_perms)
++
++
++ at click.command('download-translations')
++def _download_translations():
++ "Download latest translations"
++ download_translations_p()
+
+ #Bench commands
+
+@@ -427,12 +567,20 @@ bench.add_command(restart)
+ bench.add_command(config)
+ bench.add_command(start)
+ bench.add_command(set_nginx_port)
++bench.add_command(_set_ssl_certificate)
++bench.add_command(_set_ssl_certificate_key)
+ bench.add_command(_set_mariadb_host)
+ bench.add_command(set_default_site)
+ bench.add_command(migrate_3to4)
++bench.add_command(_switch_to_master)
++bench.add_command(_switch_to_develop)
++bench.add_command(_switch_to_v4)
+ bench.add_command(shell)
+ bench.add_command(_backup_all_sites)
+ bench.add_command(_backup_site)
+ bench.add_command(_prime_wheel_cache)
+ bench.add_command(_release)
+ bench.add_command(patch)
++bench.add_command(set_url_root)
++bench.add_command(retry_upgrade)
++bench.add_command(_download_translations)
+--- bench/config.py.orig 2014-11-19 06:36:44 UTC
++++ bench/config.py
+@@ -1,12 +1,27 @@
+ import os
+ import getpass
+ import json
++import subprocess
++import shutil
+ from jinja2 import Environment, PackageLoader
+-from .utils import get_sites, get_config, update_config
++from .utils import get_sites, get_config, update_config, get_redis_version
+
+ env = Environment(loader=PackageLoader('bench', 'templates'), trim_blocks=True)
+
++def write_config_file(bench, file_name, config):
++ config_path = os.path.join(bench, 'config')
++ file_path = os.path.join(config_path, file_name)
++ number = (len([path for path in os.listdir(config_path) if path.startswith(file_name)]) -1 ) or ''
++ if number:
++ number = '.' + str(number)
++ if os.path.exists(file_path):
++ shutil.move(file_path, file_path + '.save' + number)
++
++ with open(file_path, 'wb') as f:
++ f.write(config)
++
+ def generate_supervisor_config(bench='.', user=None):
++ from .app import get_current_frappe_version
+ template = env.get_template('supervisor.conf')
+ bench_dir = os.path.abspath(bench)
+ sites_dir = os.path.join(bench_dir, "sites")
+@@ -20,9 +35,11 @@ def generate_supervisor_config(bench='.'
+ "sites_dir": sites_dir,
+ "user": user,
+ "http_timeout": config.get("http_timeout", 120),
++ "redis_server": subprocess.check_output('which redis-server', shell=True).strip(),
++ "redis_config": os.path.join(bench_dir, 'config', 'redis.conf'),
++ "frappe_version": get_current_frappe_version()
+ })
+- with open("config/supervisor.conf", 'w') as f:
+- f.write(config)
++ write_config_file(bench, 'supervisor.conf', config)
+ update_config({'restart_supervisor_on_update': True})
+
+ def get_site_config(site, bench='.'):
+@@ -31,10 +48,16 @@ def get_site_config(site, bench='.'):
+
+ def get_sites_with_config(bench='.'):
+ sites = get_sites()
+- return [{
+- "name": site,
+- "port": get_site_config(site, bench=bench).get('nginx_port')
+- } for site in sites]
++ ret = []
++ for site in sites:
++ site_config = get_site_config(site, bench=bench)
++ ret.append({
++ "name": site,
++ "port": site_config.get('nginx_port'),
++ "ssl_certificate": site_config.get('ssl_certificate'),
++ "ssl_certificate_key": site_config.get('ssl_certificate_key')
++ })
++ return ret
+
+ def generate_nginx_config(bench='.'):
+ template = env.get_template('nginx.conf')
+@@ -59,5 +82,14 @@ def generate_nginx_config(bench='.'):
+ "dns_multitenant": get_config().get('dns_multitenant'),
+ "sites": sites
+ })
+- with open("config/nginx.conf", 'w') as f:
+- f.write(config)
++ write_config_file(bench, 'nginx.conf', config)
++
++def generate_redis_config(bench='.'):
++ template = env.get_template('redis.conf')
++ conf = {
++ "maxmemory": get_config().get('cache_maxmemory', '50'),
++ "port": get_config().get('redis_cache_port', '11311'),
++ "redis_version": get_redis_version()
++ }
++ config = template.render(**conf)
++ write_config_file(bench, 'redis.conf', config)
+--- bench/migrate_to_v5.py.orig 2015-07-31 10:19:27 UTC
++++ bench/migrate_to_v5.py
+@@ -0,0 +1,46 @@
++from .utils import exec_cmd, get_frappe, run_frappe_cmd
++from .release import get_current_version
++from .app import remove_from_appstxt
++import os
++import shutil
++import sys
++
++repos = ('frappe', 'erpnext')
++
++def migrate_to_v5(bench='.'):
++ validate_v4(bench=bench)
++ for repo in repos:
++ checkout_v5(repo, bench=bench)
++ remove_shopping_cart(bench=bench)
++ exec_cmd("{bench} update".format(bench=sys.argv[0]))
++
++def remove_shopping_cart(bench='.'):
++ archived_apps_dir = os.path.join(bench, 'archived_apps')
++ shopping_cart_dir = os.path.join(bench, 'apps', 'shopping_cart')
++
++ if not os.path.exists(shopping_cart_dir):
++ return
++
++ run_frappe_cmd('--site', 'all', 'remove-from-installed-apps', 'shopping_cart', bench=bench)
++ remove_from_appstxt('shopping_cart', bench=bench)
++ exec_cmd("{pip} --no-input uninstall -y shopping_cart".format(pip=os.path.join(bench, 'env', 'bin', 'pip')))
++
++ if not os.path.exists(archived_apps_dir):
++ os.mkdir(archived_apps_dir)
++ shutil.move(shopping_cart_dir, archived_apps_dir)
++
++def validate_v4(bench='.'):
++ for repo in repos:
++ path = os.path.join(bench, 'apps', repo)
++ if os.path.exists(path):
++ current_version = get_current_version(path)
++ if not current_version.startswith('4'):
++ raise Exception("{} is not on v4.x.x".format(repo))
++
++def checkout_v5(repo, bench='.'):
++ cwd = os.path.join(bench, 'apps', repo)
++ if os.path.exists(cwd):
++ exec_cmd("git fetch upstream", cwd=cwd)
++ exec_cmd("git checkout v5.0", cwd=cwd)
++ exec_cmd("git clean -df", cwd=cwd)
++
+--- bench/production_setup.py.orig 2014-11-19 06:36:44 UTC
++++ bench/production_setup.py
+@@ -1,17 +1,16 @@
+-from .utils import get_program, exec_cmd, get_cmd_output
++from .utils import get_program, exec_cmd, get_cmd_output, fix_prod_setup_perms
+ from .config import generate_nginx_config, generate_supervisor_config
+ from jinja2 import Environment, PackageLoader
+ import os
+ import shutil
+
+ def restart_service(service):
+- program = get_program(['systemctl', 'service'])
+- if not program:
++ if os.path.basename(get_program(['systemctl']) or '') == 'systemctl' and is_running_systemd():
++ exec_cmd("{prog} restart {service}".format(prog='systemctl', service=service))
++ elif os.path.basename(get_program(['service']) or '') == 'service':
++ exec_cmd("{prog} {service} restart ".format(prog='service', service=service))
++ else:
+ raise Exception, 'No service manager found'
+- elif os.path.basename(program) == 'systemctl':
+- exec_cmd("{prog} restart {service}".format(prog=program, service=service))
+- elif os.path.basename(program) == 'service':
+- exec_cmd("{prog} {service} restart ".format(prog=program, service=service))
+
+ def get_supervisor_confdir():
+ possiblities = ('/etc/supervisor/conf.d', '/etc/supervisor.d/', '/etc/supervisord/conf.d', '/etc/supervisord.d')
+@@ -30,6 +29,14 @@ def remove_default_nginx_configs():
+ def is_centos7():
+ return os.path.exists('/etc/redhat-release') and get_cmd_output("cat /etc/redhat-release | sed 's/Linux\ //g' | cut -d' ' -f3 | cut -d. -f1").strip() == '7'
+
++def is_running_systemd():
++ with open('/proc/1/comm') as f:
++ comm = f.read().strip()
++ if comm == "init":
++ return False
++ elif comm == "systemd":
++ return True
++ return False
+
+ def copy_default_nginx_config():
+ shutil.copy(os.path.join(os.path.dirname(__file__), 'templates', 'nginx_default.conf'), '/etc/nginx/nginx.conf')
+@@ -37,6 +44,7 @@ def copy_default_nginx_config():
+ def setup_production(user, bench='.'):
+ generate_supervisor_config(bench=bench, user=user)
+ generate_nginx_config(bench=bench)
++ fix_prod_setup_perms(frappe_user=user)
+ remove_default_nginx_configs()
+
+ if is_centos7():
+--- bench/release.py.orig 2014-11-19 06:36:44 UTC
++++ bench/release.py
+@@ -34,10 +34,10 @@ def create_release(repo_path, version, r
+ g.merge(master_branch)
+ return tag_name
+
+-def push_release(repo_path):
++def push_release(repo_path, develop_branch='develop', master_branch='master'):
+ repo = git.Repo(repo_path)
+ g = repo.git
+- print g.push('upstream', 'master:master', 'develop:develop', '--tags')
++ print g.push('upstream', '{master}:{master}'.format(master=master_branch), '{develop}:{develop}'.format(develop=develop_branch), '--tags')
+
+ def create_github_release(owner, repo, tag_name, log, gh_username=None, gh_password=None):
+ global github_username, github_password
+@@ -137,25 +137,40 @@ def get_current_version(repo):
+ contents)
+ return match.group(2)
+
+-def bump_repo(repo, bump_type):
+- update_branch(repo, 'master', remote='upstream')
+- update_branch(repo, 'develop', remote='upstream')
+- git.Repo(repo).git.checkout('develop')
+- current_version = get_current_version(repo)
+- new_version = get_bumped_version(current_version, bump_type)
+- set_version(repo, new_version)
+- return new_version
++def check_for_unmerged_changelog(repo):
++ current = os.path.join(repo, os.path.basename(repo), 'change_log', 'current')
++ if os.path.exists(current) and [f for f in os.listdir(current) if f != "readme.md"]:
++ raise Exception("Unmerged change log! in " + repo)
+
+-def bump(repo, bump_type):
++def bump_repo(repo, bump_type, develop='develop', master='master', remote='upstream'):
++ update_branch(repo, master, remote=remote)
++ update_branch(repo, develop, remote=remote)
++ git.Repo(repo).git.checkout(develop)
++ check_for_unmerged_changelog(repo)
++ current_version = get_current_version(repo)
++ new_version = get_bumped_version(current_version, bump_type)
++ set_version(repo, new_version)
++ return new_version
++
++def get_release_message(repo_path, develop_branch='develop', master_branch='master'):
++ repo = git.Repo(repo_path)
++ g = repo.git
++ return "* " + g.log('upstream/{master_branch}..upstream/{develop_branch}'.format(master_branch=master_branch, develop_branch=develop_branch), '--format=format:%s', '--no-merges').replace('\n', '\n* ')
++
++def bump(repo, bump_type, develop='develop', master='master', remote='upstream'):
+ assert bump_type in ['minor', 'major', 'patch']
+- new_version = bump_repo(repo, bump_type)
++ new_version = bump_repo(repo, bump_type, develop=develop, master=master, remote=remote)
++ message = get_release_message(repo, develop_branch=develop, master_branch=master)
++ print
++ print message
++ print
+ commit_changes(repo, new_version)
+- tag_name = create_release(repo, new_version)
+- push_release(repo)
+- create_github_release('frappe', repo, tag_name, '')
++ tag_name = create_release(repo, new_version, develop_branch=develop, master_branch=master)
++ push_release(repo, develop_branch=develop, master_branch=master)
++ create_github_release('frappe', repo, tag_name, message)
+ print 'Released {tag} for {repo}'.format(tag=tag_name, repo=repo)
+
+-def release(repo, bump_type):
++def release(repo, bump_type, develop, master):
+ if not get_config().get('release_bench'):
+ print 'bench not configured to release'
+ sys.exit(1)
+@@ -164,7 +179,7 @@ def release(repo, bump_type):
+ github_password = getpass.getpass()
+ r = requests.get('https://api.github.com/user', auth=HTTPBasicAuth(github_username, github_password))
+ r.raise_for_status()
+- bump(repo, bump_type)
++ bump(repo, bump_type, develop=develop, master=master)
+
+ if __name__ == "__main__":
+ main()
+--- bench/templates/nginx.conf.orig 2014-11-19 06:36:44 UTC
++++ bench/templates/nginx.conf
+@@ -5,15 +5,7 @@ upstream frappe {
+ server 127.0.0.1:8000 fail_timeout=0;
+ }
+
+-{% macro server_block(site, port=80, default=False, server_name=None, sites=None, dns_multitenant=False) -%}
+- server {
+- listen {{ site.port if not default and site.port else port }} {% if default %} default {% endif %};
+- client_max_body_size 4G;
+- {% if dns_multitenant and sites %}
+- server_name {% for site in sites %} {{ site.name }} {% endfor %};
+- {% else %}
+- server_name {{ site.name if not server_name else server_name }};
+- {% endif %}
++{% macro location_block(site, port=80, default=False, server_name=None, sites=None, dns_multitenant=False) -%}
+ keepalive_timeout 5;
+ sendfile on;
+ root {{ sites_dir }};
+@@ -34,30 +26,66 @@ upstream frappe {
+ location @magic {
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ {% if not dns_multitenant %}
+- proxy_set_header Host {{ site.name }};
+- {% else %}
+- proxy_set_header Host $host;
++ proxy_set_header X-Frappe-Site-Name {{ site.name }};
+ {% endif %}
++ proxy_set_header Host $host;
+ proxy_set_header X-Use-X-Accel-Redirect True;
+ proxy_read_timeout {{http_timeout}};
+ proxy_redirect off;
+ proxy_pass http://frappe;
+ }
++{%- endmacro %}
++
++{% macro server_name_block(site, default=False, server_name=None, sites=None, dns_multitenant=False) -%}
++ client_max_body_size 4G;
++ {% if dns_multitenant and sites %}
++ server_name {% for site in sites %} {{ site.name }} {% endfor %};
++ {% else %}
++ server_name {{ site.name if not server_name else server_name }};
++ {% endif %}
++{%- endmacro %}
++
++{% macro server_block_http(site, port=80, default=False, server_name=None, sites=None, dns_multitenant=False) -%}
++ server {
++ listen {{ site.port if not default and site.port else port }} {% if default %} default {% endif %};
++ {{ server_name_block(site, default=default, server_name=server_name, sites=sites, dns_multitenant=dns_multitenant) }}
++ {{ location_block(site, port=port, default=default, server_name=server_name, sites=sites, dns_multitenant=dns_multitenant) }}
++ }
++{%- endmacro %}
++
++{% macro server_block_https(site, port=443, default=False, server_name=None, sites=None, dns_multitenant=False) -%}
++ server {
++ listen {{ site.ssl_port if not default and site.ssl_port else port }} {% if default %} default {% endif %};
++ {{ server_name_block(site, default=default, server_name=server_name, sites=sites, dns_multitenant=dns_multitenant) }}
++
++ ssl on;
++ ssl_certificate {{ site.ssl_certificate }};
++ ssl_certificate_key {{ site.ssl_certificate_key }};
++ ssl_session_timeout 5m;
++ ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
++ ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
++ ssl_prefer_server_ciphers on;
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-ports-head
mailing list