Mercurial > dotfiles
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 |