2
0
mirror of https://github.com/frappe/bench.git synced 2024-09-22 20:19:01 +00:00

feat(easy-install): seperate logging and cleaner UI

This commit is contained in:
Gavin D'souza 2019-12-18 14:33:21 +05:30
parent a922d198a2
commit b3feaa255b
2 changed files with 164 additions and 137 deletions

View File

@ -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,57 +234,43 @@ 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'):
brew_exec = find_executable('brew')
if not brew_exec:
raise Exception(''' raise Exception('''
Please install brew package manager before proceeding with bench setup. Please run following Please install brew package manager before proceeding with bench setup. Please run following
to install brew package manager on your machine, to install brew package manager on your machine,
@ -192,49 +278,28 @@ def check_brew_installed():
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" /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():
warnings.simplefilter("ignore")
install_bench(args) install_bench(args)
print("Frappe/ERPNext has been successfully installed!")
print('''Frappe/ERPNext has been successfully installed!''')

View File

@ -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