mirror of
https://github.com/frappe/bench.git
synced 2024-11-11 15:51:03 +00:00
feat(easy-install): seperate logging and cleaner UI
This commit is contained in:
parent
a922d198a2
commit
b3feaa255b
@ -1,10 +1,112 @@
|
|||||||
# wget setup_frappe.py | python3
|
#!/usr/bin/env python3
|
||||||
import os, sys, subprocess, getpass, json, multiprocessing, shutil, platform
|
import os, sys, subprocess, getpass, json, multiprocessing, shutil, platform, warnings
|
||||||
from distutils.spawn import find_executable
|
from builtins import print as _
|
||||||
|
|
||||||
tmp_bench_repo = '/tmp/.bench'
|
tmp_bench_repo = '/tmp/.bench'
|
||||||
|
tmp_log_folder = '/tmp/logs/'
|
||||||
|
log_path = os.path.join(tmp_log_folder, 'install_bench.log')
|
||||||
|
log_stream = sys.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def print(message, level=0):
|
||||||
|
levels = {
|
||||||
|
0: '\033[94m', # normal
|
||||||
|
1: '\033[92m', # success
|
||||||
|
2: '\033[91m', # fail
|
||||||
|
3: '\033[93m' # warn/suggest
|
||||||
|
}
|
||||||
|
start = levels.get(level) or ''
|
||||||
|
end = '\033[0m'
|
||||||
|
_(start + message + end)
|
||||||
|
|
||||||
|
|
||||||
|
def find_executable(executable, path=None):
|
||||||
|
"""Tries to find 'executable' in the directories listed in 'path'.
|
||||||
|
A string listing directories separated by 'os.pathsep'; defaults to
|
||||||
|
os.environ['PATH']. Returns the complete filename or None if not found.
|
||||||
|
|
||||||
|
source: https://github.com/python/cpython/blob/master/Lib/distutils/spawn.py
|
||||||
|
"""
|
||||||
|
_, ext = os.path.splitext(executable)
|
||||||
|
if (sys.platform == 'win32') and (ext != '.exe'):
|
||||||
|
executable = executable + '.exe'
|
||||||
|
|
||||||
|
if os.path.isfile(executable):
|
||||||
|
return executable
|
||||||
|
|
||||||
|
if path is None:
|
||||||
|
path = os.environ.get('PATH', None)
|
||||||
|
if path is None:
|
||||||
|
try:
|
||||||
|
path = os.confstr("CS_PATH")
|
||||||
|
except (AttributeError, ValueError):
|
||||||
|
# os.confstr() or CS_PATH is not available
|
||||||
|
path = os.defpath
|
||||||
|
# bpo-35755: Don't use os.defpath if the PATH environment variable is
|
||||||
|
# set to an empty string
|
||||||
|
|
||||||
|
# PATH='' doesn't match, whereas PATH=':' looks in the current directory
|
||||||
|
if not path:
|
||||||
|
return None
|
||||||
|
|
||||||
|
paths = path.split(os.pathsep)
|
||||||
|
for p in paths:
|
||||||
|
f = os.path.join(p, executable)
|
||||||
|
if os.path.isfile(f):
|
||||||
|
# the file exists, we have a shot at spawn working
|
||||||
|
return f
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def run_os_command(command_map):
|
||||||
|
'''command_map is a dictionary of {'executable': command}. For ex. {'apt-get': 'sudo apt-get install -y python2.7'}'''
|
||||||
|
success = True
|
||||||
|
|
||||||
|
for executable, commands in command_map.items():
|
||||||
|
if find_executable(executable):
|
||||||
|
if isinstance(commands, str):
|
||||||
|
commands = [commands]
|
||||||
|
|
||||||
|
for command in commands:
|
||||||
|
returncode = subprocess.check_call(command, shell=True, stdout=log_stream, stderr=sys.stderr)
|
||||||
|
success = success and (returncode == 0)
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
|
|
||||||
|
def could_not_install(package):
|
||||||
|
raise Exception('Could not install {0}. Please install it manually.'.format(package))
|
||||||
|
|
||||||
|
|
||||||
|
def is_sudo_user():
|
||||||
|
return os.geteuid() == 0
|
||||||
|
|
||||||
|
|
||||||
|
def install_package(package):
|
||||||
|
if find_executable(package):
|
||||||
|
print("{} already installed!".format(package), level=1)
|
||||||
|
else:
|
||||||
|
print("Installing {}...".format(package))
|
||||||
|
success = run_os_command({
|
||||||
|
'apt-get': ['sudo apt-get install -y {0}'.format(package)],
|
||||||
|
'yum': ['sudo yum install -y {0}'.format(package)]
|
||||||
|
})
|
||||||
|
if success:
|
||||||
|
print("{} Installed!".format(package), level=1)
|
||||||
|
else:
|
||||||
|
could_not_install(package)
|
||||||
|
|
||||||
|
|
||||||
def install_bench(args):
|
def install_bench(args):
|
||||||
|
global log_stream
|
||||||
|
sys.stderr = sys.stdout
|
||||||
|
|
||||||
|
if not args.verbose:
|
||||||
|
if not os.path.exists(tmp_log_folder):
|
||||||
|
os.makedirs(tmp_log_folder)
|
||||||
|
log_stream = open(log_path, 'w')
|
||||||
|
print("Logs are saved under {}".format(log_path), level=3)
|
||||||
|
|
||||||
check_distribution_compatibility()
|
check_distribution_compatibility()
|
||||||
check_brew_installed()
|
check_brew_installed()
|
||||||
# pre-requisites for bench repo cloning
|
# pre-requisites for bench repo cloning
|
||||||
@ -19,9 +121,7 @@ def install_bench(args):
|
|||||||
'yum': [
|
'yum': [
|
||||||
'sudo yum groupinstall -y "Development tools"',
|
'sudo yum groupinstall -y "Development tools"',
|
||||||
'sudo yum install -y epel-release redhat-lsb-core git python-setuptools python-devel openssl-devel libffi-devel'
|
'sudo yum install -y epel-release redhat-lsb-core git python-setuptools python-devel openssl-devel libffi-devel'
|
||||||
],
|
]
|
||||||
# epel-release is required to install redis, so installing it before the playbook-run.
|
|
||||||
# redhat-lsb-core is required, so that ansible can set ansible_lsb variable
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if not find_executable("git"):
|
if not find_executable("git"):
|
||||||
@ -36,13 +136,13 @@ def install_bench(args):
|
|||||||
# secure pip installation
|
# secure pip installation
|
||||||
if find_executable('pip'):
|
if find_executable('pip'):
|
||||||
run_os_command({
|
run_os_command({
|
||||||
'pip': 'sudo pip install --upgrade setuptools cryptography pip'
|
'pip': 'sudo -H pip install --upgrade setuptools cryptography pip'
|
||||||
})
|
})
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if not os.path.exists("get-pip.py"):
|
if not os.path.exists("get-pip.py"):
|
||||||
run_os_command({
|
run_os_command({
|
||||||
'wget': 'wget https://bootstrap.pypa.io/get-pip.py'
|
'wget': 'wget -q https://bootstrap.pypa.io/get-pip.py'
|
||||||
})
|
})
|
||||||
|
|
||||||
success = run_os_command({
|
success = run_os_command({
|
||||||
@ -53,15 +153,15 @@ def install_bench(args):
|
|||||||
dist_name, dist_version = get_distribution_info()
|
dist_name, dist_version = get_distribution_info()
|
||||||
if dist_name == 'centos':
|
if dist_name == 'centos':
|
||||||
run_os_command({
|
run_os_command({
|
||||||
'pip': 'sudo pip install --upgrade --ignore-installed requests'
|
'pip': 'sudo -H pip install --upgrade --ignore-installed requests'
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
run_os_command({
|
run_os_command({
|
||||||
'pip': 'sudo pip install --upgrade requests'
|
'pip': 'sudo -H pip install --upgrade requests'
|
||||||
})
|
})
|
||||||
|
|
||||||
success = run_os_command({
|
success = run_os_command({
|
||||||
'pip': "sudo pip install --upgrade setuptools cryptography ansible==2.8.5 pip"
|
'pip': "sudo -H pip install --upgrade setuptools cryptography ansible==2.8.5 pip"
|
||||||
})
|
})
|
||||||
|
|
||||||
if not success:
|
if not success:
|
||||||
@ -134,107 +234,72 @@ def install_bench(args):
|
|||||||
# Will install ERPNext production setup by default
|
# Will install ERPNext production setup by default
|
||||||
run_playbook('site.yml', sudo=True, extra_vars=extra_vars)
|
run_playbook('site.yml', sudo=True, extra_vars=extra_vars)
|
||||||
|
|
||||||
# # Will do changes for production if --production flag is passed
|
|
||||||
# if args.production:
|
|
||||||
# run_playbook('production.yml', sudo=True, extra_vars=extra_vars)
|
|
||||||
|
|
||||||
if os.path.exists(tmp_bench_repo):
|
if os.path.exists(tmp_bench_repo):
|
||||||
shutil.rmtree(tmp_bench_repo)
|
shutil.rmtree(tmp_bench_repo)
|
||||||
|
|
||||||
def check_distribution_compatibility():
|
|
||||||
supported_dists = {'ubuntu': [14, 15, 16, 18, 19], 'debian': [8, 9, 10],
|
|
||||||
'centos': [7], 'macos': [10.9, 10.10, 10.11, 10.12]}
|
|
||||||
|
|
||||||
|
def check_distribution_compatibility():
|
||||||
dist_name, dist_version = get_distribution_info()
|
dist_name, dist_version = get_distribution_info()
|
||||||
|
supported_dists = {
|
||||||
|
'macos': [10.9, 10.10, 10.11, 10.12],
|
||||||
|
'ubuntu': [14, 15, 16, 18, 19],
|
||||||
|
'debian': [8, 9],
|
||||||
|
'centos': [7]
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Checking System Compatibility...")
|
||||||
if dist_name in supported_dists:
|
if dist_name in supported_dists:
|
||||||
if float(dist_version) in supported_dists[dist_name]:
|
if float(dist_version) in supported_dists[dist_name]:
|
||||||
return
|
print("{0} {1} is compatible!".format(dist_name, dist_version), level=1)
|
||||||
|
else:
|
||||||
|
print("Install on {0} {1} instead".format(dist_name, supported_dists[dist_name][-1]), level=3)
|
||||||
|
else:
|
||||||
|
print("Sorry, the installer doesn't support {0}. Aborting installation!".format(dist_name), level=2)
|
||||||
|
|
||||||
print("Sorry, the installer doesn't support {0} {1}. Aborting installation!".format(dist_name, dist_version))
|
|
||||||
if dist_name in supported_dists:
|
|
||||||
print("Install on {0} {1} instead".format(dist_name, supported_dists[dist_name][-1]))
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def get_distribution_info():
|
def get_distribution_info():
|
||||||
# return distribution name and major version
|
# return distribution name and major version
|
||||||
if platform.system() == "Linux":
|
if platform.system() == "Linux":
|
||||||
current_dist = platform.dist()
|
current_dist = platform.dist()
|
||||||
return current_dist[0].lower(), current_dist[1].rsplit('.')[0]
|
return current_dist[0].lower(), current_dist[1].rsplit('.')[0]
|
||||||
|
|
||||||
elif platform.system() == "Darwin":
|
elif platform.system() == "Darwin":
|
||||||
current_dist = platform.mac_ver()
|
current_dist = platform.mac_ver()
|
||||||
return "macos", current_dist[0].rsplit('.', 1)[0]
|
return "macos", current_dist[0].rsplit('.', 1)[0]
|
||||||
|
|
||||||
def install_package(package):
|
|
||||||
package_exec = find_executable(package)
|
|
||||||
|
|
||||||
if not package_exec:
|
|
||||||
success = run_os_command({
|
|
||||||
'apt-get': ['sudo apt-get install -y {0}'.format(package)],
|
|
||||||
'yum': ['sudo yum install -y {0}'.format(package)]
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not success:
|
|
||||||
could_not_install(package)
|
|
||||||
|
|
||||||
def check_brew_installed():
|
def check_brew_installed():
|
||||||
if 'Darwin' not in os.uname():
|
if 'Darwin' in os.uname():
|
||||||
return
|
if not find_executable('brew'):
|
||||||
|
raise Exception('''
|
||||||
|
Please install brew package manager before proceeding with bench setup. Please run following
|
||||||
|
to install brew package manager on your machine,
|
||||||
|
|
||||||
brew_exec = find_executable('brew')
|
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||||
|
''')
|
||||||
|
|
||||||
if not brew_exec:
|
|
||||||
raise Exception('''
|
|
||||||
Please install brew package manager before proceeding with bench setup. Please run following
|
|
||||||
to install brew package manager on your machine,
|
|
||||||
|
|
||||||
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
|
||||||
''')
|
|
||||||
|
|
||||||
def clone_bench_repo(args):
|
def clone_bench_repo(args):
|
||||||
'''Clones the bench repository in the user folder'''
|
'''Clones the bench repository in the user folder'''
|
||||||
if os.path.exists(tmp_bench_repo):
|
|
||||||
return 0
|
|
||||||
|
|
||||||
elif args.without_bench_setup:
|
|
||||||
clone_path = os.path.join(os.path.expanduser('~'), 'bench')
|
|
||||||
|
|
||||||
else:
|
|
||||||
clone_path = tmp_bench_repo
|
|
||||||
|
|
||||||
branch = args.bench_branch or 'master'
|
branch = args.bench_branch or 'master'
|
||||||
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):
|
||||||
|
return 0
|
||||||
|
elif args.without_bench_setup:
|
||||||
|
clone_path = os.path.join(os.path.expanduser('~'), 'bench')
|
||||||
|
else:
|
||||||
|
clone_path = tmp_bench_repo
|
||||||
|
|
||||||
success = run_os_command(
|
success = run_os_command(
|
||||||
{'git': 'git clone {repo_url} {bench_repo} --depth 1 --branch {branch}'.format(
|
{'git': 'git clone --quiet {repo_url} {bench_repo} --depth 1 --branch {branch}'.format(
|
||||||
repo_url=repo_url, bench_repo=clone_path, branch=branch)}
|
repo_url=repo_url, bench_repo=clone_path, branch=branch)}
|
||||||
)
|
)
|
||||||
|
|
||||||
return success
|
return success
|
||||||
|
|
||||||
def run_os_command(command_map):
|
|
||||||
'''command_map is a dictionary of {'executable': command}. For ex. {'apt-get': 'sudo apt-get install -y python2.7'} '''
|
|
||||||
success = True
|
|
||||||
for executable, commands in list(command_map.items()):
|
|
||||||
if find_executable(executable):
|
|
||||||
if isinstance(commands, str):
|
|
||||||
commands = [commands]
|
|
||||||
|
|
||||||
for command in commands:
|
|
||||||
returncode = subprocess.check_call(command, shell=True)
|
|
||||||
success = success and ( returncode == 0 )
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
return success
|
|
||||||
|
|
||||||
def could_not_install(package):
|
|
||||||
raise Exception('Could not install {0}. Please install it manually.'.format(package))
|
|
||||||
|
|
||||||
def is_sudo_user():
|
|
||||||
return os.geteuid() == 0
|
|
||||||
|
|
||||||
|
|
||||||
def get_passwords(args):
|
def get_passwords(args):
|
||||||
@ -324,92 +389,54 @@ def run_playbook(playbook_name, sudo=False, extra_vars=None):
|
|||||||
else:
|
else:
|
||||||
cwd = os.path.join(os.path.expanduser('~'), 'bench')
|
cwd = os.path.join(os.path.expanduser('~'), 'bench')
|
||||||
|
|
||||||
success = subprocess.check_call(args, cwd=os.path.join(cwd, 'playbooks'))
|
success = subprocess.check_call(args, cwd=os.path.join(cwd, 'playbooks'), stdout=log_stream, stderr=sys.stderr)
|
||||||
return success
|
return success
|
||||||
|
|
||||||
|
|
||||||
def parse_commandline_args():
|
def parse_commandline_args():
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='Frappe Installer')
|
parser = argparse.ArgumentParser(description='Frappe Installer')
|
||||||
|
|
||||||
# Arguments develop and production are mutually exclusive both can't be specified together.
|
# Arguments develop and production are mutually exclusive both can't be specified together.
|
||||||
# Hence, we need to create a group for discouraging use of both options at the same time.
|
# Hence, we need to create a group for discouraging use of both options at the same time.
|
||||||
args_group = parser.add_mutually_exclusive_group()
|
args_group = parser.add_mutually_exclusive_group()
|
||||||
|
|
||||||
args_group.add_argument('--develop', dest='develop', action='store_true', default=False,
|
args_group.add_argument('--develop', dest='develop', action='store_true', default=False, help='Install developer setup')
|
||||||
help='Install developer setup')
|
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')
|
||||||
args_group.add_argument('--production', dest='production', action='store_true',
|
parser.add_argument('--without-site', dest='without_site', action='store_true', default=False)
|
||||||
default=False, help='Setup Production environment for bench')
|
parser.add_argument('--verbose', dest='verbose', action='store_true', default=False, help='Run the script in verbose mode')
|
||||||
|
|
||||||
parser.add_argument('--site', dest='site', action='store', default='site1.local',
|
|
||||||
help='Specifiy name for your first ERPNext site')
|
|
||||||
|
|
||||||
parser.add_argument('--without-site', dest='without_site', action='store_true',
|
|
||||||
default=False)
|
|
||||||
|
|
||||||
parser.add_argument('--verbose', dest='verbosity', 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')
|
||||||
|
|
||||||
parser.add_argument('--repo-url', dest='repo_url', help='Clone bench from the given url')
|
parser.add_argument('--repo-url', dest='repo_url', help='Clone bench from the given url')
|
||||||
|
parser.add_argument('--frappe-repo-url', dest='frappe_repo_url', action='store', default='https://github.com/frappe/frappe', help='Clone frappe from the given url')
|
||||||
parser.add_argument('--frappe-repo-url', dest='frappe_repo_url', action='store', default='https://github.com/frappe/frappe',
|
parser.add_argument('--frappe-branch', dest='frappe_branch', action='store', help='Clone a particular branch of frappe')
|
||||||
help='Clone frappe from the given url')
|
parser.add_argument('--erpnext-repo-url', dest='erpnext_repo_url', action='store', default='https://github.com/frappe/erpnext', help='Clone erpnext from the given url')
|
||||||
|
parser.add_argument('--erpnext-branch', dest='erpnext_branch', action='store', help='Clone a particular branch of erpnext')
|
||||||
parser.add_argument('--frappe-branch', dest='frappe_branch', action='store',
|
parser.add_argument('--without-erpnext', dest='without_erpnext', action='store_true', default=False, help='Prevent fetching ERPNext')
|
||||||
help='Clone a particular branch of frappe')
|
|
||||||
|
|
||||||
parser.add_argument('--erpnext-repo-url', dest='erpnext_repo_url', action='store', default='https://github.com/frappe/erpnext',
|
|
||||||
help='Clone erpnext from the given url')
|
|
||||||
|
|
||||||
parser.add_argument('--erpnext-branch', dest='erpnext_branch', action='store',
|
|
||||||
help='Clone a particular branch of erpnext')
|
|
||||||
|
|
||||||
parser.add_argument('--without-erpnext', dest='without_erpnext', action='store_true', default=False,
|
|
||||||
help='Prevent fetching ERPNext')
|
|
||||||
|
|
||||||
# direct provision to install versions
|
# direct provision to install versions
|
||||||
parser.add_argument('--version', dest='version', action='store', default='12', type=int,
|
parser.add_argument('--version', dest='version', action='store', default='12', type=int, help='Clone particular version of frappe and erpnext')
|
||||||
help='Clone particular version of frappe and erpnext')
|
|
||||||
|
|
||||||
# To enable testing of script using Travis, this should skip the prompt
|
# To enable testing of script using Travis, this should skip the prompt
|
||||||
parser.add_argument('--run-travis', dest='run_travis', action='store_true', default=False,
|
parser.add_argument('--run-travis', dest='run_travis', action='store_true', default=False, help=argparse.SUPPRESS)
|
||||||
help=argparse.SUPPRESS)
|
parser.add_argument('--without-bench-setup', dest='without_bench_setup', action='store_true', default=False, help=argparse.SUPPRESS)
|
||||||
|
|
||||||
parser.add_argument('--without-bench-setup', dest='without_bench_setup', action='store_true', default=False,
|
|
||||||
help=argparse.SUPPRESS)
|
|
||||||
|
|
||||||
# whether to overwrite an existing bench
|
# whether to overwrite an existing bench
|
||||||
parser.add_argument('--overwrite', dest='overwrite', action='store_true', default=False,
|
parser.add_argument('--overwrite', dest='overwrite', action='store_true', default=False, help='Whether to overwrite an existing bench')
|
||||||
help='Whether to overwrite an existing bench')
|
|
||||||
|
|
||||||
# set passwords
|
# set passwords
|
||||||
parser.add_argument('--mysql-root-password', dest='mysql_root_password', help='Set mysql root password')
|
parser.add_argument('--mysql-root-password', dest='mysql_root_password', help='Set mysql root password')
|
||||||
parser.add_argument('--mariadb-version', dest='mariadb_version', default='10.4', help='Specify mariadb version')
|
parser.add_argument('--mariadb-version', dest='mariadb_version', default='10.2', help='Specify mariadb version')
|
||||||
parser.add_argument('--admin-password', dest='admin_password', help='Set admin password')
|
parser.add_argument('--admin-password', dest='admin_password', help='Set admin password')
|
||||||
parser.add_argument('--bench-name', dest='bench_name', help='Create bench with specified name. Default name is frappe-bench')
|
parser.add_argument('--bench-name', dest='bench_name', help='Create bench with specified name. Default name is frappe-bench')
|
||||||
|
|
||||||
# Python interpreter to be used
|
# Python interpreter to be used
|
||||||
parser.add_argument('--python', dest='python', default='python3',
|
parser.add_argument('--python', dest='python', default='python3', help=argparse.SUPPRESS)
|
||||||
help=argparse.SUPPRESS
|
|
||||||
)
|
|
||||||
|
|
||||||
# LXC Support
|
# LXC Support
|
||||||
parser.add_argument('--container', dest='container', default=False, action='store_true',
|
parser.add_argument('--container', dest='container', default=False, action='store_true', help='Use if you\'re creating inside LXC')
|
||||||
help='Use if you\'re creating inside LXC'
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
return args
|
return args
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
args = parse_commandline_args()
|
args = parse_commandline_args()
|
||||||
|
with warnings.catch_warnings():
|
||||||
install_bench(args)
|
warnings.simplefilter("ignore")
|
||||||
|
install_bench(args)
|
||||||
print('''Frappe/ERPNext has been successfully installed!''')
|
print("Frappe/ERPNext has been successfully installed!")
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
register: bench_stat
|
register: bench_stat
|
||||||
|
|
||||||
- name: Fix permissions
|
- name: Fix permissions
|
||||||
become_method: sudo
|
become_user: root
|
||||||
command: chown {{ frappe_user }} -R ~
|
command: chown {{ frappe_user }} -R ~
|
||||||
|
|
||||||
- name: python3 bench init for develop
|
- name: python3 bench init for develop
|
||||||
|
Loading…
Reference in New Issue
Block a user