From c92df33bb019f0e0363f13e3eabd77dd9ca9582d Mon Sep 17 00:00:00 2001 From: Meutel Date: Sun, 11 Nov 2018 09:27:20 +0100 Subject: [PATCH] Dotfile manager python 2.7 --- dotfile-manager__hellaynnea | 204 ++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100755 dotfile-manager__hellaynnea diff --git a/dotfile-manager__hellaynnea b/dotfile-manager__hellaynnea new file mode 100755 index 0000000..6e6fbee --- /dev/null +++ b/dotfile-manager__hellaynnea @@ -0,0 +1,204 @@ +#!/usr/bin/env python2.7 +"""dotfilemanager.py - a dotfiles manager script. See --help for usage +and command-line arguments. + +""" +import os,sys,platform + +# TODO: allow setting hostname as a command-line argument also? +try: + HOSTNAME = os.environ['DOTFILEMANAGER_HOSTNAME'] +except KeyError: + HOSTNAME = platform.node() +HOSTNAME_SEPARATOR = '__' + +def tidy(d,report=False): + """Find and delete any broken symlinks in directory d. + + Arguments: + d -- The directory to consider (absolute path) + + Keyword arguments: + report -- If report is True just report on what broken symlinks are + found, don't attempt to delete them (default: False) + + """ + for f in os.listdir(d): + path = os.path.join(d,f) + if os.path.islink(path): + target_path = os.readlink(path) + target_path = os.path.abspath(os.path.expanduser(target_path)) + if not os.path.exists(target_path): + # This is a broken symlink. + if report: + print 'tidy would delete broken symlink: %s->%s' % (path,target_path) + else: + print 'Deleting broken symlink: %s->%s' % (path,target_path) + os.remove(path) + +def get_target_paths(to_dir,report=False): + """Return the list of absolute paths to link to for a given to_dir. + + This handles skipping various types of filename in to_dir and + resolving host-specific filenames. + + """ + paths = [] + filenames = os.listdir(to_dir) + for filename in filenames: + path = os.path.join(to_dir,filename) + if filename.endswith('~'): + if report: + print 'Skipping %s' % filename + continue + elif (not os.path.isfile(path)) and (not os.path.isdir(path)): + if report: + print 'Skipping %s (not a file or directory)' % filename + continue + elif filename.startswith('.'): + if report: + print 'Skipping %s (filename has a leading dot)' % filename + continue + else: + if HOSTNAME_SEPARATOR in filename: + # This appears to be a filename with a trailing + # hostname, e.g. _muttrc__dulip. If the trailing + # hostname matches the hostname of this host then we + # link to it. + hostname = filename.split(HOSTNAME_SEPARATOR)[-1] + if hostname == HOSTNAME: + paths.append(path) + else: + if report: + print 'Skipping %s (different hostname)' % filename + continue + else: + # This appears to be a filename without a trailing + # hostname. + if filename + HOSTNAME_SEPARATOR + HOSTNAME in filenames: + if report: + print 'Skipping %s (there is a host-specific version of this file for this host)' % filename + continue + else: + paths.append(path) + return paths + +def link(from_dir,to_dir,report=False): + """Make symlinks in from_dir to each file and directory in to_dir. + + This handles converting leading underscores in to_dir to leading + dots in from_dir. + + Arguments: + from_dir -- The directory in which symlinks will be created (string, + absolute path) + to_dir -- The directory containing the files and directories that + will be linked to (string, absolute path) + + Keyword arguments: + report -- If report is True then only report on the status of + symlinks in from_dir, don't actually create any new + symlinks (default: False) + + """ + # The paths in to_dir that we will be symlinking to. + to_paths = get_target_paths(to_dir,report) + + # Dictionary of symlinks we will be creating, from_path->to_path + symlinks = {} + for to_path in to_paths: + to_directory, to_filename = os.path.split(to_path) + # Change leading underscores to leading dots. + if to_filename.startswith('_'): + from_filename = '.' + to_filename[1:] + else: + from_filename = to_filename + # Remove hostname specifiers. + parts = from_filename.split(HOSTNAME_SEPARATOR) + assert len(parts) == 1 or len(parts) == 2 + from_filename = parts[0] + from_path = os.path.join(from_dir,from_filename) + symlinks[from_path] = to_path + + # Attempt to create the symlinks that don't already exist. + for from_path,to_path in symlinks.items(): + # Check that nothing already exists at from_path. + if os.path.islink(from_path): + # A link already exists. + existing_to_path = os.readlink(from_path) + existing_to_path = os.path.abspath(os.path.expanduser(existing_to_path)) + if existing_to_path == to_path: + # It's already a link to the intended target. All is + # well. + continue + else: + # It's a link to somewhere else. + print from_path+" => is already symlinked to "+existing_to_path + elif os.path.isfile(from_path): + print "There's a file in the way at "+from_path + elif os.path.isdir(from_path): + print "There's a directory in the way at "+from_path + elif os.path.ismount(from_path): + print "There's a mount point in the way at "+from_path + else: + # The path is clear, make the symlink. + if report: + print 'link would make symlink: %s->%s' % (from_path,to_path) + else: + print 'Making symlink %s->%s' % (from_path,to_path) + os.symlink(to_path,from_path) + +def usage(): + return """Usage: + +dotfilemanager link|tidy|report [FROM_DIR [TO_DIR]] + +Commands: + link -- make symlinks in FROM_DIR to files and directories in TO_DIR + tidy -- remove broken symlinks from FROM_DIR + report -- report on symlinks in FROM_DIR and files and directories in TO_DIR + +FROM_DIR defaults to ~ and TO_DIR defaults to ~/.dotfiles. + """ + +if __name__ == "__main__": + try: + ACTION = sys.argv[1] + except IndexError: + print usage() + sys.exit(2) + + try: + FROM_DIR = sys.argv[2] + except IndexError: + FROM_DIR = '~' + FROM_DIR = os.path.abspath(os.path.expanduser(FROM_DIR)) + + if not os.path.isdir(FROM_DIR): + print "FROM_DIR %s is not a directory!" % FROM_DIR + print usage() + sys.exit(2) + + if ACTION == 'tidy': + tidy(FROM_DIR) + else: + + try: + TO_DIR = sys.argv[3] + except IndexError: + TO_DIR = os.path.join('~','.dotfiles') + TO_DIR = os.path.abspath(os.path.expanduser(TO_DIR)) + + if not os.path.isdir(TO_DIR): + print "TO_DIR %s is not a directory!" % TO_DIR + print usage() + sys.exit(2) + + if ACTION == 'link': + link(FROM_DIR,TO_DIR) + elif ACTION == 'report': + link(FROM_DIR,TO_DIR,report=True) + tidy(FROM_DIR,report=True) + else: + print usage() + sys.exit(2)