diff install.py @ 487:5bbc05a69f4c

Improvements to installation script. - Support for local scripts and configurations. - Support for local vim bundle. - Better error reporting.
author Ludovic Chabant <ludovic@chabant.com>
date Thu, 24 Sep 2020 23:14:46 -0700
parents 91652f4b9752
children b4d2eca00197
line wrap: on
line diff
--- a/install.py	Thu Sep 24 23:13:07 2020 -0700
+++ b/install.py	Thu Sep 24 23:14:46 2020 -0700
@@ -5,6 +5,7 @@
 import shutil
 import argparse
 import functools
+import traceback
 import subprocess
 import configparser
 
@@ -149,6 +150,36 @@
                ['source %s' % _p('fish', 'config.fish')])
 
 
+def _install_vim_bundle(cfg, cfg_section_name, bundle_dir, force=False):
+    if not cfg.has_section(cfg_section_name):
+        return
+
+    os.makedirs(bundle_dir, exist_ok=True)
+
+    # Keep track of lowercase directory names because the cfg parser stores
+    # config section names in lowercase.
+    existing_plugins = dict([(d.lower(), d) for d in os.listdir(bundle_dir)])
+
+    for name, url in cfg.items(cfg_section_name):
+        path = os.path.join(bundle_dir, name)
+        if url.startswith('[local]'):
+            pass
+        elif url.startswith('[git]'):
+            clone_git(url[len('[git]'):], path, force=force)
+        else:
+            clone_hg(url, path, force=force)
+        print()
+
+        existing_plugins.pop(name, None)
+
+    for k, name in existing_plugins.items():
+        print("Removing plugin %s" % name)
+        ok = input("OK? [Y/n]")
+        if ok.lower() == "y":
+            path = os.path.join(bundle_dir, name)
+            rmtree(path)
+
+
 @needs_config
 @supports_forcing
 def install_vim(cfg, force=False):
@@ -160,24 +191,8 @@
         '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)
+    _install_vim_bundle(cfg, 'vimbundles', _p('vim', 'bundle'), force)
+    _install_vim_bundle(cfg, 'vimbundles:local', _p('vim', 'local'), force)
 
 
 @run_priority(2)   # Needs to run before `fish`.
@@ -368,6 +383,7 @@
             clone_git(url[len('[git]'):], full_path, force=force)
         else:
             clone_hg(url, full_path, force=force)
+        print()
 
 
 @only_on_mac
@@ -447,7 +463,9 @@
     print('')
 
     cfg = configparser.ConfigParser()
-    cfg.read(_p('install.cfg'))
+    cfg.read([
+        _p('install.cfg'),
+        _p('local', 'local_install.cfg')])
 
     # Get all the methods in this module that are named `install_xxx`.
     mod_names = ['all']
@@ -461,8 +479,7 @@
 
     # See if we have any local install script.
     local_mod = None
-    local_install_py = os.path.join(dotfiles_dir, 'local',
-                                    'local_install.py')
+    local_install_py = _p('local', 'local_install.py')
     if os.path.isfile(local_install_py):
         import importlib.util
         spec = importlib.util.spec_from_file_location('local_install',
@@ -526,16 +543,25 @@
         try:
             func(*f_args, **f_kwargs)
         except Exception as ex:
-            failed_installs.append(name)
+            failed_installs.append((name, sys.exc_info()))
             print("ERROR: %s" % ex)
+            traceback.print_exc()
             if isinstance(ex, FatalInstallerError):
                 print("Aborting all remaining installs because '%s' failed!" % name)
                 break
             else:
                 print("Skipping install of '%s'." % name)
+
+        print()
+
     if failed_installs:
-        for name in failed_installs:
+        print()
+        print("----------------------------------")
+        print("ERROR: There were failed installs!")
+        for name, ex_info in failed_installs:
             print("ERROR: failed to install '%s'." % name)
+            print("ERROR: %s" % ex_info[1])
+            traceback.print_exception(*ex_info)
 
 
 def _get_install_func_priority(func_info):