1
0
Fork 0
mirror of synced 2024-11-17 22:15:35 -05:00
dotbot/plugins/link.py

148 lines
5.6 KiB
Python
Raw Normal View History

2016-01-16 22:00:15 -05:00
import os, shutil, dotbot
2014-03-19 23:07:30 -04:00
2016-01-16 22:00:15 -05:00
class Link(dotbot.Plugin):
2014-03-19 23:07:30 -04:00
'''
Symbolically links dotfiles.
'''
2014-04-24 15:41:34 -04:00
_directive = 'link'
2014-03-19 23:07:30 -04:00
def can_handle(self, directive):
2014-04-24 15:41:34 -04:00
return directive == self._directive
2014-03-19 23:07:30 -04:00
2016-02-27 00:43:11 -05:00
def handle(self, directive, data, defaults):
2014-04-24 15:41:34 -04:00
if directive != self._directive:
2016-01-16 22:00:15 -05:00
raise ValueError('Link cannot handle directive %s' % directive)
2016-02-27 00:43:11 -05:00
return self._process_links(data, defaults)
2014-03-19 23:07:30 -04:00
2016-02-27 00:43:11 -05:00
def _process_links(self, links, defaults):
2014-03-19 23:07:30 -04:00
success = True
for destination, source in links.items():
source = os.path.expandvars(source)
destination = os.path.expandvars(destination)
if isinstance(source, dict):
# extended config
path = source['path']
2016-02-27 00:43:11 -05:00
relative = source.get('relative', defaults.get('relative', False))
force = source.get('force', defaults.get('force', False))
relink = source.get('relink', defaults.get('relink', False))
create = source.get('create', defaults.get('create', False))
if create:
success &= self._create(destination)
if force:
success &= self._delete(path, destination, force=True)
elif relink:
success &= self._delete(path, destination, force=False)
else:
relative = False
path = source
success &= self._link(path, destination, relative)
2014-03-19 23:07:30 -04:00
if success:
self._log.info('All links have been set up')
else:
self._log.error('Some links were not successfully set up')
return success
def _is_link(self, path):
'''
Returns true if the path is a symbolic link.
'''
return os.path.islink(os.path.expanduser(path))
def _link_destination(self, path):
'''
Returns the absolute path to the destination of the symbolic link.
'''
path = os.path.expanduser(path)
rel_dest = os.readlink(path)
return os.path.join(os.path.dirname(path), rel_dest)
def _exists(self, path):
'''
Returns true if the path exists.
'''
path = os.path.expanduser(path)
return os.path.exists(path)
def _create(self, path):
success = True
parent = os.path.abspath(os.path.join(os.path.expanduser(path), os.pardir))
if not self._exists(parent):
try:
os.makedirs(parent)
except OSError:
self._log.warning('Failed to create directory %s' % parent)
success = False
else:
self._log.lowinfo('Creating directory %s' % parent)
return success
def _delete(self, source, path, force):
success = True
source = os.path.join(self._base_directory, source)
if ((self._is_link(path) and self._link_destination(path) != source) or
(self._exists(path) and not self._is_link(path))):
fullpath = os.path.expanduser(path)
removed = False
try:
if os.path.islink(fullpath):
os.unlink(fullpath)
removed = True
elif force:
if os.path.isdir(fullpath):
shutil.rmtree(fullpath)
removed = True
else:
os.remove(fullpath)
removed = True
except OSError:
self._log.warning('Failed to remove %s' % path)
success = False
else:
if removed:
self._log.lowinfo('Removing %s' % path)
return success
def _link(self, source, link_name, relative):
2014-03-19 23:07:30 -04:00
'''
Links link_name to source.
Returns true if successfully linked files.
'''
success = False
source = os.path.join(self._base_directory, source)
if (not self._exists(link_name) and self._is_link(link_name) and
self._link_destination(link_name) != source):
2014-03-19 23:07:30 -04:00
self._log.warning('Invalid link %s -> %s' %
(link_name, self._link_destination(link_name)))
elif not self._exists(link_name) and self._exists(source):
try:
destination = os.path.expanduser(link_name)
if relative:
destination_dir = os.path.dirname(destination)
source = os.path.relpath(source, destination_dir)
os.symlink(source, destination)
2014-08-20 16:58:42 -04:00
except OSError:
self._log.warning('Linking failed %s -> %s' % (link_name, source))
else:
self._log.lowinfo('Creating link %s -> %s' % (link_name, source))
success = True
2014-03-19 23:07:30 -04:00
elif self._exists(link_name) and not self._is_link(link_name):
self._log.warning(
'%s already exists but is a regular file or directory' %
link_name)
elif self._is_link(link_name) and self._link_destination(link_name) != source:
2014-03-19 23:07:30 -04:00
self._log.warning('Incorrect link %s -> %s' %
(link_name, self._link_destination(link_name)))
elif not self._exists(source):
if self._is_link(link_name):
self._log.warning('Nonexistent target %s -> %s' %
(link_name, source))
else:
self._log.warning('Nonexistent target for %s : %s' %
(link_name, source))
2014-03-19 23:07:30 -04:00
else:
self._log.lowinfo('Link exists %s -> %s' % (link_name, source))
success = True
return success