Add 'canonicalize-path' option to link
Dotbot had a hardcoded behaviour that the BASEDIR was always passed to os.path.realpath which "returns the canonical path of the specified filename, eliminating any symbolic links encountered in the path". This might not always be desirable so this commit makes it configurable. The use case where `canonicalize-path` comes in handy is the following: You want to provide dotfiles in the Filesystem Hierarchy Standard under `/usr/local/share/ypid_dotfiles/`. Now you want to provide `.config/dotfiles` as a default in `/etc/skel`. When you now pre-configure `/etc/skel` by running dotbot in it set has HOME, dotfiles will refer to `/usr/local/share/ypid_dotfiles/` and not `/etc/skel/.config/dotfiles` which does not look nice. This is related to but not the same as the `relative` parameter used with link commands.
This commit is contained in:
parent
e38e021ab3
commit
138fdbc8d7
4 changed files with 22 additions and 14 deletions
|
@ -176,6 +176,7 @@ Available extended configuration parameters:
|
||||||
| `relink` | Removes the old target if it's a symlink (default:false) |
|
| `relink` | Removes the old target if it's a symlink (default:false) |
|
||||||
| `force` | Force removes the old target, file or folder, and forces a new link (default:false) |
|
| `force` | Force removes the old target, file or folder, and forces a new link (default:false) |
|
||||||
| `relative` | Use a relative path to the source when creating the symlink (default:false, absolute links) |
|
| `relative` | Use a relative path to the source when creating the symlink (default:false, absolute links) |
|
||||||
|
| `canonicalize-path` | Resolve any symbolic links encountered in the source to symlink to the canonical path (default:true, real paths) |
|
||||||
| `glob` | Treat a `*` character as a wildcard, and perform link operations on all of those matches (default:false) |
|
| `glob` | Treat a `*` character as a wildcard, and perform link operations on all of those matches (default:false) |
|
||||||
| `if` | Execute this in your `$SHELL` and only link if it is successful. |
|
| `if` | Execute this in your `$SHELL` and only link if it is successful. |
|
||||||
| `ignore-missing` | Do not fail if the source is missing and create the link anyway (default:false) |
|
| `ignore-missing` | Do not fail if the source is missing and create the link anyway (default:false) |
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import copy
|
import copy
|
||||||
|
import os
|
||||||
|
|
||||||
class Context(object):
|
class Context(object):
|
||||||
'''
|
'''
|
||||||
|
@ -13,8 +14,11 @@ class Context(object):
|
||||||
def set_base_directory(self, base_directory):
|
def set_base_directory(self, base_directory):
|
||||||
self._base_directory = base_directory
|
self._base_directory = base_directory
|
||||||
|
|
||||||
def base_directory(self):
|
def base_directory(self, canonical_path=True):
|
||||||
return self._base_directory
|
base_directory = self._base_directory
|
||||||
|
if canonical_path:
|
||||||
|
base_directory = os.path.realpath(base_directory)
|
||||||
|
return base_directory
|
||||||
|
|
||||||
def set_defaults(self, defaults):
|
def set_defaults(self, defaults):
|
||||||
self._defaults = defaults
|
self._defaults = defaults
|
||||||
|
|
|
@ -10,8 +10,8 @@ class Dispatcher(object):
|
||||||
self._load_plugins()
|
self._load_plugins()
|
||||||
|
|
||||||
def _setup_context(self, base_directory):
|
def _setup_context(self, base_directory):
|
||||||
path = os.path.abspath(os.path.realpath(
|
path = os.path.abspath(
|
||||||
os.path.expanduser(base_directory)))
|
os.path.expanduser(base_directory))
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
raise DispatchError('Nonexistent base directory')
|
raise DispatchError('Nonexistent base directory')
|
||||||
self._context = Context(path)
|
self._context = Context(path)
|
||||||
|
|
|
@ -26,6 +26,7 @@ class Link(dotbot.Plugin):
|
||||||
for destination, source in links.items():
|
for destination, source in links.items():
|
||||||
destination = os.path.expandvars(destination)
|
destination = os.path.expandvars(destination)
|
||||||
relative = defaults.get('relative', False)
|
relative = defaults.get('relative', False)
|
||||||
|
canonical_path = defaults.get('canonicalize-path', True)
|
||||||
force = defaults.get('force', False)
|
force = defaults.get('force', False)
|
||||||
relink = defaults.get('relink', False)
|
relink = defaults.get('relink', False)
|
||||||
create = defaults.get('create', False)
|
create = defaults.get('create', False)
|
||||||
|
@ -36,6 +37,7 @@ class Link(dotbot.Plugin):
|
||||||
# extended config
|
# extended config
|
||||||
test = source.get('if', test)
|
test = source.get('if', test)
|
||||||
relative = source.get('relative', relative)
|
relative = source.get('relative', relative)
|
||||||
|
canonical_path = source.get('canonicalize-path', canonical_path)
|
||||||
force = source.get('force', force)
|
force = source.get('force', force)
|
||||||
relink = source.get('relink', relink)
|
relink = source.get('relink', relink)
|
||||||
create = source.get('create', create)
|
create = source.get('create', create)
|
||||||
|
@ -68,8 +70,8 @@ class Link(dotbot.Plugin):
|
||||||
if create:
|
if create:
|
||||||
success &= self._create(destination)
|
success &= self._create(destination)
|
||||||
if force or relink:
|
if force or relink:
|
||||||
success &= self._delete(path, destination, relative, force)
|
success &= self._delete(path, destination, relative, canonical_path, force)
|
||||||
success &= self._link(path, destination, relative, ignore_missing)
|
success &= self._link(path, destination, relative, canonical_path, ignore_missing)
|
||||||
else:
|
else:
|
||||||
self._log.lowinfo("Globs from '" + path + "': " + str(glob_results))
|
self._log.lowinfo("Globs from '" + path + "': " + str(glob_results))
|
||||||
glob_base = path[:glob_star_loc]
|
glob_base = path[:glob_star_loc]
|
||||||
|
@ -79,8 +81,8 @@ class Link(dotbot.Plugin):
|
||||||
if create:
|
if create:
|
||||||
success &= self._create(glob_link_destination)
|
success &= self._create(glob_link_destination)
|
||||||
if force or relink:
|
if force or relink:
|
||||||
success &= self._delete(glob_full_item, glob_link_destination, relative, force)
|
success &= self._delete(glob_full_item, glob_link_destination, relative, canonical_path, force)
|
||||||
success &= self._link(glob_full_item, glob_link_destination, relative, ignore_missing)
|
success &= self._link(glob_full_item, glob_link_destination, relative, canonical_path, ignore_missing)
|
||||||
else:
|
else:
|
||||||
if create:
|
if create:
|
||||||
success &= self._create(destination)
|
success &= self._create(destination)
|
||||||
|
@ -94,8 +96,8 @@ class Link(dotbot.Plugin):
|
||||||
(destination, path))
|
(destination, path))
|
||||||
continue
|
continue
|
||||||
if force or relink:
|
if force or relink:
|
||||||
success &= self._delete(path, destination, relative, force)
|
success &= self._delete(path, destination, relative, canonical_path, force)
|
||||||
success &= self._link(path, destination, relative, ignore_missing)
|
success &= self._link(path, destination, relative, canonical_path, ignore_missing)
|
||||||
if success:
|
if success:
|
||||||
self._log.info('All links have been set up')
|
self._log.info('All links have been set up')
|
||||||
else:
|
else:
|
||||||
|
@ -159,9 +161,9 @@ class Link(dotbot.Plugin):
|
||||||
self._log.lowinfo('Creating directory %s' % parent)
|
self._log.lowinfo('Creating directory %s' % parent)
|
||||||
return success
|
return success
|
||||||
|
|
||||||
def _delete(self, source, path, relative, force):
|
def _delete(self, source, path, relative, canonical_path, force):
|
||||||
success = True
|
success = True
|
||||||
source = os.path.join(self._context.base_directory(), source)
|
source = os.path.join(self._context.base_directory(canonical_path=canonical_path), source)
|
||||||
fullpath = os.path.expanduser(path)
|
fullpath = os.path.expanduser(path)
|
||||||
if relative:
|
if relative:
|
||||||
source = self._relative_path(source, fullpath)
|
source = self._relative_path(source, fullpath)
|
||||||
|
@ -195,7 +197,7 @@ class Link(dotbot.Plugin):
|
||||||
destination_dir = os.path.dirname(destination)
|
destination_dir = os.path.dirname(destination)
|
||||||
return os.path.relpath(source, destination_dir)
|
return os.path.relpath(source, destination_dir)
|
||||||
|
|
||||||
def _link(self, source, link_name, relative, ignore_missing):
|
def _link(self, source, link_name, relative, canonical_path, ignore_missing):
|
||||||
'''
|
'''
|
||||||
Links link_name to source.
|
Links link_name to source.
|
||||||
|
|
||||||
|
@ -203,7 +205,8 @@ class Link(dotbot.Plugin):
|
||||||
'''
|
'''
|
||||||
success = False
|
success = False
|
||||||
destination = os.path.expanduser(link_name)
|
destination = os.path.expanduser(link_name)
|
||||||
absolute_source = os.path.join(self._context.base_directory(), source)
|
base_directory = self._context.base_directory(canonical_path=canonical_path)
|
||||||
|
absolute_source = os.path.join(base_directory, source)
|
||||||
if relative:
|
if relative:
|
||||||
source = self._relative_path(absolute_source, destination)
|
source = self._relative_path(absolute_source, destination)
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in a new issue