From 907446f97f6abe9e69359eb7a78f820e22e1eb26 Mon Sep 17 00:00:00 2001 From: Mike Thornton Date: Mon, 7 May 2012 15:41:41 -0500 Subject: [PATCH] More robust installer with ability to save install preferences --- install.sh | 294 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 215 insertions(+), 79 deletions(-) diff --git a/install.sh b/install.sh index 92f3d7cb..80617bfd 100755 --- a/install.sh +++ b/install.sh @@ -1,88 +1,224 @@ -#!/usr/bin/env bash -BASH_IT="$HOME/.bash_it" +#!/usr/bin/env python +import os +import sys +import shutil +from os import path +from optparse import OptionParser +import ConfigParser +from glob import glob -cp $HOME/.bash_profile $HOME/.bash_profile.bak +HOME=os.getenv('HOME') +BASH_IT=path.join(HOME, '.bash_it') +BASH_PROFILE = path.join(HOME, '.bash_profile') -echo "Your original .bash_profile has been backed up to .bash_profile.bak" +def confirm(prompt=None, default=False): + """ + Prompt the user for yes or no input. -cp $HOME/.bash_it/template/bash_profile.template.bash $HOME/.bash_profile + Returns True or False + """ + if prompt is None: + prompt = 'Confirm' -echo "Copied the template .bash_profile into ~/.bash_profile, edit this file to customize bash-it" + if default: + prompt = '%s [%s/%s]: ' % (prompt, 'Y', 'n') + else: + prompt = '%s [%s/%s]: ' % (prompt, 'y', 'N') + + while True: + ans = raw_input(prompt) + if not ans: + return default + elif ans.lower() == 'y': + return True + elif ans.lower() == 'n': + return False + else: + print 'Please enter y or n.' + continue -while true -do - read -p "Do you use Jekyll? (If you don't know what Jekyll is, answer 'n') [Y/N] " RESP +def ask(prompt, values, default=None): + for i, v in enumerate(values): + prompt += '\n %d. %s' % (i + 1, v) - case $RESP - in - [yY]) - cp $HOME/.bash_it/template/jekyllconfig.template.bash $HOME/.jekyllconfig - echo "Copied the template .jekyllconfig into your home directory. Edit this file to customize bash-it for using the Jekyll plugins" - break - ;; - [nN]) - break - ;; - *) - echo "Please enter Y or N" - esac -done + prompt += '\n Choice #?' -function load_all() { - file_type=$1 - [ ! -d "$BASH_IT/$file_type/enabled" ] && mkdir "$BASH_IT/${file_type}/enabled" - ln -s $BASH_IT/${file_type}/[^_]available/* "${BASH_IT}/${file_type}/enabled" -} + if default: + prompt += ' [%s]' % default -function load_some() { - file_type=$1 - for file in `ls $BASH_IT/${file_type}/available/[^_]*` - do - if [ ! -d "$BASH_IT/$file_type/enabled" ] - then - mkdir "$BASH_IT/$file_type/enabled" - fi - while true - do - read -p "Would you like to enable the ${file%.*.*} $file_type? [Y/N] " RESP - case $RESP in - [yY]) - ln -s "$BASH_IT/$file_type/available/$file" "$BASH_IT/$file_type/enabled" - break - ;; - [nN]) - break - ;; - *) - echo "Please choose y or n." - ;; - esac - done - done -} + prompt += ': ' -for type in "aliases" "plugins" "completion" -do - while true - do - read -p "Would you like to enable all, some, or no $type? Some of these may make bash slower to start up (especially completion). (all/some/none) " RESP - case $RESP - in - some) - load_some $type - break - ;; - all) - load_all $type - break - ;; - none) - break - ;; - *) - echo "Unknown choice. Please enter some, all, or none" - continue - ;; - esac - done -done + min = 1 + max = len(values) + options = range(min, max + 1) + + while True: + ans = raw_input(prompt) + if not ans: + return default + + try: + ans = int(ans) + except ValueError: + print('Please enter a number %d-%d' % (min, max)) + prompt = '#?: ' + continue + + if ans in options: + return values[ans - 1] + else: + print('Please enter a number %d-%d' % (min, max)) + prompt = '#?: ' + + +def is_bash_it_installed(): + if path.exists(BASH_PROFILE): + f = open(BASH_PROFILE, 'r') + installed = '$BASH_IT' in f.read() + f.close() + return installed + return False + +class Configurator(object): + def __init__(self, config, prompt): + self.config = config + self.prompt = prompt + + def getbool(self, key, default=False): + section, key = key.split('.', 1) + if self.config.has_option(section, key): + val = self.config.getboolean(section, key) + else: + val = default + + return val + + def get(self, key, default=None): + section, key = key.split('.', 1) + if self.config.has_option(section, key): + val = self.config.get(section, key) + else: + val = default + + return val + + def set(self, key, value): + section, key = key.split('.', 1) + if not self.config.has_section(section): + self.config.add_section(section) + + self.config.set(section, key, value) + + def confirm(self, key, question, default=False): + val = self.getbool(key, default) + if self.prompt: + val = confirm(question, val) + + if val: + self.set(key, 'yes') + else: + self.set(key, 'no') + return val + + def ask(self, key, question, values, default=None): + val = self.get(key, default) + + if self.prompt: + val = ask(question, values, val) + + self.set(key, val) + return val + + +def main(configfile=None, prompt=True): + config = ConfigParser.RawConfigParser() + if configfile: + config.read(configfile) + + conf = Configurator(config, prompt) + + if is_bash_it_installed(): + print('Bash-it already installed, running configurator...') + else: + profile_name = path.basename(BASH_PROFILE) + if conf.confirm('bash-it.install', 'Do you want to install bash-it into your %s?' % profile_name): + print('Installing bash-it into %s...' % profile_name) + if path.exists(BASH_PROFILE) and conf.confirm('bash-it.backup', 'Do you want to back up your %s?' % profile_name, True): + shutil.copy(BASH_PROFILE, BASH_PROFILE + '.bak') + print("Your original .bash_profile has been backed up to %s.bak" % profile_name) + + shutil.copyfile(path.join(BASH_IT, 'template/bash_profile.template.bash'), BASH_PROFILE) + + print("Copied the template .bash_profile into %s, edit this file to customize bash-it" % BASH_PROFILE) + + + jekyll_config = path.join(HOME, '.jekyllconfig') + if path.exists(jekyll_config): + print('Jekyll config already installed.') + elif conf.confirm('jekyll.install', 'Do you use Jekyll? (If you don\'t know what Jekyll is, answer "n")'): + shutil.copyfile(path.join(BASH_IT, 'template/jekyllconfig.template.bash'), jekyll_config) + print("Copied the template .jekyllconfig to %s. Edit this file to customize bash-it for using the Jekyll plugins" % jekyll_config) + + for name, type in [('alias', 'aliases'), ('plugin', 'plugins'), ('completion', 'completion')]: + resp = conf.ask('%s.install' % type, 'Would you like to enable all, some, or no %s? Some of these may make bash slower to start up (especially completion)' % type, values=['all', 'some', 'none'], default='all') + install_dir = path.join(BASH_IT, type, 'enabled') + + if resp in ['all', 'some'] and not path.exists(install_dir): + os.makedirs(install_dir) + + if resp in ['all', 'none']: + config.remove_section(type) + conf.set(type + '.install', resp) + + for filename in glob(path.join(BASH_IT, type, 'available', '*')): + plugin_filename = path.basename(filename) + if plugin_filename.startswith('_'): + continue + + plugin_name, _ = plugin_filename.split('.', 1) + + install_file = path.join(install_dir, plugin_filename) + conf_key = '.'.join([type, plugin_name]) + + if resp == 'all' or (resp == 'some' and + conf.confirm(conf_key, 'Would you like to enable the %s %s' % (plugin_name, name))): + if path.exists(install_file): + os.remove(install_file) + + os.symlink(filename, install_file) + print(' Installed %s %s' % (plugin_name, name)) + elif path.exists(install_file): + os.remove(install_file) + print(' Removed %s %s' % (plugin_name, name)) + + if conf.prompt: + if confirm('Would you like to save your install preferences?'): + filename = configfile or 'pref.ini' + ans = raw_input('Where would you like to save to? [%s]:' % filename) + if not ans: + ans = filename + + ans = path.join(BASH_IT, ans) + + f = open(ans, 'w') + config.write(f) + f.close() + print('Saved preferences to %s' % ans) + + +if __name__ == '__main__': + parser = OptionParser() + parser.add_option('-c', '--config', dest='config', + help='Config file to use', metavar="FILE") + parser.add_option('-n', '--no-prompt', dest='no_prompt', action='store_true', + help='Install without prompting.') + + (options, args) = parser.parse_args() + + if options.no_prompt and not options.config: + parser.abort('You must supply a config to use without prompting.') + + try: + main(options.config, not options.no_prompt) + except KeyboardInterrupt: + print('\nCancelled by keyboard.')