mirror of
1
0
Fork 0

Add functionality to overwrite broken links

This commit adds an option to the extended configuration syntax for
linking files and directories. The relink option is a safe alternative
to forcibly linking that only removes broken symbolic links, so it
cannot result in data loss.
This commit is contained in:
Anish Athalye 2015-05-02 22:27:05 -04:00
parent db8364490d
commit 3725d21684
2 changed files with 22 additions and 10 deletions

View File

@ -131,8 +131,9 @@ Link commands support an (optional) extended configuration. In this type of
configuration, instead of specifying source locations directly, targets are configuration, instead of specifying source locations directly, targets are
mapped to extended configuration dictionaries. These dictionaries map `path` to mapped to extended configuration dictionaries. These dictionaries map `path` to
the source path, specify `create` as `true` if the parent directory should be the source path, specify `create` as `true` if the parent directory should be
created if necessary, and specify `force` as `true` if the file or directory created if necessary, specify `relink` as `true` if incorrect symbolic links
should be forcibly linked. should be automatically overwritten, and specify `force` as `true` if the file
or directory should be forcibly linked.
#### Example #### Example
@ -142,7 +143,9 @@ should be forcibly linked.
create: true create: true
path: config/terminator/ path: config/terminator/
~/.vim: vim/ ~/.vim: vim/
~/.vimrc: vimrc ~/.vimrc:
relink: true
path: vimrc
~/.zshrc: ~/.zshrc:
force: true force: true
path: zshrc path: zshrc

View File

@ -23,11 +23,14 @@ class Linker(Executor):
# extended config # extended config
path = source['path'] path = source['path']
force = source.get('force', False) force = source.get('force', False)
relink = source.get('relink', False)
create = source.get('create', False) create = source.get('create', False)
if create: if create:
success &= self._create(destination) success &= self._create(destination)
if force: if force:
success &= self._delete(path, destination) success &= self._delete(path, destination, force=True)
elif relink:
success &= self._delete(path, destination, force=False)
else: else:
path = source path = source
success &= self._link(path, destination) success &= self._link(path, destination)
@ -71,24 +74,30 @@ class Linker(Executor):
self._log.lowinfo('Creating directory %s' % parent) self._log.lowinfo('Creating directory %s' % parent)
return success return success
def _delete(self, source, path): def _delete(self, source, path, force):
success = True success = True
source = os.path.join(self._base_directory, source) source = os.path.join(self._base_directory, source)
if ((self._is_link(path) and self._link_destination(path) != source) or if ((self._is_link(path) and self._link_destination(path) != source) or
(self._exists(path) and not self._is_link(path))): (self._exists(path) and not self._is_link(path))):
fullpath = os.path.expanduser(path) fullpath = os.path.expanduser(path)
removed = False
try: try:
if os.path.islink(fullpath): if os.path.islink(fullpath):
os.unlink(fullpath) os.unlink(fullpath)
elif os.path.isdir(fullpath): removed = True
shutil.rmtree(fullpath) elif force:
else: if os.path.isdir(fullpath):
os.remove(fullpath) shutil.rmtree(fullpath)
removed = True
else:
os.remove(fullpath)
removed = True
except OSError: except OSError:
self._log.warning('Failed to remove %s' % path) self._log.warning('Failed to remove %s' % path)
success = False success = False
else: else:
self._log.lowinfo('Removing %s' % path) if removed:
self._log.lowinfo('Removing %s' % path)
return success return success
def _link(self, source, link_name): def _link(self, source, link_name):