diff install.py @ 407:c6da0c9f40ae

Replace subrepos with an install script. Finally.
author Ludovic Chabant <ludovic@chabant.com>
date Wed, 10 Jan 2018 00:00:00 -0800
parents
children 222b477ad678
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/install.py	Wed Jan 10 00:00:00 2018 -0800
@@ -0,0 +1,260 @@
+import os
+import os.path
+import sys
+import stat
+import argparse
+import functools
+import subprocess
+import configparser
+
+
+dotfiles_dir = os.path.abspath(os.path.dirname(__file__))
+
+is_nix = True
+is_windows = False
+if sys.platform == "win32":
+    is_nix = False
+    is_windows = True
+
+
+def _p(*paths):
+    return os.path.join(dotfiles_dir, *paths).replace('/', os.sep)
+
+
+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 only_on_nix(f):
+    @functools.wraps(f)
+    def decorator(*args, **kwargs):
+        if is_nix:
+            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 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')
+    mklink('fish', '~/.config/fish')
+
+
+def install_vim():
+    vimrc_path = '~/.vimrc'
+    if is_windows:
+        vimrc_path = '~/_vimrc'
+    writelines(vimrc_path, [
+        'set runtimepath+=%s' % nixslash(_p('vim')),
+        'source %s' % nixslash(_p('vim', 'vimrc'))
+    ])
+
+
+@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/mutable-history/hgext3rd/evolve'),
+        'terse-status = %s' % _p('lib/hg/terse-status/terse-status.py')
+    ])
+    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')
+    ])
+    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 clone_git(url, path):
+    if os.path.isdir(path):
+        print("Skipping git clone of %s -- directory exists" % path)
+    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):
+    if os.path.isdir(path):
+        print("Skipping hg clone of %s -- directory exists" % path)
+    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
+@run_priority(100)
+def install_subrepos(cfg):
+    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)
+        else:
+            clone_hg(url, full_path)
+
+
+def main():
+    print("dotfiles installer")
+    print("python %s" % sys.version)
+    print("on %s" % sys.platform)
+    print('')
+
+    cfg = configparser.ConfigParser()
+    cfg.read(_p('install.cfg'))
+
+    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)
+
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        'module', nargs='*',
+        choices=mod_names,
+        help="Which module(s) to install. Defaults to all modules.")
+    args = parser.parse_args()
+
+    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))
+
+    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()
+
+
+def _get_install_func_priority(func_info):
+    func = func_info[1]
+    return getattr(func, '__dotfiles_priority__', 0)
+
+
+if __name__ == '__main__':
+    main()