diff --git a/autolink b/autolink index 8e53b40..7721ac9 100755 --- a/autolink +++ b/autolink @@ -1,62 +1,80 @@ -#!/usr/bin/env python2 -""" -A simple script to create a symlinks in your home directory for every filename -in this directory. - -For example: - ~/.bashrc -> dot-files/bashrc -""" - -from os import getcwd, listdir, getenv, symlink, remove -from os.path import (dirname, basename, join, expanduser, lexists, exists, - relpath, normpath, isfile, islink, realpath, abspath) - -def log(filename, message): - """Simple logging function""" - name = "'{0}':".format(filename).ljust(30, ' ') - print "{0}\t{0}".format(name, message) - -def dir_filter(file_name): - return file_name != basename(__file__) and \ - file_name != 'README.md' and \ - file_name[0] != '.' - -def main(): - """Create the symlinks""" - dir_path = dirname(__file__) - base_name = dirname(normpath(join(getenv('PWD'), __file__))) - home_dir = expanduser('~') - rel_path = normpath(relpath(base_name, home_dir)) - - for filename in filter(dir_filter, listdir(dirname(abspath(__file__)))): - config_path = join(home_dir, ".{0}".format(filename)) - file_path = join(rel_path, filename) - - # If the current file is going to be linked deeper in the - # home directory, for example, we want to link - # `~/.ssh/config` instead of the whole `~/.ssh` directory. - if filename[0] == '_': - deep_dirs = filename[1:].split('_') - link_file = deep_dirs[-1] - dir_name = join(*deep_dirs[:-1]) - config_path = join(home_dir, ".{0}".format(dir_name), link_file) - file_path = join("..", file_path) - - if lexists(config_path): - if not exists(config_path): - # If it does not exists but lexists is true, that means this - # is a broken symlink - remove(config_path) - elif islink(config_path): - if realpath(config_path) != file_path: - continue - elif isfile(config_path): - log(config_path, 'existing file, consider merge') +#!/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) + link_to(dot_file, new_file) + +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) - log(config_path, "now linking to '{0}'".format(file_path)) - symlink(file_path, config_path) +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() - diff --git a/_ssh_config b/ssh/config similarity index 100% rename from _ssh_config rename to ssh/config