#!/usr/bin/env python3 import os from os import path SCRIPT_ABS_PATH = path.abspath(__file__) SCRIPT_NAME = path.basename(SCRIPT_ABS_PATH) DOT_FILES_DIR = path.dirname(SCRIPT_ABS_PATH) HOME_DIR = path.expanduser('~') class ExistingFileError(OSError): pass class ExistingSymlinkError(OSError): pass def main(*, dot_files=DOT_FILES_DIR, home_dir=HOME_DIR): for directory in walk_directories(dot_files): hidden_dir = path.join(home_dir, '.' + directory) make_directory(hidden_dir) for filename in walk_files(dot_files): dot_file = path.join(dot_files, filename) new_file = path.join(home_dir, '.' + filename) try: link_to(dot_file, new_file) except ExistingSymlinkError as err: print(err) def walk_directories(directory): visited = set() for root, dirs, _ in os.walk(directory): for d in (path.join(root, d) for d in dirs): rel_dir = path.join(root, d).replace(directory + os.sep, '') if rel_dir in visited: continue if path_contains(rel_dir, '.git'): continue yield rel_dir visited.add(rel_dir) def walk_files(directory): visited = set() for root, _, files in os.walk(directory): for f in (path.join(root, f) for f in files if not f.startswith('.')): relative_file = path.join(root, f).replace(directory + os.sep, '') if relative_file in visited: continue if path_contains(relative_file, '.git', 'README.md', SCRIPT_NAME): continue yield relative_file visited.add(relative_file) def make_directory(directory): if path.isdir(directory): return if path.exists(directory): raise ( ExistingFileError('expected no file or directory: {0}'.format(directory)) ) os.mkdir(directory, mode=0o755) def link_to(src, dest): if path.islink(dest): if path.realpath(src) == path.realpath(dest): return if path.exists(dest): raise ( ExistingSymlinkError('found unexpected symlink: {0}'.format(dest)) ) # we have a broken symlink, remove it os.remove(dest) if path.isfile(dest): raise ExistingSymlinkError('found unexpected file: {0}'.format(dest)) src_rel = path.relpath(src, start=path.dirname(dest)) dir_fd = os.open(path.dirname(dest), os.O_RDONLY) os.symlink(src_rel, dest, dir_fd=dir_fd) def path_contains(directory, *strings): directories = directory.split(os.sep) for s in strings: if s in directories: return True return False if __name__ == '__main__': main()