Mercurial > dotfiles
view install.py @ 459:b302f58a56aa
Add srht remote paths
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sat, 06 Apr 2019 18:20:39 -0700 |
parents | 1172b8484c68 |
children | 3b9394a0a58b |
line wrap: on
line source
import os import os.path import sys import stat import shutil import argparse import functools import subprocess import configparser dotfiles_dir = os.path.abspath(os.path.dirname(__file__)) is_nix = True is_mac = False is_windows = False if sys.platform == "win32": is_nix = False is_windows = True if sys.platform == 'darwin': is_mac = True 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): return path.replace('\\', '/') def ensure_dir(path): full_path = os.path.abspath(os.path.expanduser(path)) if not os.path.isdir(full_path): os.makedirs(full_path, mode=0o700) def mklink(orig_rel_path, link_path, mode=None): orig_full_path = os.path.join(dotfiles_dir, orig_rel_path) link_full_path = os.path.abspath(os.path.expanduser(link_path)) if os.path.islink(link_full_path): print("Unlinking %s" % link_full_path) os.unlink(link_full_path) elif os.path.exists(link_full_path): print("Removing %s" % link_full_path) os.remove(link_full_path) print("%s -> %s" % (link_full_path, orig_full_path)) os.symlink(orig_full_path, link_full_path) if mode is not None: os.chmod(link_full_path, mode) def writelines(path, lines): full_path = os.path.abspath(os.path.expanduser(path)) print("%d lines to %s" % (len(lines), full_path)) with open(full_path, 'w') as fp: for l in lines: fp.write(l) fp.write('\n') def _on_rmtree_err(func, name, excinfo): os.chmod(name, stat.S_IWUSR | stat.S_IWGRP) os.remove(name) def rmtree(dirpath): shutil.rmtree(dirpath, onerror=_on_rmtree_err) def only_on_nix(f): @functools.wraps(f) def decorator(*args, **kwargs): if is_nix: return f(*args, **kwargs) return decorator def only_on_mac(f): @functools.wraps(f) def decorator(*args, **kwargs): if is_mac: return f(*args, **kwargs) return decorator def only_on_win(f): @functools.wraps(f) def decorator(*args, **kwargs): if is_windows: return f(*args, **kwargs) return decorator def supports_forcing(f): f.__dotfiles_supports_forcing__ = True return f def needs_config(f): f.__dotfiles_needs_config__ = True return f def run_priority(prio): def wrapper(f): f.__dotfiles_priority__ = prio return f return wrapper @only_on_nix def install_bash(): mklink('bashrc/bashrc', '.bashrc') mklink('bashrc/bash_profile', '.bash_profile') @only_on_nix def install_fish(): ensure_dir('~/.config/fish') writelines('~/.config/fish/config.fish', ['source %s' % _p('fish', 'config.fish')]) @needs_config @supports_forcing def install_vim(cfg, force=False): vimrc_path = '~/.vimrc' if is_windows: vimrc_path = '~/_vimrc' writelines(vimrc_path, [ 'set runtimepath+=%s' % nixslash(_p('vim')), 'source %s' % nixslash(_p('vim', 'vimrc')) ]) if cfg.has_section('vimbundles'): bundle_dir = _p('vim', 'bundle') os.makedirs(bundle_dir, exist_ok=True) existing_plugins = set(os.listdir(bundle_dir)) for name, url in cfg.items('vimbundles'): path = os.path.join(bundle_dir, name) if url.startswith('[git]'): clone_git(url[len('[git]'):], path, force=force) else: clone_hg(url, path, force=force) existing_plugins.discard(name) for name in existing_plugins: print("Removing plugin %s" % name) path = os.path.join(bundle_dir, name) rmtree(path) @run_priority(2) # Needs to run before `fish`. def install_mercurial(): hgrc_path = '~/.hgrc' if is_windows: hgrc_path = '~/mercurial.ini' writelines(hgrc_path, [ '%%include %s' % _p('hgrc/hgrc'), '[ui]', 'ignore = %s' % _p('hgrc/hgignore'), '[subrepos]', 'git:allowed = true', '[extensions]', 'hggit = %s' % _p('lib/hg/hg-git/hggit/'), 'onsub = %s' % _p('lib/hg/onsub/onsub.py'), 'allpaths = %s' % _p('lib/hg/allpaths/mercurial_all_paths.py'), 'prompt = %s' % _p('lib/hg/hg-prompt/prompt.py'), 'evolve = %s' % _p('lib/hg/evolve/hgext3rd/evolve'), '[alias]', ('dlog = log --pager=yes --style=%s' % _p('lib/hg/mercurial-cli-templates/map-cmdline.dlog', force_unix=True)), ('slog = log --pager=yes --style=%s' % _p('lib/hg/mercurial-cli-templates/map-cmdline.slog', force_unix=True)), ('nlog = log --pager=yes --style=%s' % _p('lib/hg/mercurial-cli-templates/map-cmdline.nlog', force_unix=True)), ('sglog = glog --pager=yes --style=%s' % _p('lib/hg/mercurial-cli-templates/map-cmdline.sglog', force_unix=True)), ('nglog = glog --pager=yes --style=%s' % _p('lib/hg/mercurial-cli-templates/map-cmdline.nlog', force_unix=True)), ('blog = glog --page=yes --style=%s' % _p('hgrc/logstyles')), ('wip = glog --pager=yes --style=%s --rev wip' % _p('hgrc/wip.style', force_unix=True)) ]) if is_nix: print("Building fast-hg-prompt...") compile_ok = True try: subprocess.check_call(['make'], cwd=_p('lib/hg/fast-hg-prompt')) except subprocess.CalledProcessError: compile_ok = False for n in ['bookmark', 'remote', 'status']: link_path = os.path.expanduser('~/.local/bin/fast-hg-%s' % n) if compile_ok: mklink('lib/hg/fast-hg-prompt/fast-hg-%s' % n, link_path, mode=(stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)) elif os.path.islink(link_path): os.unlink(link_path) elif os.path.exists(link_path): os.remove(link_path) def install_git(): writelines('~/.gitconfig', [ '[include]', ' path = %s' % _p('git/gitconfig', force_unix=True), '[core]', ' excludesfile = %s' % _p('git/gitignore', force_unix=True) ]) if is_windows: subprocess.check_call( ['setx', 'GIT_SSH', '%USERPROFILE%\\Dropbox\\Utilities\\plink.exe'], shell=True) @only_on_nix def install_tmux(): mklink('tmux/tmux.conf', '~/.tmux.conf') @only_on_nix def install_weechat(): mklink('weechat', '~/.weechat') @only_on_nix def install_mutt(): writelines('~/.muttrc', [ 'source "gpg2 -dq %s |"' % _p('mutt/variables.gpg'), 'source "%s"' % _p('mutt/muttrc'), 'source "%s"' % _p('lib/mutt/mutt-colors-solarized/' 'mutt-colors-solarized-dark-256.muttrc') ]) def install_tridactyl(): cfgname = '~/_tridactylrc' if is_windows else '~/.tridactylrc' writelines(cfgname, [ 'source %s' % _p('tridactyl/tridactylrc') ]) 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 _is_non_empty_dir_with(path, contains=None): if not os.path.isdir(path): return False if isinstance(contains, str): contains = [contains] for cnt in contains: if not os.path.exists(os.path.join(path, cnt)): return False return True def clone_git(url, path, force=False): if _is_non_empty_dir_with(path, '.git'): if not force: print("git pull origin master %s" % path) subprocess.check_call(['git', 'pull', 'origin', 'master'], cwd=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, force=False): if _is_non_empty_dir_with(path, '.hg'): if not force: print("hg pull -u %s" % path) subprocess.check_call(['hg', 'pull', '-u'], cwd=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) env.update({'HGPLAIN': '1'}) subprocess.check_call(['hg', 'clone', url, path], env=env) @needs_config @supports_forcing @run_priority(100) 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, force=force) else: clone_hg(url, full_path, force=force) @only_on_mac @run_priority(210) def install_xcode(): if shutil.which('xcodebuild') is None: print("Installing XCode") subprocess.check_call(['xcode-select', '--install']) subprocess.check_call(['sudo', 'xcodebuild', '-license', 'accept']) @only_on_mac @run_priority(209) def install_homebrew(): if shutil.which('brew') is None: print("Installing Homebrew and Homebrew Cask") subprocess.check_call([ '/usr/bin/ruby', '-e', "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"]) # NOQA subprocess.check_call(['brew', 'tap', 'caskroom/fonts']) @only_on_mac @needs_config @supports_forcing @run_priority(208) def install_mactools(cfg, force=False): if not cfg.has_section('mactools'): return for name, _ in cfg.items('mactools'): args = ['brew', 'install', name] if force: args.append('--force') subprocess.check_call(args) def main(): print("dotfiles installer") print("python %s" % sys.version) print("on %s" % sys.platform) print('') 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): if not an.startswith('install_'): continue 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: selected_mods = set(mod_names) selected_mods.remove('all') 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)) failed_installs = [] funcs = sorted(funcs, key=_get_install_func_priority, reverse=True) for name, func in funcs: print("Installing %s" % name) 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("Skipping install of '%s'." % name) failed_installs.append(name) if failed_installs: for name in failed_installs: print("ERROR: failed to install '%s'." % name) def _get_install_func_priority(func_info): func = func_info[1] return getattr(func, '__dotfiles_priority__', 0) if __name__ == '__main__': main()