Mercurial > dotfiles
diff install.py @ 416:222b477ad678
Install script improvements.
- Support a local script that adds more installs.
- Add `--force` flag to force certain things.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Fri, 19 Jan 2018 09:19:34 -0800 |
parents | c6da0c9f40ae |
children | 6dbef23ca6bd |
line wrap: on
line diff
--- a/install.py Fri Jan 19 09:18:30 2018 -0800 +++ b/install.py Fri Jan 19 09:19:34 2018 -0800 @@ -2,6 +2,7 @@ import os.path import sys import stat +import shutil import argparse import functools import subprocess @@ -17,8 +18,13 @@ is_windows = True -def _p(*paths): - return os.path.join(dotfiles_dir, *paths).replace('/', os.sep) +def _p(*paths, force_unix=False): + res = os.path.join(dotfiles_dir, *paths) + if force_unix: + res = res.replace('\\', '/') + else: + res = res.replace('/', os.sep) + return res def nixslash(path): @@ -72,6 +78,11 @@ return decorator +def supports_forcing(f): + f.__dotfiles_supports_forcing__ = True + return f + + def needs_config(f): f.__dotfiles_needs_config__ = True return f @@ -147,7 +158,7 @@ def install_git(): writelines('~/.gitconfig', [ '[include]', - 'path = %s' % _p('git/gitconfig') + 'path = %s' % _p('git/gitconfig', force_unix=True) ]) if is_windows: subprocess.check_call( @@ -174,19 +185,39 @@ 'source "%s"' % _p('lib/mutt/mutt-colors-solarized/' 'mutt-colors-solarized-dark-256.muttrc') ]) + + +def _on_error_try_make_readable(func, path, exc_info): + if not os.access(path, os.W_OK): + os.chmod(path, stat.S_IWUSR) + func(path) + else: + raise -def clone_git(url, path): +def clone_git(url, path, force=False): if os.path.isdir(path): - print("Skipping git clone of %s -- directory exists" % path) + if not force: + print("Skipping git clone of %s -- directory exists" % path) + return + else: + print("Deleting existing: %s" % path) + shutil.rmtree(path, onerror=_on_error_try_make_readable) + print("git clone %s %s" % (url, path)) ensure_dir(os.path.dirname(path)) subprocess.check_call(['git', 'clone', url, path]) -def clone_hg(url, path): +def clone_hg(url, path, force=False): if os.path.isdir(path): - print("Skipping hg clone of %s -- directory exists" % path) + if not force: + print("Skipping hg clone of %s -- directory exists" % path) + return + else: + print("Deleting existing: %s" % path) + shutil.rmtree(path, onerror=_on_error_try_make_readable) + print("hg clone %s %s" % (url, path)) ensure_dir(os.path.dirname(path)) env = dict(os.environ) @@ -195,17 +226,18 @@ @needs_config +@supports_forcing @run_priority(100) -def install_subrepos(cfg): +def install_subrepos(cfg, force=False): if not cfg.has_section('subrepos'): return for path, url in cfg.items('subrepos'): full_path = _p(path) if url.startswith('[git]'): - clone_git(url[len('[git]'):], full_path) + clone_git(url[len('[git]'):], full_path, force=force) else: - clone_hg(url, full_path) + clone_hg(url, full_path, force=force) def main(): @@ -217,6 +249,7 @@ cfg = configparser.ConfigParser() cfg.read(_p('install.cfg')) + # Get all the methods in this module that are named `install_xxx`. mod_names = ['all'] this_mod = sys.modules[__name__] for an in dir(this_mod): @@ -225,14 +258,31 @@ name = an[len('install_'):] mod_names.append(name) + + # See if we have any local install script. + local_mod = None + local_install_py = os.path.join(dotfiles_dir, 'local', + 'local_install.py') + if os.path.isfile(local_install_py): + import importlib.util + spec = importlib.util.spec_from_file_location('local_install', + local_install_py) + local_mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(local_mod) + sys.modules['local_install'] = local_mod + # Create the parser, where you can specify one or more install target. parser = argparse.ArgumentParser() parser.add_argument( 'module', nargs='*', choices=mod_names, help="Which module(s) to install. Defaults to all modules.") + parser.add_argument( + '-f', '--force', action='store_true', + help="Force installation by overwriting things.") args = parser.parse_args() + # Get the list of methods to run. funcs = [] selected_mods = set(args.module) if 'all' in selected_mods: @@ -241,14 +291,29 @@ for mn in selected_mods: func = getattr(this_mod, 'install_%s' % mn) funcs.append((mn, func)) + # See if there's a local method too for this. + if local_mod is not None: + local_func = getattr(local_mod, 'install_%s' % mn, None) + if local_func is not None: + lmn = '%s (local)' % mn + funcs.append((lmn, local_func)) funcs = sorted(funcs, key=_get_install_func_priority, reverse=True) for name, func in funcs: print("Installing %s" % name) - if hasattr(func, '__dotfiles_needs_config__'): - func(cfg) - else: - func() + + f_args = [] + f_kwargs = {} + if getattr(func, '__dotfiles_needs_config__', False): + f_args.append(cfg) + if getattr(func, '__dotfiles_supports_forcing__', False): + f_kwargs['force'] = args.force + + try: + func(*f_args, **f_kwargs) + except Exception as ex: + print("ERROR: %s" % ex) + print("Aborting install.") def _get_install_func_priority(func_info):