2
0
mirror of https://github.com/frappe/bench.git synced 2024-11-17 02:25:16 +00:00

Merge branch 'develop' of github.com:frappe/bench into declarative-setup-bench

This commit is contained in:
Gavin D'souza 2021-04-20 10:56:33 +05:30
commit 1e56d04e94
35 changed files with 287 additions and 154 deletions

13
.github/semantic.yml vendored Normal file
View File

@ -0,0 +1,13 @@
# Always validate the PR title AND all the commits
titleAndCommits: true
# Allow use of Merge commits (eg on github: "Merge branch 'master' into feature/ride-unicorns")
# this is only relevant when using commitsOnly: true (or titleAndCommits: true)
allowMergeCommits: true
# Allow use of Revert commits (eg on github: "Revert "feat: ride unicorns"")
# this is only relevant when using commitsOnly: true (or titleAndCommits: true)
allowRevertCommits: true
# For allowed PR types: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json
# Tool Reference: https://github.com/zeke/semantic-pull-requests

28
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: Generate Semantic Release and publish on PyPI
on:
push:
branches:
- v5.x
jobs:
release:
name: Release
runs-on: ubuntu-18.04
steps:
- name: Checkout Entire Repository
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup Node.js v12
uses: actions/setup-node@v1
with:
node-version: 12
- name: Setup dependencies
run: |
npm install @semantic-release/git @semantic-release/exec --no-save
pip install wheel twine
- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }}
run: npx semantic-release

35
.releaserc Normal file
View File

@ -0,0 +1,35 @@
{
"branches": ["v5.x"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/exec", {
"prepareCmd": 'sed -ir "s/[0-9]*\.[0-9]*\.[0-9]*/${nextRelease.version}/" bench/__init__.py'
}
],
[
"@semantic-release/exec", {
"prepareCmd": "python setup.py bdist_wheel --universal"
}
],
[
"@semantic-release/git", {
"assets": ["bench/__init__.py"],
"message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}"
}
],
[
"@semantic-release/github", {
"assets": [
{"path": "dist/*"},
]
}
],
[
"@semantic-release/exec", {
"publishCmd": "python -m twine upload dist/* -u $PYPI_USERNAME -p $PYPI_PASSWORD"
}
]
]
}

View File

@ -13,7 +13,7 @@ Bench is a command-line utility that helps you to install, update, and manage mu
- [Production Setup](#docker-installation-for-production) - [Production Setup](#docker-installation-for-production)
- [Easy Install Script](#easy-install-script) - [Easy Install Script](#easy-install-script)
- [Manual Installation](#manual-installation) - [Manual Installation](#manual-installation)
- [Usage](#usage) - [Usage](#basic-usage)
- [Custom Bench commands](#custom-bench-commands) - [Custom Bench commands](#custom-bench-commands)
- [Bench Manager](#bench-manager) - [Bench Manager](#bench-manager)
- [Guides](#guides) - [Guides](#guides)

View File

@ -1,4 +1,4 @@
VERSION = "5.2.1" VERSION = "5.0.0-dev"
PROJECT_NAME = "frappe-bench" PROJECT_NAME = "frappe-bench"
FRAPPE_VERSION = None FRAPPE_VERSION = None

View File

@ -6,21 +6,15 @@ import json
import logging import logging
import os import os
import re import re
import shutil
import subprocess import subprocess
import sys import sys
# imports - third party imports # imports - third party imports
import click import click
import git
import requests
import semantic_version
from six.moves import reload_module
from setuptools.config import read_configuration from setuptools.config import read_configuration
# imports - module imports # imports - module imports
import bench import bench
from bench.config.common_site_config import get_config
from bench.utils import color, CommandFailedError, build_assets, check_git_for_shallow_clone, exec_cmd, get_cmd_output, get_frappe, restart_supervisor_processes, restart_systemd_processes, run_frappe_cmd from bench.utils import color, CommandFailedError, build_assets, check_git_for_shallow_clone, exec_cmd, get_cmd_output, get_frappe, restart_supervisor_processes, restart_systemd_processes, run_frappe_cmd
@ -92,6 +86,9 @@ def remove_from_excluded_apps_txt(app, bench_path='.'):
return write_excluded_apps_txt(apps, bench_path=bench_path) return write_excluded_apps_txt(apps, bench_path=bench_path)
def get_app(git_url, branch=None, bench_path='.', skip_assets=False, verbose=False, restart_bench=True, overwrite=False): def get_app(git_url, branch=None, bench_path='.', skip_assets=False, verbose=False, restart_bench=True, overwrite=False):
import requests
import shutil
if not os.path.exists(git_url): if not os.path.exists(git_url):
if not is_git_url(git_url): if not is_git_url(git_url):
orgs = ['frappe', 'erpnext'] orgs = ['frappe', 'erpnext']
@ -166,17 +163,13 @@ def new_app(app, bench_path='.'):
app = app.lower().replace(" ", "_").replace("-", "_") app = app.lower().replace(" ", "_").replace("-", "_")
logger.log('creating new app {}'.format(app)) logger.log('creating new app {}'.format(app))
apps = os.path.abspath(os.path.join(bench_path, 'apps')) apps = os.path.abspath(os.path.join(bench_path, 'apps'))
bench.set_frappe_version(bench_path=bench_path)
if bench.FRAPPE_VERSION == 4:
exec_cmd("{frappe} --make_app {apps} {app}".format(frappe=get_frappe(bench_path=bench_path),
apps=apps, app=app))
else:
run_frappe_cmd('make-app', apps, app, bench_path=bench_path) run_frappe_cmd('make-app', apps, app, bench_path=bench_path)
install_app(app, bench_path=bench_path) install_app(app, bench_path=bench_path)
def install_app(app, bench_path=".", verbose=False, no_cache=False, restart_bench=True, skip_assets=False): def install_app(app, bench_path=".", verbose=False, no_cache=False, restart_bench=True, skip_assets=False):
from bench.config.common_site_config import get_config
print('\n{0}Installing {1}{2}'.format(color.yellow, app, color.nc)) print('\n{0}Installing {1}{2}'.format(color.yellow, app, color.nc))
logger.log("installing {}".format(app)) logger.log("installing {}".format(app))
@ -205,6 +198,9 @@ def install_app(app, bench_path=".", verbose=False, no_cache=False, restart_benc
def remove_app(app, bench_path='.'): def remove_app(app, bench_path='.'):
import shutil
from bench.config.common_site_config import get_config
if app not in get_apps(bench_path): if app not in get_apps(bench_path):
print("No app named {0}".format(app)) print("No app named {0}".format(app))
sys.exit(1) sys.exit(1)
@ -232,6 +228,8 @@ def remove_app(app, bench_path='.'):
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.config.common_site_config import get_config
rebase = '--rebase' if get_config(bench_path).get('rebase_on_pull') else '' rebase = '--rebase' if get_config(bench_path).get('rebase_on_pull') else ''
apps = apps or get_apps(bench_path=bench_path) apps = apps or get_apps(bench_path=bench_path)
@ -380,6 +378,8 @@ def get_repo_dir(app, bench_path='.'):
return os.path.join(bench_path, 'apps', app) return os.path.join(bench_path, 'apps', app)
def switch_branch(branch, apps=None, bench_path='.', upgrade=False, check_upgrade=True): def switch_branch(branch, apps=None, bench_path='.', upgrade=False, check_upgrade=True):
import git
from six.moves import reload_module
from bench.utils import update_requirements, update_node_packages, backup_all_sites, patch_sites, build_assets, post_upgrade from bench.utils import update_requirements, update_node_packages, backup_all_sites, patch_sites, build_assets, post_upgrade
apps_dir = os.path.join(bench_path, 'apps') apps_dir = os.path.join(bench_path, 'apps')
version_upgrade = (False,) version_upgrade = (False,)
@ -447,6 +447,8 @@ def get_version_from_string(contents, field='__version__'):
return match.group(2) return match.group(2)
def get_major_version(version): def get_major_version(version):
import semantic_version
return semantic_version.Version(version).major return semantic_version.Version(version).major
def install_apps_from_path(path, bench_path='.'): def install_apps_from_path(path, bench_path='.'):
@ -455,6 +457,8 @@ def install_apps_from_path(path, bench_path='.'):
get_app(app['url'], branch=app.get('branch'), bench_path=bench_path, skip_assets=True) get_app(app['url'], branch=app.get('branch'), bench_path=bench_path, skip_assets=True)
def get_apps_json(path): def get_apps_json(path):
import requests
if path.startswith('http'): if path.startswith('http'):
r = requests.get(path) r = requests.get(path)
return r.json() return r.json()

View File

@ -30,10 +30,9 @@ def cli():
logger = setup_logging() logger = setup_logging()
logger.info(command) logger.info(command)
if sys.argv[1] not in ("src", ): if len(sys.argv) > 1 and sys.argv[1] not in ("src", ):
check_uid() check_uid()
change_uid() change_uid()
change_dir() change_dir()
if is_dist_editable(bench.PROJECT_NAME) and len(sys.argv) > 1 and sys.argv[1] != "src" and not get_config(".").get("developer_mode"): if is_dist_editable(bench.PROJECT_NAME) and len(sys.argv) > 1 and sys.argv[1] != "src" and not get_config(".").get("developer_mode"):

View File

@ -1,6 +1,3 @@
# imports - standard imports
import ast
# imports - module imports # imports - module imports
from bench.config.common_site_config import update_config, get_config, put_config from bench.config.common_site_config import update_config, get_config, put_config
@ -52,6 +49,8 @@ def config_http_timeout(seconds):
@click.command('set-common-config', help='Set value in common config') @click.command('set-common-config', help='Set value in common config')
@click.option('configs', '-c', '--config', multiple=True, type=(str, str)) @click.option('configs', '-c', '--config', multiple=True, type=(str, str))
def set_common_config(configs): def set_common_config(configs):
import ast
common_site_config = {} common_site_config = {}
for key, value in configs: for key, value in configs:
if value in ('true', 'false'): if value in ('true', 'false'):

View File

@ -8,11 +8,11 @@ import click
@click.option('--ignore-exist', is_flag = True, default = False, help = "Ignore if Bench instance exists.") @click.option('--ignore-exist', is_flag = True, default = False, help = "Ignore if Bench instance exists.")
@click.option('--apps_path', default=None, help="path to json files with apps to install after init") @click.option('--apps_path', default=None, help="path to json files with apps to install after init")
@click.option('--frappe-path', default=None, help="path to frappe repo") @click.option('--frappe-path', default=None, help="path to frappe repo")
@click.option('--frappe-branch', default=None, help="path to frappe repo") @click.option('--frappe-branch', default=None, help="Clone a particular branch of frappe")
@click.option('--clone-from', default=None, help="copy repos from path") @click.option('--clone-from', default=None, help="copy repos from path")
@click.option('--clone-without-update', is_flag=True, help="copy repos from path without update") @click.option('--clone-without-update', is_flag=True, help="copy repos from path without update")
@click.option('--no-procfile', is_flag=True, help="Pull changes in all the apps in bench") @click.option('--no-procfile', is_flag=True, help="Do not create a Procfile")
@click.option('--no-backups',is_flag=True, help="Run migrations for all sites in the bench") @click.option('--no-backups',is_flag=True, help="Do not set up automatic periodic backups for all sites on this bench")
@click.option('--skip-redis-config-generation', is_flag=True, help="Skip redis config generation if already specifying the common-site-config file") @click.option('--skip-redis-config-generation', is_flag=True, help="Skip redis config generation if already specifying the common-site-config file")
@click.option('--skip-assets',is_flag=True, default=False, help="Do not build assets") @click.option('--skip-assets',is_flag=True, default=False, help="Do not build assets")
@click.option('--verbose',is_flag=True, help="Verbose output during install") @click.option('--verbose',is_flag=True, help="Verbose output during install")

View File

@ -6,10 +6,7 @@ import sys
import click import click
# imports - module imports # imports - module imports
import bench.config.lets_encrypt
import bench.config.nginx
import bench.config.procfile import bench.config.procfile
import bench.config.production_setup
import bench.config.redis import bench.config.redis
import bench.config.site_config import bench.config.site_config
import bench.config.supervisor import bench.config.supervisor
@ -31,20 +28,25 @@ def setup_sudoers(user):
@click.command("nginx", help="Generate configuration files for NGINX") @click.command("nginx", help="Generate configuration files for NGINX")
@click.option("--yes", help="Yes to regeneration of nginx config file", default=False, is_flag=True) @click.option("--yes", help="Yes to regeneration of nginx config file", default=False, is_flag=True)
def setup_nginx(yes=False): def setup_nginx(yes=False):
import bench.config.nginx
bench.config.nginx.make_nginx_conf(bench_path=".", yes=yes) bench.config.nginx.make_nginx_conf(bench_path=".", yes=yes)
@click.command("reload-nginx", help="Checks NGINX config file and reloads service") @click.command("reload-nginx", help="Checks NGINX config file and reloads service")
def reload_nginx(): def reload_nginx():
import bench.config.production_setup
bench.config.production_setup.reload_nginx() bench.config.production_setup.reload_nginx()
@click.command("supervisor", help="Generate configuration for supervisor") @click.command("supervisor", help="Generate configuration for supervisor")
@click.option("--user", help="optional user argument") @click.option("--user", help="optional user argument")
@click.option("--yes", help="Yes to regeneration of supervisor config", is_flag=True, default=False) @click.option("--yes", help="Yes to regeneration of supervisor config", is_flag=True, default=False)
def setup_supervisor(user=None, yes=False): @click.option("--skip-redis", help="Skip redis configuration", is_flag=True, default=False)
def setup_supervisor(user=None, yes=False, skip_redis=False):
bench.config.supervisor.update_supervisord_config(user=user, yes=yes) bench.config.supervisor.update_supervisord_config(user=user, yes=yes)
bench.config.supervisor.generate_supervisor_config(bench_path=".", user=user, yes=yes) bench.config.supervisor.generate_supervisor_config(bench_path=".", user=user, yes=yes, skip_redis=skip_redis)
@click.command("redis", help="Generates configuration for Redis") @click.command("redis", help="Generates configuration for Redis")
@ -61,6 +63,8 @@ def setup_fonts():
@click.argument("user") @click.argument("user")
@click.option("--yes", help="Yes to regeneration config", is_flag=True, default=False) @click.option("--yes", help="Yes to regeneration config", is_flag=True, default=False)
def setup_production(user, yes=False): def setup_production(user, yes=False):
import bench.config.production_setup
bench.config.production_setup.setup_production(user=user, yes=yes) bench.config.production_setup.setup_production(user=user, yes=yes)
@ -103,6 +107,8 @@ def set_ssh_port(port, force=False):
@click.option("--custom-domain") @click.option("--custom-domain")
@click.option('-n', '--non-interactive', default=False, is_flag=True, help="Run command non-interactively. This flag restarts nginx and runs certbot non interactively. Shouldn't be used on 1'st attempt") @click.option('-n', '--non-interactive', default=False, is_flag=True, help="Run command non-interactively. This flag restarts nginx and runs certbot non interactively. Shouldn't be used on 1'st attempt")
def setup_letsencrypt(site, custom_domain, non_interactive): def setup_letsencrypt(site, custom_domain, non_interactive):
import bench.config.lets_encrypt
bench.config.lets_encrypt.setup_letsencrypt(site, custom_domain, bench_path=".", interactive=not non_interactive) bench.config.lets_encrypt.setup_letsencrypt(site, custom_domain, bench_path=".", interactive=not non_interactive)
@ -111,6 +117,8 @@ def setup_letsencrypt(site, custom_domain, non_interactive):
@click.option("--email") @click.option("--email")
@click.option("--exclude-base-domain", default=False, is_flag=True, help="SSL Certificate not applicable for base domain") @click.option("--exclude-base-domain", default=False, is_flag=True, help="SSL Certificate not applicable for base domain")
def setup_wildcard_ssl(domain, email, exclude_base_domain): def setup_wildcard_ssl(domain, email, exclude_base_domain):
import bench.config.lets_encrypt
bench.config.lets_encrypt.setup_wildcard_ssl(domain, email, bench_path=".", exclude_base_domain=exclude_base_domain) bench.config.lets_encrypt.setup_wildcard_ssl(domain, email, bench_path=".", exclude_base_domain=exclude_base_domain)
@ -253,8 +261,8 @@ def setup_roles(role, **kwargs):
@click.command("fail2ban", help="Setup fail2ban, an intrusion prevention software framework that protects computer servers from brute-force attacks") @click.command("fail2ban", help="Setup fail2ban, an intrusion prevention software framework that protects computer servers from brute-force attacks")
@click.option("--maxretry", default=6, help="Number of matches (i.e. value of the counter) which triggers ban action on the IP. Default is 6 seconds" ) @click.option("--maxretry", default=6, help="Number of matches (i.e. value of the counter) which triggers ban action on the IP. Default is 6 seconds" )
@click.option("--bantime", default=600, help="The counter is set to zero if no match is found within 'findtime' seconds. Default is 600 seconds") @click.option("--bantime", default=600, help="Duration (in seconds) for IP to be banned for. Negative number for 'permanent' ban. Default is 600 seconds")
@click.option("--findtime", default=600, help="Duration (in seconds) for IP to be banned for. Negative number for 'permanent' ban. Default is 600 seconds") @click.option("--findtime", default=600, help="The counter is set to zero if match found within 'findtime' seconds doesn't exceed 'maxretry'. Default is 600 seconds")
def setup_nginx_proxy_jail(**kwargs): def setup_nginx_proxy_jail(**kwargs):
run_playbook("roles/fail2ban/tasks/configure_nginx_jail.yml", extra_vars=kwargs) run_playbook("roles/fail2ban/tasks/configure_nginx_jail.yml", extra_vars=kwargs)

View File

@ -15,11 +15,12 @@ from bench.utils import post_upgrade, patch_sites, build_assets
@click.option('--restart-supervisor', is_flag=True, help="Restart supervisor processes after update") @click.option('--restart-supervisor', is_flag=True, help="Restart supervisor processes after update")
@click.option('--restart-systemd', is_flag=True, help="Restart systemd units after update") @click.option('--restart-systemd', is_flag=True, help="Restart systemd units after update")
@click.option('--no-backup', is_flag=True, help="If this flag is set, sites won't be backed up prior to updates. Note: This is not recommended in production.") @click.option('--no-backup', is_flag=True, help="If this flag is set, sites won't be backed up prior to updates. Note: This is not recommended in production.")
@click.option('--no-compile', is_flag=True, help="If set, Python bytecode won't be compiled before restarting the processes")
@click.option('--force', is_flag=True, help="Forces major version upgrades") @click.option('--force', is_flag=True, help="Forces major version upgrades")
@click.option('--reset', is_flag=True, help="Hard resets git branch's to their new states overriding any changes and overriding rebase on pull") @click.option('--reset', is_flag=True, help="Hard resets git branch's to their new states overriding any changes and overriding rebase on pull")
def update(pull, apps, patch, build, requirements, restart_supervisor, restart_systemd, no_backup, force, reset): def update(pull, apps, patch, build, requirements, restart_supervisor, restart_systemd, no_backup, no_compile, force, reset):
from bench.utils import update from bench.utils import update
update(pull=pull, apps=apps, patch=patch, build=build, requirements=requirements, restart_supervisor=restart_supervisor, restart_systemd=restart_systemd, backup=not no_backup, force=force, reset=reset) update(pull=pull, apps=apps, patch=patch, build=build, requirements=requirements, restart_supervisor=restart_supervisor, restart_systemd=restart_systemd, backup=not no_backup, compile=not no_compile, force=force, reset=reset)
@click.command('retry-upgrade', help="Retry a failed upgrade") @click.command('retry-upgrade', help="Retry a failed upgrade")

View File

@ -1,6 +1,6 @@
"""Module for setting up system and respective bench configurations""" """Module for setting up system and respective bench configurations"""
# imports - third party imports
from jinja2 import Environment, PackageLoader
env = Environment(loader=PackageLoader('bench.config')) def env():
from jinja2 import Environment, PackageLoader
return Environment(loader=PackageLoader('bench.config'))

View File

@ -1,11 +1,8 @@
# imports - standard imports # imports - standard imports
import getpass import getpass
import json import json
import multiprocessing
import os import os
# imports - third party imports
from six.moves.urllib.parse import urlparse
default_config = { default_config = {
@ -54,8 +51,10 @@ def get_config_path(bench_path):
def get_gunicorn_workers(): def get_gunicorn_workers():
'''This function will return the maximum workers that can be started depending upon '''This function will return the maximum workers that can be started depending upon
number of cpu's present on the machine''' number of cpu's present on the machine'''
import multiprocessing
return { return {
"gunicorn_workers": multiprocessing.cpu_count() "gunicorn_workers": multiprocessing.cpu_count() * 2 + 1
} }
def update_config_for_frappe(config, bench_path): def update_config_for_frappe(config, bench_path):
@ -73,6 +72,8 @@ def update_config_for_frappe(config, bench_path):
# TODO Optionally we need to add the host or domain name in case dns_multitenant is false # TODO Optionally we need to add the host or domain name in case dns_multitenant is false
def make_ports(bench_path): def make_ports(bench_path):
from six.moves.urllib.parse import urlparse
benches_path = os.path.dirname(os.path.abspath(bench_path)) benches_path = os.path.dirname(os.path.abspath(bench_path))
default_ports = { default_ports = {

View File

@ -3,8 +3,6 @@ import os
# imports - third party imports # imports - third party imports
import click import click
from crontab import CronTab
from six.moves.urllib.request import urlretrieve
# imports - module imports # imports - module imports
import bench import bench
@ -48,7 +46,7 @@ def setup_letsencrypt(site, custom_domain, bench_path, interactive):
def create_config(site, custom_domain): def create_config(site, custom_domain):
config = bench.config.env.get_template('letsencrypt.cfg').render(domain=custom_domain or site) config = bench.config.env().get_template('letsencrypt.cfg').render(domain=custom_domain or site)
config_path = '/etc/letsencrypt/configs/{site}.cfg'.format(site=custom_domain or site) config_path = '/etc/letsencrypt/configs/{site}.cfg'.format(site=custom_domain or site)
create_dir_if_missing(config_path) create_dir_if_missing(config_path)
@ -86,6 +84,8 @@ def run_certbot_and_setup_ssl(site, custom_domain, bench_path, interactive=True)
def setup_crontab(): def setup_crontab():
from crontab import CronTab
job_command = '/opt/certbot-auto renew -a nginx --post-hook "systemctl reload nginx"' job_command = '/opt/certbot-auto renew -a nginx --post-hook "systemctl reload nginx"'
job_comment = 'Renew lets-encrypt every month' job_comment = 'Renew lets-encrypt every month'
print("Setting Up cron job to {0}".format(job_comment)) print("Setting Up cron job to {0}".format(job_comment))
@ -106,6 +106,8 @@ def create_dir_if_missing(path):
def get_certbot(): def get_certbot():
from six.moves.urllib.request import urlretrieve
certbot_path = get_certbot_path() certbot_path = get_certbot_path()
create_dir_if_missing(certbot_path) create_dir_if_missing(certbot_path)

View File

@ -20,7 +20,7 @@ def make_nginx_conf(bench_path, yes=False):
if not click.confirm('nginx.conf already exists and this will overwrite it. Do you want to continue?'): if not click.confirm('nginx.conf already exists and this will overwrite it. Do you want to continue?'):
return return
template = bench.config.env.get_template('nginx.conf') template = bench.config.env().get_template('nginx.conf')
bench_path = os.path.abspath(bench_path) bench_path = os.path.abspath(bench_path)
sites_path = os.path.join(bench_path, "sites") sites_path = os.path.join(bench_path, "sites")
@ -59,7 +59,7 @@ def make_bench_manager_nginx_conf(bench_path, yes=False, port=23624, domain=None
from bench.config.site_config import get_site_config from bench.config.site_config import get_site_config
from bench.config.common_site_config import get_config from bench.config.common_site_config import get_config
template = bench.config.env.get_template('bench_manager_nginx.conf') template = bench.config.env().get_template('bench_manager_nginx.conf')
bench_path = os.path.abspath(bench_path) bench_path = os.path.abspath(bench_path)
sites_path = os.path.join(bench_path, "sites") sites_path = os.path.join(bench_path, "sites")

View File

@ -18,7 +18,7 @@ def setup_procfile(bench_path, yes=False, skip_redis=False):
click.confirm('A Procfile already exists and this will overwrite it. Do you want to continue?', click.confirm('A Procfile already exists and this will overwrite it. Do you want to continue?',
abort=True) abort=True)
procfile = bench.config.env.get_template('Procfile').render( procfile = bench.config.env().get_template('Procfile').render(
node=find_executable("node") or find_executable("nodejs"), node=find_executable("node") or find_executable("nodejs"),
use_rq=use_rq(bench_path), use_rq=use_rq(bench_path),
webserver_port=config.get('webserver_port'), webserver_port=config.get('webserver_port'),

View File

@ -3,16 +3,14 @@ import os
import re import re
import subprocess import subprocess
# imports - third party imports
import semantic_version
from six.moves.urllib.parse import urlparse
# imports - module imports # imports - module imports
import bench import bench
from bench.config.common_site_config import get_config from bench.config.common_site_config import get_config
def generate_config(bench_path): def generate_config(bench_path):
from six.moves.urllib.parse import urlparse
config = get_config(bench_path) config = get_config(bench_path)
ports = {} ports = {}
@ -52,7 +50,7 @@ def generate_config(bench_path):
os.makedirs(pid_path) os.makedirs(pid_path)
def write_redis_config(template_name, context, bench_path): def write_redis_config(template_name, context, bench_path):
template = bench.config.env.get_template(template_name) template = bench.config.env().get_template(template_name)
if "pid_path" not in context: if "pid_path" not in context:
context["pid_path"] = os.path.abspath(os.path.join(bench_path, "config", "pids")) context["pid_path"] = os.path.abspath(os.path.join(bench_path, "config", "pids"))
@ -61,6 +59,8 @@ def write_redis_config(template_name, context, bench_path):
f.write(template.render(**context)) f.write(template.render(**context))
def get_redis_version(): def get_redis_version():
import semantic_version
version_string = subprocess.check_output('redis-server --version', shell=True) version_string = subprocess.check_output('redis-server --version', shell=True)
version_string = version_string.decode('utf-8').strip() version_string = version_string.decode('utf-8').strip()
# extract version number from string # extract version number from string

View File

@ -4,7 +4,6 @@ import os
from collections import defaultdict from collections import defaultdict
# imports - module imports # imports - module imports
from bench.config.nginx import make_nginx_conf
from bench.utils import get_sites from bench.utils import get_sites
@ -35,6 +34,8 @@ def set_ssl_certificate_key(site, ssl_certificate_key, bench_path='.', gen_confi
set_site_config_nginx_property(site, {"ssl_certificate_key": ssl_certificate_key}, bench_path=bench_path, gen_config=gen_config) set_site_config_nginx_property(site, {"ssl_certificate_key": ssl_certificate_key}, bench_path=bench_path, gen_config=gen_config)
def set_site_config_nginx_property(site, config, bench_path='.', gen_config=True): def set_site_config_nginx_property(site, config, bench_path='.', gen_config=True):
from bench.config.nginx import make_nginx_conf
if site not in get_sites(bench_path=bench_path): if site not in get_sites(bench_path=bench_path):
raise Exception("No such site") raise Exception("No such site")
update_site_config(site, config, bench_path=bench_path) update_site_config(site, config, bench_path=bench_path)

View File

@ -5,24 +5,23 @@ import os
# imports - module imports # imports - module imports
import bench import bench
from bench.app import get_current_frappe_version, use_rq from bench.app import use_rq
from bench.utils import get_bench_name, find_executable from bench.utils import get_bench_name, find_executable
from bench.config.common_site_config import get_config, update_config, get_gunicorn_workers from bench.config.common_site_config import get_config, update_config, get_gunicorn_workers
# imports - third party imports # imports - third party imports
import click import click
from six.moves import configparser
logger = logging.getLogger(bench.PROJECT_NAME) logger = logging.getLogger(bench.PROJECT_NAME)
def generate_supervisor_config(bench_path, user=None, yes=False): def generate_supervisor_config(bench_path, user=None, yes=False, skip_redis=False):
"""Generate supervisor config for respective bench path""" """Generate supervisor config for respective bench path"""
if not user: if not user:
user = getpass.getuser() user = getpass.getuser()
template = bench.config.env.get_template('supervisor.conf') template = bench.config.env().get_template('supervisor.conf')
config = get_config(bench_path=bench_path) config = get_config(bench_path=bench_path)
bench_dir = os.path.abspath(bench_path) bench_dir = os.path.abspath(bench_path)
@ -30,7 +29,6 @@ def generate_supervisor_config(bench_path, user=None, yes=False):
"bench_dir": bench_dir, "bench_dir": bench_dir,
"sites_dir": os.path.join(bench_dir, 'sites'), "sites_dir": os.path.join(bench_dir, 'sites'),
"user": user, "user": user,
"frappe_version": get_current_frappe_version(bench_path),
"use_rq": use_rq(bench_path), "use_rq": use_rq(bench_path),
"http_timeout": config.get("http_timeout", 120), "http_timeout": config.get("http_timeout", 120),
"redis_server": find_executable('redis-server'), "redis_server": find_executable('redis-server'),
@ -42,7 +40,8 @@ def generate_supervisor_config(bench_path, user=None, yes=False):
"gunicorn_workers": config.get('gunicorn_workers', get_gunicorn_workers()["gunicorn_workers"]), "gunicorn_workers": config.get('gunicorn_workers', get_gunicorn_workers()["gunicorn_workers"]),
"bench_name": get_bench_name(bench_path), "bench_name": get_bench_name(bench_path),
"background_workers": config.get('background_workers') or 1, "background_workers": config.get('background_workers') or 1,
"bench_cmd": find_executable('bench') "bench_cmd": find_executable('bench'),
"skip_redis": skip_redis,
}) })
conf_path = os.path.join(bench_path, 'config', 'supervisor.conf') conf_path = os.path.join(bench_path, 'config', 'supervisor.conf')
@ -68,6 +67,8 @@ def get_supervisord_conf():
def update_supervisord_config(user=None, yes=False): def update_supervisord_config(user=None, yes=False):
"""From bench v5.x, we're moving to supervisor running as user""" """From bench v5.x, we're moving to supervisor running as user"""
from six.moves import configparser
from bench.config.production_setup import service from bench.config.production_setup import service
if not user: if not user:

View File

@ -7,7 +7,7 @@ import click
# imports - module imports # imports - module imports
import bench import bench
from bench.app import get_current_frappe_version, use_rq from bench.app import use_rq
from bench.config.common_site_config import get_config, get_gunicorn_workers, update_config from bench.config.common_site_config import get_config, get_gunicorn_workers, update_config
from bench.utils import exec_cmd, find_executable, get_bench_name from bench.utils import exec_cmd, find_executable, get_bench_name
@ -51,7 +51,6 @@ def generate_systemd_config(bench_path, user=None, yes=False,
"bench_dir": bench_dir, "bench_dir": bench_dir,
"sites_dir": os.path.join(bench_dir, 'sites'), "sites_dir": os.path.join(bench_dir, 'sites'),
"user": user, "user": user,
"frappe_version": get_current_frappe_version(bench_path),
"use_rq": use_rq(bench_path), "use_rq": use_rq(bench_path),
"http_timeout": config.get("http_timeout", 120), "http_timeout": config.get("http_timeout", 120),
"redis_server": find_executable('redis-server'), "redis_server": find_executable('redis-server'),
@ -85,7 +84,7 @@ def setup_systemd_directory(bench_path):
def setup_main_config(bench_info, bench_path): def setup_main_config(bench_info, bench_path):
# Main config # Main config
bench_template = bench.config.env.get_template('systemd/frappe-bench.target') bench_template = bench.config.env().get_template('systemd/frappe-bench.target')
bench_config = bench_template.render(**bench_info) bench_config = bench_template.render(**bench_info)
bench_config_path = os.path.join(bench_path, 'config', 'systemd' , bench_info.get("bench_name") + '.target') bench_config_path = os.path.join(bench_path, 'config', 'systemd' , bench_info.get("bench_name") + '.target')
@ -94,11 +93,11 @@ def setup_main_config(bench_info, bench_path):
def setup_workers_config(bench_info, bench_path): def setup_workers_config(bench_info, bench_path):
# Worker Group # Worker Group
bench_workers_target_template = bench.config.env.get_template('systemd/frappe-bench-workers.target') bench_workers_target_template = bench.config.env().get_template('systemd/frappe-bench-workers.target')
bench_default_worker_template = bench.config.env.get_template('systemd/frappe-bench-frappe-default-worker.service') bench_default_worker_template = bench.config.env().get_template('systemd/frappe-bench-frappe-default-worker.service')
bench_short_worker_template = bench.config.env.get_template('systemd/frappe-bench-frappe-short-worker.service') bench_short_worker_template = bench.config.env().get_template('systemd/frappe-bench-frappe-short-worker.service')
bench_long_worker_template = bench.config.env.get_template('systemd/frappe-bench-frappe-long-worker.service') bench_long_worker_template = bench.config.env().get_template('systemd/frappe-bench-frappe-long-worker.service')
bench_schedule_worker_template = bench.config.env.get_template('systemd/frappe-bench-frappe-schedule.service') bench_schedule_worker_template = bench.config.env().get_template('systemd/frappe-bench-frappe-schedule.service')
bench_workers_target_config = bench_workers_target_template.render(**bench_info) bench_workers_target_config = bench_workers_target_template.render(**bench_info)
bench_default_worker_config = bench_default_worker_template.render(**bench_info) bench_default_worker_config = bench_default_worker_template.render(**bench_info)
@ -129,9 +128,9 @@ def setup_workers_config(bench_info, bench_path):
def setup_web_config(bench_info, bench_path): def setup_web_config(bench_info, bench_path):
# Web Group # Web Group
bench_web_target_template = bench.config.env.get_template('systemd/frappe-bench-web.target') bench_web_target_template = bench.config.env().get_template('systemd/frappe-bench-web.target')
bench_web_service_template = bench.config.env.get_template('systemd/frappe-bench-frappe-web.service') bench_web_service_template = bench.config.env().get_template('systemd/frappe-bench-frappe-web.service')
bench_node_socketio_template = bench.config.env.get_template('systemd/frappe-bench-node-socketio.service') bench_node_socketio_template = bench.config.env().get_template('systemd/frappe-bench-node-socketio.service')
bench_web_target_config = bench_web_target_template.render(**bench_info) bench_web_target_config = bench_web_target_template.render(**bench_info)
bench_web_service_config = bench_web_service_template.render(**bench_info) bench_web_service_config = bench_web_service_template.render(**bench_info)
@ -152,10 +151,10 @@ def setup_web_config(bench_info, bench_path):
def setup_redis_config(bench_info, bench_path): def setup_redis_config(bench_info, bench_path):
# Redis Group # Redis Group
bench_redis_target_template = bench.config.env.get_template('systemd/frappe-bench-redis.target') bench_redis_target_template = bench.config.env().get_template('systemd/frappe-bench-redis.target')
bench_redis_cache_template = bench.config.env.get_template('systemd/frappe-bench-redis-cache.service') bench_redis_cache_template = bench.config.env().get_template('systemd/frappe-bench-redis-cache.service')
bench_redis_queue_template = bench.config.env.get_template('systemd/frappe-bench-redis-queue.service') bench_redis_queue_template = bench.config.env().get_template('systemd/frappe-bench-redis-queue.service')
bench_redis_socketio_template = bench.config.env.get_template('systemd/frappe-bench-redis-socketio.service') bench_redis_socketio_template = bench.config.env().get_template('systemd/frappe-bench-redis-socketio.service')
bench_redis_target_config = bench_redis_target_template.render(**bench_info) bench_redis_target_config = bench_redis_target_template.render(**bench_info)
bench_redis_cache_config = bench_redis_cache_template.render(**bench_info) bench_redis_cache_config = bench_redis_cache_template.render(**bench_info)

View File

@ -30,6 +30,10 @@ server {
limit_conn per_host_{{ bench_name_hash }} 8; limit_conn per_host_{{ bench_name_hash }} 8;
{% endif %} {% endif %}
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
{% if ssl_certificate and ssl_certificate_key %} {% if ssl_certificate and ssl_certificate_key %}
ssl on; ssl on;
ssl_certificate {{ ssl_certificate }}; ssl_certificate {{ ssl_certificate }};

View File

@ -114,6 +114,7 @@ killasgroup=true
{% endif %} {% endif %}
{% if not skip_redis %}
[program:{{ bench_name }}-redis-cache] [program:{{ bench_name }}-redis-cache]
command={{ redis_server }} {{ redis_cache_config }} command={{ redis_server }} {{ redis_cache_config }}
priority=1 priority=1
@ -133,8 +134,9 @@ stdout_logfile={{ bench_dir }}/logs/redis-queue.log
stderr_logfile={{ bench_dir }}/logs/redis-queue.error.log stderr_logfile={{ bench_dir }}/logs/redis-queue.error.log
user={{ user }} user={{ user }}
directory={{ sites_dir }} directory={{ sites_dir }}
{% endif %}
{% if frappe_version > 5 %} {% if not skip_redis %}
[program:{{ bench_name }}-redis-socketio] [program:{{ bench_name }}-redis-socketio]
command={{ redis_server }} {{ redis_socketio_config }} command={{ redis_server }} {{ redis_socketio_config }}
priority=1 priority=1
@ -144,6 +146,7 @@ stdout_logfile={{ bench_dir }}/logs/redis-socketio.log
stderr_logfile={{ bench_dir }}/logs/redis-socketio.error.log stderr_logfile={{ bench_dir }}/logs/redis-socketio.error.log
user={{ user }} user={{ user }}
directory={{ sites_dir }} directory={{ sites_dir }}
{% endif %}
{% if node %} {% if node %}
[program:{{ bench_name }}-node-socketio] [program:{{ bench_name }}-node-socketio]
@ -157,8 +160,6 @@ user={{ user }}
directory={{ bench_dir }} directory={{ bench_dir }}
{% endif %} {% endif %}
{% endif %}
[group:{{ bench_name }}-web] [group:{{ bench_name }}-web]
programs={{ bench_name }}-frappe-web {%- if node -%} ,{{ bench_name }}-node-socketio {%- endif%} programs={{ bench_name }}-frappe-web {%- if node -%} ,{{ bench_name }}-node-socketio {%- endif%}
@ -174,5 +175,7 @@ programs={{ bench_name }}-frappe-workerbeat,{{ bench_name }}-frappe-worker,{{ be
{% endif %} {% endif %}
{% if not skip_redis %}
[group:{{ bench_name }}-redis] [group:{{ bench_name }}-redis]
programs={{ bench_name }}-redis-cache,{{ bench_name }}-redis-queue {%- if frappe_version > 5 -%} ,{{ bench_name }}-redis-socketio {%- endif %} programs={{ bench_name }}-redis-cache,{{ bench_name }}-redis-queue,{{ bench_name }}-redis-socketio
{% endif %}

View File

@ -11,7 +11,7 @@
- name: Set home folder perms - name: Set home folder perms
file: file:
path: '/home/{{ frappe_user }}' path: '{{ user_directory }}'
mode: 'o+rx' mode: 'o+rx'
owner: '{{ frappe_user }}' owner: '{{ frappe_user }}'
group: '{{ frappe_user }}' group: '{{ frappe_user }}'

View File

@ -33,7 +33,7 @@
- name: Fix permissions - name: Fix permissions
become_user: root become_user: root
command: chown {{ frappe_user }} -R /home/{{ frappe_user }} command: chown {{ frappe_user }} -R {{ user_directory }}
- name: python3 bench init for develop - name: python3 bench init for develop
command: bench init {{ bench_path }} --frappe-path {{ frappe_repo_url }} --frappe-branch {{ frappe_branch }} --python {{ python }} command: bench init {{ bench_path }} --frappe-path {{ frappe_repo_url }} --frappe-branch {{ frappe_branch }} --python {{ python }}
@ -77,6 +77,6 @@
# Setup Bench for production environment # Setup Bench for production environment
- include_tasks: setup_bench_production.yml - include_tasks: setup_bench_production.yml
vars: vars:
bench_path: "/home/{{ frappe_user }}/{{ bench_name }}" bench_path: "{{ user_directory }}/{{ bench_name }}"
when: not run_travis and production when: not run_travis and production
... ...

View File

@ -13,16 +13,17 @@
- name: Check whether the site already exists - name: Check whether the site already exists
stat: path="{{ bench_path }}/sites/{{ site }}" stat: path="{{ bench_path }}/sites/{{ site }}"
register: site_folder register: site_folder
when: not without_site
- name: Create a new site - name: Create a new site
command: "bench new-site {{ site }} --admin-password '{{ admin_password }}' --mariadb-root-password '{{ mysql_root_password }}'" command: "bench new-site {{ site }} --admin-password '{{ admin_password }}' --mariadb-root-password '{{ mysql_root_password }}'"
args: args:
chdir: "{{ bench_path }}" chdir: "{{ bench_path }}"
when: not site_folder.stat.exists when: not without_site and not site_folder.stat.exists
- name: Install ERPNext to default site - name: Install ERPNext to default site
command: "bench --site {{ site }} install-app erpnext" command: "bench --site {{ site }} install-app erpnext"
args: args:
chdir: "{{ bench_path }}" chdir: "{{ bench_path }}"
when: not without_erpnext when: not without_site and not without_erpnext
... ...

View File

@ -1,7 +1,7 @@
--- ---
- name: insert/update inputrc for history - name: insert/update inputrc for history
blockinfile: blockinfile:
dest: "/home/{{ frappe_user }}/.inputrc" dest: "{{ user_directory }}/.inputrc"
create: yes create: yes
block: | block: |
## arrow up ## arrow up

View File

@ -48,6 +48,10 @@
[mysqld] [mysqld]
pid-file = /var/run/mysqld/mysqld.pid pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock socket = /var/run/mysqld/mysqld.sock
# setting appeared inside mysql but overwritten by mariadb inside mariadb.conf.d/xx-server.cnf valued as utf8mb4_general_ci
collation-server = utf8mb4_unicode_ci
create: yes create: yes
become: yes become: yes
become_user: root become_user: root

View File

@ -24,7 +24,14 @@
get_url: get_url:
url: https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.5/wkhtmltox_0.12.5-1.focal_amd64.deb url: https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.5/wkhtmltox_0.12.5-1.focal_amd64.deb
dest: /tmp/wkhtmltox.deb dest: /tmp/wkhtmltox.deb
when: ansible_distribution == 'Ubuntu' and ansible_distribution_major_version == '20' when: ansible_distribution == 'Ubuntu' and ansible_distribution_major_version == '20' and ansible_architecture != 'aarch64'
- name: download wkthmltox Ubuntu 20 arm64
get_url:
# wkhtmltox supports arm64 starting from 0.12.6
url: https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_arm64.deb
dest: /tmp/wkhtmltox.deb
when: ansible_distribution == 'Ubuntu' and ansible_distribution_major_version == '20' and ansible_architecture == 'aarch64'
- name: download wkthmltox Ubuntu 18 - name: download wkthmltox Ubuntu 18
get_url: get_url:

View File

@ -40,8 +40,8 @@
- name: setup bench and dev environment - name: setup bench and dev environment
hosts: localhost hosts: localhost
vars: vars:
bench_repo_path: "/home/{{ frappe_user }}/.bench" bench_repo_path: "{{ user_directory }}/.bench"
bench_path: "/home/{{ frappe_user }}/{{ bench_name }}" bench_path: "{{ user_directory }}/{{ bench_name }}"
roles: roles:
# setup frappe-bench # setup frappe-bench
- { role: bench, tags: "bench", when: not run_travis and not without_bench_setup } - { role: bench, tags: "bench", when: not run_travis and not without_bench_setup }

View File

@ -4,11 +4,8 @@ import os
import sys import sys
import semantic_version import semantic_version
import git import git
import requests
import getpass import getpass
import re import re
from requests.auth import HTTPBasicAuth
import requests.exceptions
from time import sleep from time import sleep
from .config.common_site_config import get_config from .config.common_site_config import get_config
import click import click
@ -47,6 +44,9 @@ def release(bench_path, app, bump_type, from_branch, to_branch,
repo_name=repo_name, remote=remote, frontport=frontport) repo_name=repo_name, remote=remote, frontport=frontport)
def validate(bench_path, config): def validate(bench_path, config):
import requests
from requests.auth import HTTPBasicAuth
global github_username, github_password global github_username, github_password
github_username = config.get('github_username') github_username = config.get('github_username')
@ -306,6 +306,9 @@ def push_release(repo_path, from_branch, to_branch, remote='upstream'):
def create_github_release(repo_path, tag_name, message, remote='upstream', owner='frappe', repo_name=None, def create_github_release(repo_path, tag_name, message, remote='upstream', owner='frappe', repo_name=None,
gh_username=None, gh_password=None, prerelease=False): gh_username=None, gh_password=None, prerelease=False):
import requests
import requests.exceptions
from requests.auth import HTTPBasicAuth
print('creating release on github') print('creating release on github')

View File

@ -9,6 +9,7 @@ import git
# imports - module imports # imports - module imports
import bench import bench
import bench.cli
import bench.utils import bench.utils
from bench.release import get_bumped_version from bench.release import get_bumped_version
from bench.tests.test_base import FRAPPE_BRANCH, TestBenchBase from bench.tests.test_base import FRAPPE_BRANCH, TestBenchBase
@ -27,6 +28,10 @@ class TestBenchInit(TestBenchBase):
self.assertEqual( get_bumped_version('11.0.5-beta.22', 'prerelease'), '11.0.5-beta.23' ) self.assertEqual( get_bumped_version('11.0.5-beta.22', 'prerelease'), '11.0.5-beta.23' )
def test_utils(self):
self.assertEqual(subprocess.call("bench"), 0)
def test_init(self, bench_name="test-bench", **kwargs): def test_init(self, bench_name="test-bench", **kwargs):
self.init_bench(bench_name, **kwargs) self.init_bench(bench_name, **kwargs)
self.assert_folders(bench_name) self.assert_folders(bench_name)

View File

@ -2,18 +2,17 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# imports - standard imports # imports - standard imports
import compileall
import errno import errno
import glob import glob
import grp import grp
import itertools import itertools
import json import json
import logging import logging
import multiprocessing
import os import os
import pwd import pwd
import re import re
import select import select
import shutil
import site import site
import subprocess import subprocess
import sys import sys
@ -22,11 +21,7 @@ from distutils.spawn import find_executable
# imports - third party imports # imports - third party imports
import click import click
from crontab import CronTab
import requests
from semantic_version import Version
from six import iteritems from six import iteritems
from six.moves.urllib.parse import urlparse
# imports - module imports # imports - module imports
import bench import bench
@ -92,6 +87,12 @@ def safe_decode(string, encoding = 'utf-8'):
def check_latest_version(): def check_latest_version():
if bench.VERSION.endswith("dev"):
return
import requests
from semantic_version import Version
try: try:
pypi_request = requests.get("https://pypi.org/pypi/frappe-bench/json") pypi_request = requests.get("https://pypi.org/pypi/frappe-bench/json")
except Exception: except Exception:
@ -162,9 +163,6 @@ def init(path, apps_path=None, no_procfile=False, no_backups=False,
if apps_path: if apps_path:
install_apps_from_path(apps_path, bench_path=path) install_apps_from_path(apps_path, bench_path=path)
bench.set_frappe_version(bench_path=path)
if bench.FRAPPE_VERSION > 5:
if not skip_assets: if not skip_assets:
update_node_packages(bench_path=path) update_node_packages(bench_path=path)
@ -183,8 +181,8 @@ def init(path, apps_path=None, no_procfile=False, no_backups=False,
copy_patches_txt(path) copy_patches_txt(path)
def update(pull=False, apps=None, patch=False, build=False, requirements=False, backup=True, force=False, reset=False, def update(pull=False, apps=None, patch=False, build=False, requirements=False, backup=True, compile=True,
restart_supervisor=False, restart_systemd=False): force=False, reset=False, restart_supervisor=False, restart_systemd=False):
"""command: bench update""" """command: bench update"""
from bench import patches from bench import patches
from bench.app import is_version_upgrade, pull_apps, validate_branch from bench.app import is_version_upgrade, pull_apps, validate_branch
@ -218,7 +216,6 @@ def update(pull=False, apps=None, patch=False, build=False, requirements=False,
if version_upgrade[0] or (not version_upgrade[0] and force): if version_upgrade[0] or (not version_upgrade[0] and force):
validate_upgrade(version_upgrade[1], version_upgrade[2], bench_path=bench_path) validate_upgrade(version_upgrade[1], version_upgrade[2], bench_path=bench_path)
conf.update({ "maintenance_mode": 1, "pause_scheduler": 1 }) conf.update({ "maintenance_mode": 1, "pause_scheduler": 1 })
update_config(conf, bench_path=bench_path) update_config(conf, bench_path=bench_path)
@ -246,6 +243,10 @@ def update(pull=False, apps=None, patch=False, build=False, requirements=False,
if version_upgrade[0] or (not version_upgrade[0] and force): if version_upgrade[0] or (not version_upgrade[0] and force):
post_upgrade(version_upgrade[1], version_upgrade[2], bench_path=bench_path) post_upgrade(version_upgrade[1], version_upgrade[2], bench_path=bench_path)
if pull and compile:
print("Compiling Python files...")
compileall.compile_dir('../apps', quiet=1, rx=re.compile('.*node_modules.*'))
if restart_supervisor or conf.get('restart_supervisor_on_update'): if restart_supervisor or conf.get('restart_supervisor_on_update'):
restart_supervisor_processes(bench_path=bench_path) restart_supervisor_processes(bench_path=bench_path)
@ -259,6 +260,8 @@ def update(pull=False, apps=None, patch=False, build=False, requirements=False,
def copy_patches_txt(bench_path): def copy_patches_txt(bench_path):
import shutil
shutil.copy(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'patches', 'patches.txt'), shutil.copy(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'patches', 'patches.txt'),
os.path.join(bench_path, 'patches.txt')) os.path.join(bench_path, 'patches.txt'))
@ -352,23 +355,13 @@ def setup_socketio(bench_path='.'):
def patch_sites(bench_path='.'): def patch_sites(bench_path='.'):
bench.set_frappe_version(bench_path=bench_path)
try: try:
if bench.FRAPPE_VERSION == 4:
exec_cmd("{frappe} --latest all".format(frappe=get_frappe(bench_path=bench_path)), cwd=os.path.join(bench_path, 'sites'))
else:
run_frappe_cmd('--site', 'all', 'migrate', bench_path=bench_path) run_frappe_cmd('--site', 'all', 'migrate', bench_path=bench_path)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
raise PatchError raise PatchError
def build_assets(bench_path='.', app=None): def build_assets(bench_path='.', app=None):
bench.set_frappe_version(bench_path=bench_path)
if bench.FRAPPE_VERSION == 4:
exec_cmd("{frappe} --build".format(frappe=get_frappe(bench_path=bench_path)), cwd=os.path.join(bench_path, 'sites'))
else:
command = 'bench build' command = 'bench build'
if app: if app:
command += ' --app {}'.format(app) command += ' --app {}'.format(app)
@ -382,20 +375,15 @@ def get_sites(bench_path='.'):
def setup_backups(bench_path='.'): def setup_backups(bench_path='.'):
from crontab import CronTab
from bench.config.common_site_config import get_config from bench.config.common_site_config import get_config
logger.log('setting up backups') logger.log('setting up backups')
bench_dir = os.path.abspath(bench_path) bench_dir = os.path.abspath(bench_path)
user = get_config(bench_path=bench_dir).get('frappe_user') user = get_config(bench_path=bench_dir).get('frappe_user')
logfile = os.path.join(bench_dir, 'logs', 'backup.log') logfile = os.path.join(bench_dir, 'logs', 'backup.log')
bench.set_frappe_version(bench_path=bench_path)
system_crontab = CronTab(user=user) system_crontab = CronTab(user=user)
if bench.FRAPPE_VERSION == 4:
backup_command = "cd {sites_dir} && {frappe} --backup all".format(frappe=get_frappe(bench_path=bench_path),)
else:
backup_command = "cd {bench_dir} && {bench} --verbose --site all backup".format(bench_dir=bench_dir, bench=sys.argv[0]) backup_command = "cd {bench_dir} && {bench} --verbose --site all backup".format(bench_dir=bench_dir, bench=sys.argv[0])
job_command = "{backup_command} >> {logfile} 2>&1".format(backup_command=backup_command, logfile=logfile) job_command = "{backup_command} >> {logfile} 2>&1".format(backup_command=backup_command, logfile=logfile)
if job_command not in str(system_crontab): if job_command not in str(system_crontab):
@ -418,7 +406,7 @@ def setup_sudoers(user):
if set_permissions: if set_permissions:
os.chmod('/etc/sudoers', 0o440) os.chmod('/etc/sudoers', 0o440)
template = bench.config.env.get_template('frappe_sudoers') template = bench.config.env().get_template('frappe_sudoers')
frappe_sudoers = template.render(**{ frappe_sudoers = template.render(**{
'user': user, 'user': user,
'service': find_executable('service'), 'service': find_executable('service'),
@ -655,12 +643,6 @@ def update_npm_packages(bench_path='.'):
def backup_site(site, bench_path='.'): def backup_site(site, bench_path='.'):
bench.set_frappe_version(bench_path=bench_path)
if bench.FRAPPE_VERSION == 4:
exec_cmd("{frappe} --backup {site}".format(frappe=get_frappe(bench_path=bench_path), site=site),
cwd=os.path.join(bench_path, 'sites'))
else:
run_frappe_cmd('--site', site, 'backup', bench_path=bench_path) run_frappe_cmd('--site', site, 'backup', bench_path=bench_path)
@ -747,11 +729,6 @@ def fix_prod_setup_perms(bench_path='.', frappe_user=None):
os.chown(path, uid, gid) os.chown(path, uid, gid)
def get_current_frappe_version(bench_path='.'):
from .app import get_current_frappe_version as fv
return fv(bench_path=bench_path)
def run_frappe_cmd(*args, **kwargs): def run_frappe_cmd(*args, **kwargs):
from .cli import from_command_line from .cli import from_command_line
@ -811,6 +788,8 @@ sudo supervisorctl reload
def update_translations_p(args): def update_translations_p(args):
import requests
try: try:
update_translations(*args) update_translations(*args)
except requests.exceptions.HTTPError: except requests.exceptions.HTTPError:
@ -818,6 +797,8 @@ def update_translations_p(args):
def download_translations_p(): def download_translations_p():
import multiprocessing
pool = multiprocessing.Pool(multiprocessing.cpu_count()) pool = multiprocessing.Pool(multiprocessing.cpu_count())
langs = get_langs() langs = get_langs()
@ -842,6 +823,8 @@ def get_langs():
def update_translations(app, lang): def update_translations(app, lang):
import requests
translations_dir = os.path.join('apps', app, app, 'translations') translations_dir = os.path.join('apps', app, app, 'translations')
csv_file = os.path.join(translations_dir, lang + '.csv') csv_file = os.path.join(translations_dir, lang + '.csv')
url = "https://translate.erpnext.com/files/{}-{}.csv".format(app, lang) url = "https://translate.erpnext.com/files/{}-{}.csv".format(app, lang)
@ -891,6 +874,8 @@ def get_bench_name(bench_path):
def setup_fonts(): def setup_fonts():
import shutil
fonts_path = os.path.join('/tmp', 'fonts') fonts_path = os.path.join('/tmp', 'fonts')
if os.path.exists('/etc/fonts_backup'): if os.path.exists('/etc/fonts_backup'):
@ -963,6 +948,8 @@ def find_benches(directory=None):
def migrate_env(python, backup=False): def migrate_env(python, backup=False):
import shutil
from six.moves.urllib.parse import urlparse
from bench.config.common_site_config import get_config from bench.config.common_site_config import get_config
from bench.app import get_apps from bench.app import get_apps

View File

@ -34,6 +34,8 @@ If you are on a fresh server and logged in as root, at first create a dedicated
*(it is very common to use "frappe" as frappe-username, but this comes with the security flaw of ["frappe" ranking very high](https://www.reddit.com/r/dataisbeautiful/comments/b3sirt/i_deployed_over_a_dozen_cyber_honeypots_all_over/?st=JTJ0SC0Q&sh=76e05240) in as a username challenged in hacking attempts. So, for production sites it is highly recommended to use a custom username harder to guess)* *(it is very common to use "frappe" as frappe-username, but this comes with the security flaw of ["frappe" ranking very high](https://www.reddit.com/r/dataisbeautiful/comments/b3sirt/i_deployed_over_a_dozen_cyber_honeypots_all_over/?st=JTJ0SC0Q&sh=76e05240) in as a username challenged in hacking attempts. So, for production sites it is highly recommended to use a custom username harder to guess)*
*(you can specify the flag --home to specify a directory for your [frappe-user]. Bench will follow the home directory specified by the user's home directory e.g. /data/[frappe-user]/frappe-bench)*
Switch to `[frappe-user]` (using `su [frappe-user]`) and start the setup Switch to `[frappe-user]` (using `su [frappe-user]`) and start the setup
wget https://raw.githubusercontent.com/frappe/bench/develop/install.py wget https://raw.githubusercontent.com/frappe/bench/develop/install.py
@ -71,7 +73,7 @@ use --python flag to specify virtual environments python version, by default scr
## How do I start ERPNext ## How do I start ERPNext
1. For development: Go to your bench folder (`frappe-bench` by default) and start the bench with `bench start` 1. For development: Go to your bench folder (`~[frappe-user]/frappe-bench` by default) and start the bench with `bench start`
2. For production: Your process will be setup and managed by `nginx` and `supervisor`. Checkout [Setup Production](https://frappe.io/docs/user/en/bench/guides/setup-production.html) for more information. 2. For production: Your process will be setup and managed by `nginx` and `supervisor`. Checkout [Setup Production](https://frappe.io/docs/user/en/bench/guides/setup-production.html) for more information.
--- ---

View File

@ -157,13 +157,24 @@ def install_prerequisites():
] ]
}) })
# until psycopg2-binary is available for aarch64 (Arm 64-bit), we'll need libpq and libssl dev packages to build psycopg2 from source
if platform.machine() == 'aarch64':
log("Installing libpq and libssl dev packages to build psycopg2 for aarch64...")
run_os_command({
'apt-get': ['sudo apt-get install -y libpq-dev libssl-dev'],
'yum': ['sudo yum install -y libpq-devel openssl-devel']
})
install_package('curl') install_package('curl')
install_package('wget') install_package('wget')
install_package('git') install_package('git')
install_package('pip3', 'python3-pip') install_package('pip3', 'python3-pip')
run_os_command({
'python3': "sudo -H python3 -m pip install --upgrade pip setuptools-rust"
})
success = run_os_command({ success = run_os_command({
'python3': "sudo -H python3 -m pip install --upgrade setuptools wheel cryptography ansible==2.8.5 pip" 'python3': "sudo -H python3 -m pip install --upgrade setuptools wheel cryptography ansible~=2.8.15"
}) })
if not (success or shutil.which('ansible')): if not (success or shutil.which('ansible')):
@ -224,9 +235,10 @@ def install_bench(args):
extra_vars = vars(args) extra_vars = vars(args)
extra_vars.update(frappe_user=args.user) extra_vars.update(frappe_user=args.user)
extra_vars.update(user_directory=get_user_home_directory(args.user))
if os.path.exists(tmp_bench_repo): if os.path.exists(tmp_bench_repo):
repo_path = tmp_bench_repo repo_path = tmp_bench_repo
else: else:
repo_path = os.path.join(os.path.expanduser('~'), 'bench') repo_path = os.path.join(os.path.expanduser('~'), 'bench')
@ -247,10 +259,9 @@ def install_bench(args):
else: else:
frappe_branch = "version-{0}".format(args.version) frappe_branch = "version-{0}".format(args.version)
erpnext_branch = "version-{0}".format(args.version) erpnext_branch = "version-{0}".format(args.version)
else: # Allow override of frappe_branch and erpnext_branch, regardless of args.version (which always has a default set)
if args.frappe_branch: if args.frappe_branch:
frappe_branch = args.frappe_branch frappe_branch = args.frappe_branch
if args.erpnext_branch: if args.erpnext_branch:
erpnext_branch = args.erpnext_branch erpnext_branch = args.erpnext_branch
@ -261,6 +272,10 @@ def install_bench(args):
extra_vars.update(bench_name=bench_name) extra_vars.update(bench_name=bench_name)
# Will install ERPNext production setup by default # Will install ERPNext production setup by default
if args.without_erpnext:
log("Initializing bench {bench_name}:\n\tFrappe Branch: {frappe_branch}\n\tERPNext will not be installed due to --without-erpnext".format(bench_name=bench_name, frappe_branch=frappe_branch))
else:
log("Initializing bench {bench_name}:\n\tFrappe Branch: {frappe_branch}\n\tERPNext Branch: {erpnext_branch}".format(bench_name=bench_name, frappe_branch=frappe_branch, erpnext_branch=erpnext_branch))
run_playbook('site.yml', sudo=True, extra_vars=extra_vars) run_playbook('site.yml', sudo=True, extra_vars=extra_vars)
if os.path.exists(tmp_bench_repo): if os.path.exists(tmp_bench_repo):
@ -273,11 +288,15 @@ def clone_bench_repo(args):
repo_url = args.repo_url or 'https://github.com/frappe/bench' repo_url = args.repo_url or 'https://github.com/frappe/bench'
if os.path.exists(tmp_bench_repo): if os.path.exists(tmp_bench_repo):
log('Not cloning already existing Bench repository at {tmp_bench_repo}'.format(tmp_bench_repo=tmp_bench_repo))
return 0 return 0
elif args.without_bench_setup: elif args.without_bench_setup:
clone_path = os.path.join(os.path.expanduser('~'), 'bench') clone_path = os.path.join(os.path.expanduser('~'), 'bench')
log('--without-bench-setup specified, clone path is: {clone_path}'.format(clone_path=clone_path))
else: else:
clone_path = tmp_bench_repo clone_path = tmp_bench_repo
# Not logging repo_url to avoid accidental credential leak in case credential is embedded in URL
log('Cloning bench repository branch {branch} into {clone_path}'.format(branch=branch, clone_path=clone_path))
success = run_os_command( success = run_os_command(
{'git': 'git clone --quiet {repo_url} {bench_repo} --depth 1 --branch {branch}'.format( {'git': 'git clone --quiet {repo_url} {bench_repo} --depth 1 --branch {branch}'.format(
@ -327,8 +346,8 @@ def get_passwords(args):
mysql_root_password = '' mysql_root_password = ''
continue continue
# admin password # admin password, only needed if we're also creating a site
if not admin_password: if not admin_password and not args.without_site:
admin_password = getpass.unix_getpass(prompt='Please enter the default Administrator user password: ') admin_password = getpass.unix_getpass(prompt='Please enter the default Administrator user password: ')
conf_admin_passswd = getpass.unix_getpass(prompt='Re-enter Administrator password: ') conf_admin_passswd = getpass.unix_getpass(prompt='Re-enter Administrator password: ')
@ -336,6 +355,8 @@ def get_passwords(args):
passwords_didnt_match("Administrator") passwords_didnt_match("Administrator")
admin_password = '' admin_password = ''
continue continue
elif args.without_site:
log("Not creating a new site due to --without-site")
pass_set = False pass_set = False
else: else:
@ -366,6 +387,11 @@ def get_extra_vars_json(extra_args):
return ('@' + json_path) return ('@' + json_path)
def get_user_home_directory(user):
# Return home directory /home/USERNAME or anything else defined as home directory in
# passwd for user.
return os.path.expanduser('~'+user)
def run_playbook(playbook_name, sudo=False, extra_vars=None): def run_playbook(playbook_name, sudo=False, extra_vars=None):
args = ['ansible-playbook', '-c', 'local', playbook_name , '-vvvv'] args = ['ansible-playbook', '-c', 'local', playbook_name , '-vvvv']
@ -405,8 +431,8 @@ def parse_commandline_args():
args_group.add_argument('--develop', dest='develop', action='store_true', default=False, help='Install developer setup') args_group.add_argument('--develop', dest='develop', action='store_true', default=False, help='Install developer setup')
args_group.add_argument('--production', dest='production', action='store_true', default=False, help='Setup Production environment for bench') args_group.add_argument('--production', dest='production', action='store_true', default=False, help='Setup Production environment for bench')
parser.add_argument('--site', dest='site', action='store', default='site1.local', help='Specifiy name for your first ERPNext site') parser.add_argument('--site', dest='site', action='store', default='site1.local', help='Specify name for your first ERPNext site')
parser.add_argument('--without-site', dest='without_site', action='store_true', default=False) parser.add_argument('--without-site', dest='without_site', action='store_true', default=False, help='Do not create a new site')
parser.add_argument('--verbose', dest='verbose', action='store_true', default=False, help='Run the script in verbose mode') parser.add_argument('--verbose', dest='verbose', action='store_true', default=False, help='Run the script in verbose mode')
parser.add_argument('--user', dest='user', help='Install frappe-bench for this user') parser.add_argument('--user', dest='user', help='Install frappe-bench for this user')
parser.add_argument('--bench-branch', dest='bench_branch', help='Clone a particular branch of bench repository') parser.add_argument('--bench-branch', dest='bench_branch', help='Clone a particular branch of bench repository')

View File

@ -1,7 +1,7 @@
Click==7.0 Click==7.0
GitPython==2.1.15 GitPython==2.1.15
honcho==1.0.1 honcho==1.0.1
Jinja2==2.10.3 Jinja2==2.11.3
python-crontab==2.4.0 python-crontab==2.4.0
requests==2.22.0 requests==2.22.0
semantic-version==2.8.2 semantic-version==2.8.2