diff --git a/bin/bin/stowage b/bin/bin/stowage index 40d1d6d..ccf5a5f 100755 --- a/bin/bin/stowage +++ b/bin/bin/stowage @@ -1,9 +1,13 @@ #!/usr/bin/env python3 """ -stowage -by Keith Gaughan -Stow, but in Python, and in a single file. +originally stowage, by Keith Gaughan +modified by Andrew Williams + +A dotfile package manager + Copyright (c) Keith Gaughan, 2017. +Copyright (c) Andrew Williams, 2021. + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to @@ -32,8 +36,8 @@ import sys def add(args): target = path.realpath(args.target) - file_path = path.realpath(args.add) - package = path.realpath(args.packages[0]) + file_path = path.realpath(args.file) + package = path.realpath(os.path.join(args.repository, args.packages[0])) if path.commonprefix([target, file_path]) != target: print(f"error: '{args.add}' not under '{args.target}'", file=sys.stderr) @@ -55,15 +59,16 @@ def add(args): def install(args, is_excluded): for package in args.packages: - if not path.isdir(package): + package_dir = os.path.join(args.repository, package) + if not path.isdir(package_dir): print(f"no such package: {package}; skipping", file=sys.stderr) continue - for root, _, files in os.walk(package, followlinks=True): + for root, _, files in os.walk(package_dir, followlinks=True): files = [ filename for filename in files if not is_excluded(filename)] if len(files) == 0: continue - rest = root[len(package) + 1:] + rest = root[len(package_dir) + 1:] dest = path.join(args.target, rest) if rest != "": if args.verbose: @@ -90,15 +95,16 @@ def install(args, is_excluded): def uninstall(args, is_excluded): dirs = [] for package in args.packages: - if not path.isdir(package): + package_dir = os.path.join(args.repository, package) + if not path.isdir(package_dir): print(f"no such package: {package}; skipping", file=sys.stderr) continue - for root, _, files in os.walk(package, followlinks=True): + for root, _, files in os.walk(package_dir, followlinks=True): files = [ filename for filename in files if not is_excluded(filename)] if len(files) == 0: continue - rest = root[len(package) + 1:] + rest = root[len(package_dir) + 1:] dest = path.join(args.target, rest) if rest != "": dirs.append(dest) @@ -128,15 +134,23 @@ def uninstall(args, is_excluded): def make_argparser(): - parser = argparse.ArgumentParser(description="A symlink farm manager.") + parser = argparse.ArgumentParser(description="A dotfile package manager.") parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output") + parser.add_argument("--dry-run", "-n", + action="store_true", help="Dry run.") parser.add_argument( "--target", "-t", default=os.path.expanduser('~'), help="Target directory in which to place symlinks", ) + parser.add_argument( + "--repository", + "-r", + default=os.path.expanduser('~/.dotfiles'), + help="The location of the dotfile repository", + ) parser.add_argument( "--exclude", "-x", @@ -145,46 +159,60 @@ def make_argparser(): metavar="GLOB", help="Glob pattern of files to exclude", ) - parser.add_argument("--dry-run", "-n", - action="store_true", help="Dry run.") - group = parser.add_mutually_exclusive_group(required=False) - group.add_argument( - "--uninstall", - "-D", - action="store_false", - dest="install", - help="Uninstall symlinks", - ) - group.add_argument( - "--add", "-a", metavar="FILE", help="Stow files in a particular package" - ) + subparsers = parser.add_subparsers(dest='command', help='sub-command help') - parser.add_argument( + # List + parser_add = subparsers.add_parser('list', help='List packages in the repository') + + # Add + parser_add = subparsers.add_parser('add', help='Add a file to a package') + parser_add.add_argument( + "file", metavar="FILE", help="File to stow" + ) + parser_add.add_argument( "packages", metavar="PACKAGE", nargs="+", help="Packages to install" ) + + # Uninstall + parser_uninstall = subparsers.add_parser('uninstall', help='Remove a package') + parser_uninstall.add_argument( + "packages", metavar="PACKAGE", nargs="+", help="Packages to uninstall" + ) + + # Install + parser_install = subparsers.add_parser('install', help='Install packages') + parser_install.add_argument( + "packages", metavar="PACKAGE", nargs="+", help="Packages to install" + ) + return parser def main(): parser = make_argparser() args = parser.parse_args() + if args.dry_run: + args.verbose = True exclude = [re.compile(fnmatch.translate(pattern)) for pattern in args.exclude] def is_excluded(filename): return any(pattern.match(filename) for pattern in exclude) - if args.add: + if args.command == 'list': + for dir in os.listdir(args.repository): + if os.path.isdir(os.path.join(args.repository, dir)) and dir[0] != '.': print(dir) + elif args.command == 'add': if len(args.packages) > 1: parser.error("--add only works with a single package") - args.add = path.normpath(path.join(args.target, args.add)) - if not path.isfile(args.add): - parser.error(f"no such file: {args.add}") + args.file = path.normpath(path.join(args.target, args.file)) + if not path.isfile(args.file): + parser.error(f"no such file: {args.file}") add(args) - elif args.install: + elif args.command == 'install': install(args, is_excluded) - else: + elif args.command == 'uninstall': uninstall(args, is_excluded) diff --git a/bootstrap.sh b/bootstrap.sh index 1692d7e..e89b8d5 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -10,18 +10,16 @@ fi git clone https://github.com/nikdoof/dotfiles.git $HOME/.dotfiles > /dev/null # Clean bash files -for file in .bash_profile .bashrc .bash_logout; do +for file in .bash_profile .bashrc .bash_logout .zshrc; do if [ -e $file ]; then rm -f $file fi done -cd $HOME/.dotfiles/ - # Stow the default packages -for package in bin bash; do +for package in bin bash zsh; do echo "Stowing ${package}" - ./bin/bin/stowage $package + $HOME/.dotfiles/bin/bin/stowage install $package done echo "" -echo "Done, either source ~/.bash_profile or restart your shell." \ No newline at end of file +echo "Done, either source ~/.bash_profile / ~/.zshrc or restart your shell." \ No newline at end of file