comparison install.py @ 472:97412ea9b3fa

Improve install script, add scoop apps on Windows.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 28 Jul 2019 22:26:24 -0700
parents 31079b060068
children e368c8ae2a4b
comparison
equal deleted inserted replaced
471:31079b060068 472:97412ea9b3fa
7 import functools 7 import functools
8 import subprocess 8 import subprocess
9 import configparser 9 import configparser
10 10
11 11
12 # Utility stuff.
13
12 dotfiles_dir = os.path.abspath(os.path.dirname(__file__)) 14 dotfiles_dir = os.path.abspath(os.path.dirname(__file__))
13 15
14 is_nix = True 16 is_nix = True
15 is_mac = False 17 is_mac = False
16 is_windows = False 18 is_windows = False
61 elif os.path.exists(link_full_path): 63 elif os.path.exists(link_full_path):
62 print("Removing %s" % link_full_path) 64 print("Removing %s" % link_full_path)
63 os.remove(link_full_path) 65 os.remove(link_full_path)
64 66
65 print("%s -> %s" % (link_full_path, orig_full_path)) 67 print("%s -> %s" % (link_full_path, orig_full_path))
66 os.symlink(orig_full_path, link_full_path) 68 if not is_windows or os.path.isfile(orig_full_path):
69 os.symlink(orig_full_path, link_full_path)
70 else:
71 subprocess.check_call(['mklink', '/J', link_full_path, orig_full_path], shell=True)
67 if mode is not None: 72 if mode is not None:
68 os.chmod(link_full_path, mode) 73 os.chmod(link_full_path, mode)
69 74
70 75
71 def writelines(path, lines): 76 def writelines(path, lines):
84 89
85 def rmtree(dirpath): 90 def rmtree(dirpath):
86 shutil.rmtree(dirpath, onerror=_on_rmtree_err) 91 shutil.rmtree(dirpath, onerror=_on_rmtree_err)
87 92
88 93
94 # Installer method decorators.
95
89 def only_on_nix(f): 96 def only_on_nix(f):
90 @functools.wraps(f) 97 @functools.wraps(f)
91 def decorator(*args, **kwargs): 98 def decorator(*args, **kwargs):
92 if is_nix: 99 if is_nix:
93 return f(*args, **kwargs) 100 return f(*args, **kwargs)
124 def wrapper(f): 131 def wrapper(f):
125 f.__dotfiles_priority__ = prio 132 f.__dotfiles_priority__ = prio
126 return f 133 return f
127 return wrapper 134 return wrapper
128 135
136
137 # Installer methods.
129 138
130 @only_on_nix 139 @only_on_nix
131 def install_bash(): 140 def install_bash():
132 mklink('bashrc/bashrc', '.bashrc') 141 mklink('bashrc/bashrc', '.bashrc')
133 mklink('bashrc/bash_profile', '.bash_profile') 142 mklink('bashrc/bash_profile', '.bash_profile')
386 if force: 395 if force:
387 args.append('--force') 396 args.append('--force')
388 subprocess.check_call(args) 397 subprocess.check_call(args)
389 398
390 399
400 @only_on_win
401 @run_priority(209)
402 def install_scoop():
403 if shutil.which('scoop') is None:
404 print("Installing Scoop")
405 subprocess.check_call(
406 '@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" '
407 '-NoProfile -InputFormat None -ExecutionPolicy Bypass '
408 '-Command "iex (new-object net.webclient).downloadstring(\'https://get.scoop.sh\')"')
409 subprocess.check_call(
410 ['scoop.cmd', 'bucket', 'add', 'extras'])
411 else:
412 print("Scoop is already installed")
413
414 @only_on_win
415 @needs_config
416 @supports_forcing
417 @run_priority(208)
418 def install_wintools(cfg, force=False):
419 if not cfg.has_section('wintools'):
420 return
421
422 for name, arch in cfg.items('wintools'):
423 args = ['scoop.cmd', 'install', name]
424 if arch:
425 args += ['-a', arch]
426 subprocess.check_call(args)
427
428
429 # Main stuff!
430
431 class FatalInstallerError(Exception):
432 pass
433
434
391 def main(): 435 def main():
392 print("dotfiles installer") 436 print("dotfiles installer")
393 print("python %s" % sys.version) 437 print("python %s" % sys.version)
394 print("on %s" % sys.platform) 438 print("on %s" % sys.platform)
395 print('') 439 print('')
421 465
422 # Create the parser, where you can specify one or more install target. 466 # Create the parser, where you can specify one or more install target.
423 parser = argparse.ArgumentParser() 467 parser = argparse.ArgumentParser()
424 parser.add_argument( 468 parser.add_argument(
425 'module', nargs='*', 469 'module', nargs='*',
426 choices=mod_names,
427 help="Which module(s) to install. Defaults to all modules.") 470 help="Which module(s) to install. Defaults to all modules.")
471 parser.add_argument(
472 '-l', '--list', action='store_true',
473 help="List available modules to install.")
428 parser.add_argument( 474 parser.add_argument(
429 '-f', '--force', action='store_true', 475 '-f', '--force', action='store_true',
430 help="Force installation by overwriting things.") 476 help="Force installation by overwriting things.")
431 args = parser.parse_args() 477 args = parser.parse_args()
478
479 # Print list and exit if needed.
480 if args.list:
481 print("Available modules to install:")
482 for nm in mod_names:
483 print(nm)
484 print()
485 print("Specify 'all' to install all modules.")
486 return
432 487
433 # Get the list of methods to run. 488 # Get the list of methods to run.
434 funcs = [] 489 funcs = []
435 selected_mods = set(args.module) 490 selected_mods = set(args.module)
436 if 'all' in selected_mods: 491 if 'all' in selected_mods:
437 selected_mods = set(mod_names) 492 selected_mods = set(mod_names)
438 selected_mods.remove('all') 493 selected_mods.remove('all')
494 selected_mods.difference_update([neg[3:] for neg in args.module if neg.startswith('no-')])
495
439 for mn in selected_mods: 496 for mn in selected_mods:
440 func = getattr(this_mod, 'install_%s' % mn) 497 func = getattr(this_mod, 'install_%s' % mn)
441 funcs.append((mn, func)) 498 funcs.append((mn, func))
442 # See if there's a local method too for this. 499 # See if there's a local method too for this.
443 if local_mod is not None: 500 if local_mod is not None:
459 f_kwargs['force'] = args.force 516 f_kwargs['force'] = args.force
460 517
461 try: 518 try:
462 func(*f_args, **f_kwargs) 519 func(*f_args, **f_kwargs)
463 except Exception as ex: 520 except Exception as ex:
521 failed_installs.append(name)
464 print("ERROR: %s" % ex) 522 print("ERROR: %s" % ex)
465 print("Skipping install of '%s'." % name) 523 if isinstance(ex, FatalInstallerError):
466 failed_installs.append(name) 524 print("Aborting all remaining installs because '%s' failed!" % name)
525 break
526 else:
527 print("Skipping install of '%s'." % name)
467 if failed_installs: 528 if failed_installs:
468 for name in failed_installs: 529 for name in failed_installs:
469 print("ERROR: failed to install '%s'." % name) 530 print("ERROR: failed to install '%s'." % name)
470 531
471 532