1
0
mirror of https://github.com/deadc0de6/dotdrop.git synced 2026-02-04 20:54:51 +00:00

Add option to symlink only child files

This commit is contained in:
Marcel Robitaille
2018-12-29 15:24:02 -04:00
parent 0dd9dea1ff
commit fffe55ecd9
6 changed files with 87 additions and 26 deletions

View File

@@ -15,6 +15,7 @@ from dotdrop.templategen import Templategen
from dotdrop.logger import Logger
from dotdrop.action import Action, Transform
from dotdrop.utils import *
from dotdrop.linktypes import LinkTypes
class Cfg:
@@ -67,7 +68,7 @@ class Cfg:
default_backup = True
default_create = True
default_banner = True
default_link = False
default_link = LinkTypes.NOLINK
default_longkey = False
default_keepdot = False
default_showdiff = False
@@ -212,10 +213,13 @@ class Cfg:
# ensures the dotfiles entry is a dict
self.content[self.key_dotfiles] = {}
for k, v in self.content[self.key_dotfiles].items():
src = v[self.key_dotfiles_src]
dst = v[self.key_dotfiles_dst]
link = v[self.key_dotfiles_link] if self.key_dotfiles_link \
in v else self.default_link
src = os.path.normpath(v[self.key_dotfiles_src])
dst = os.path.normpath(v[self.key_dotfiles_dst])
link = LinkTypes.PARENTS \
if self.key_dotfiles_link in v and v[self.key_dotfiles_link] \
else LinkTypes.CHILDREN \
if 'link_children' in v and v['link_children'] \
else LinkTypes.NOLINK
noempty = v[self.key_dotfiles_noempty] if \
self.key_dotfiles_noempty \
in v else self.lnk_settings[self.key_ignoreempty]
@@ -264,7 +268,7 @@ class Cfg:
return False
# disable transformation when link is true
if link and (trans_r or trans_w):
if link != LinkTypes.NOLINK and (trans_r or trans_w):
msg = 'transformations disabled for \"{}\"'.format(dst)
msg += ' because link is True'
self.log.warn(msg)
@@ -520,7 +524,7 @@ class Cfg:
return False, self._get_long_key(path)
return False, self._get_short_key(path, self.dotfiles.keys())
def new(self, dotfile, profile, link=False, debug=False):
def new(self, dotfile, profile, link=LinkTypes.NOLINK, debug=False):
"""import new dotfile
dotfile key will change and can be empty"""
# keep it short
@@ -569,9 +573,9 @@ class Cfg:
self.key_dotfiles_dst: dotfile.dst,
self.key_dotfiles_src: dotfile.src,
}
if link:
if link != LinkTypes.NOLINK:
# set the link flag
dots[dotfile.key][self.key_dotfiles_link] = True
dots[dotfile.key][self.key_dotfiles_link] = link
# link it to this profile in the yaml file
pro = self.content[self.key_profiles][profile]

View File

@@ -7,7 +7,6 @@ entry point
import os
import sys
import subprocess
import socket
from docopt import docopt
@@ -20,7 +19,8 @@ from dotdrop.updater import Updater
from dotdrop.comparator import Comparator
from dotdrop.dotfile import Dotfile
from dotdrop.config import Cfg
from dotdrop.utils import *
import dotdrop.utils as dd
from dotdrop.linktypes import LinkTypes
LOG = Logger()
ENV_PROFILE = 'DOTDROP_PROFILE'
@@ -92,7 +92,7 @@ def cmd_install(opts, conf, temporary=False, keys=[]):
variables=opts['variables'], debug=opts['debug'])
tmpdir = None
if temporary:
tmpdir = get_tmpdir()
tmpdir = dd.get_tmpdir()
inst = Installer(create=opts['create'], backup=opts['backup'],
dry=opts['dry'], safe=opts['safe'],
base=opts['dotpath'], workdir=opts['workdir'],
@@ -106,8 +106,10 @@ def cmd_install(opts, conf, temporary=False, keys=[]):
preactions.append(action)
if opts['debug']:
LOG.dbg('installing {}'.format(dotfile))
if hasattr(dotfile, 'link') and dotfile.link:
if hasattr(dotfile, 'link') and dotfile.link == LinkTypes.PARENTS:
r = inst.link(t, dotfile.src, dotfile.dst, actions=preactions)
elif hasattr(dotfile, 'link') and dotfile.link == LinkTypes.CHILDREN:
r = inst.linkall(t, dotfile.src, dotfile.dst, actions=preactions)
else:
src = dotfile.src
tmp = None
@@ -121,7 +123,7 @@ def cmd_install(opts, conf, temporary=False, keys=[]):
if tmp:
tmp = os.path.join(opts['dotpath'], tmp)
if os.path.exists(tmp):
remove(tmp)
dd.remove(tmp)
if len(r) > 0:
if Cfg.key_actions_post in dotfile.actions:
actions = dotfile.actions[Cfg.key_actions_post]
@@ -189,7 +191,7 @@ def cmd_compare(opts, conf, tmp, focus=[], ignore=[]):
# clean tmp transformed dotfile if any
tmpsrc = os.path.join(opts['dotpath'], tmpsrc)
if os.path.exists(tmpsrc):
remove(tmpsrc)
dd.remove(tmpsrc)
if diff == '':
if opts['debug']:
LOG.dbg('diffing \"{}\" VS \"{}\"'.format(dotfile.key,
@@ -243,7 +245,7 @@ def cmd_importer(opts, conf, paths):
continue
dst = path.rstrip(os.sep)
dst = os.path.abspath(dst)
src = strip_home(dst)
src = dd.strip_home(dst)
strip = '.' + os.sep
if opts['keepdot']:
strip = os.sep
@@ -262,7 +264,7 @@ def cmd_importer(opts, conf, paths):
if opts['dry']:
LOG.dry('would run: {}'.format(' '.join(cmd)))
else:
r, _ = run(cmd, raw=False, debug=opts['debug'], checkerr=True)
r, _ = dd.run(cmd, raw=False, debug=opts['debug'], checkerr=True)
if not r:
LOG.err('importing \"{}\" failed!'.format(path))
ret = False
@@ -273,13 +275,13 @@ def cmd_importer(opts, conf, paths):
if linkit:
LOG.dry('would symlink {} to {}'.format(srcf, dst))
else:
r, _ = run(cmd, raw=False, debug=opts['debug'], checkerr=True)
r, _ = dd.run(cmd, raw=False, debug=opts['debug'], checkerr=True)
if not r:
LOG.err('importing \"{}\" failed!'.format(path))
ret = False
continue
if linkit:
remove(dst)
dd.remove(dst)
os.symlink(srcf, dst)
retconf, dotfile = conf.new(dotfile, opts['profile'],
link=linkit, debug=opts['debug'])
@@ -400,7 +402,7 @@ def apply_trans(opts, dotfile):
msg = 'transformation \"{}\" failed for {}'
LOG.err(msg.format(trans.key, dotfile.key))
if new_src and os.path.exists(new_src):
remove(new_src)
dd.remove(new_src)
return None
return new_src
@@ -469,12 +471,12 @@ def main():
# compare local dotfiles with dotfiles stored in dotdrop
if opts['debug']:
LOG.dbg('running cmd: compare')
tmp = get_tmpdir()
tmp = dd.get_tmpdir()
opts['dopts'] = args['--dopts']
ret = cmd_compare(opts, conf, tmp, focus=args['--file'],
ignore=args['--ignore'])
# clean tmp directory
remove(tmp)
dd.remove(tmp)
elif args['import']:
# import dotfile(s)

View File

@@ -5,12 +5,14 @@ Copyright (c) 2017, deadc0de6
represents a dotfile in dotdrop
"""
from dotdrop.linktypes import LinkTypes
class Dotfile:
def __init__(self, key, dst, src,
actions={}, trans_r=None, trans_w=None,
link=False, cmpignore=[], noempty=False):
link=LinkTypes.NOLINK, cmpignore=[], noempty=False):
# key of dotfile in the config
self.key = key
# path where to install this dotfile
@@ -32,10 +34,11 @@ class Dotfile:
def __str__(self):
msg = 'key:\"{}\", src:\"{}\", dst:\"{}\", link:\"{}\"'
return msg.format(self.key, self.src, self.dst, self.link)
return msg.format(self.key, self.src, self.dst, self.link.name)
def __eq__(self, other):
return self.__dict__ == other.__dict__
def __hash__(self):
return hash(self.dst) ^ hash(self.src) ^ hash(self.key)

View File

@@ -64,7 +64,7 @@ class Installer:
if not os.path.exists(src):
self.log.err('source dotfile does not exist: {}'.format(src))
return []
dst = os.path.expanduser(dst)
dst = os.path.normpath(os.path.expanduser(dst))
if self.totemp:
# ignore actions
return self.install(templater, src, dst, actions=[])
@@ -80,6 +80,42 @@ class Installer:
src = tmp
return self._link(src, dst, actions=actions)
def linkall(self, templater, src, dst, actions=[]):
"""link all dotfiles in a given directory"""
self.action_executed = False
parent = os.path.join(self.base, os.path.expanduser(src))
if not os.path.exists(parent):
self.log.err('source dotfile does not exist: {}'.format(parent))
return []
dst = os.path.normpath(os.path.expanduser(dst))
if not os.path.lexists(dst):
self.log.sub('creating directory "{}"'.format(dst))
os.makedirs(dst)
if os.path.isfile(dst):
msg = 'Remove regular file "{}" and replace with empty directory?' \
.format(dst)
if self.safe and not self.log.ask(msg):
msg = 'ignoring "{}", nothing installed'
self.log.warn(msg.format(dst))
return []
os.unlink(dst)
os.mkdir(dst)
children = os.listdir(parent)
srcs = [os.path.join(parent, child) for child in children]
dsts = [os.path.join(dst, child) for child in children]
results = []
for i in range(len(children)):
result = self._link(srcs[i], dsts[i], actions)
if len(result):
actions = []
results.append(result)
return utils.flatten(results)
def _link(self, src, dst, actions=[]):
"""set src as a link target of dst"""
if os.path.lexists(dst):
@@ -308,3 +344,4 @@ class Installer:
self.comparing = False
self.create = createsaved
return ret, tmpdst

8
dotdrop/linktypes.py Normal file
View File

@@ -0,0 +1,8 @@
from enum import IntEnum
class LinkTypes(IntEnum):
NOLINK = 0
PARENTS = 1
CHILDREN = 2

View File

@@ -10,11 +10,12 @@ import tempfile
import os
import uuid
import shlex
import functools
import operator
from shutil import rmtree
# local import
from dotdrop.logger import Logger
from dotdrop.version import __version__ as VERSION
LOG = Logger()
@@ -109,3 +110,9 @@ def strip_home(path):
if path.startswith(home):
path = path[len(home):]
return path
def flatten(a):
"""flatten list"""
return functools.reduce(operator.iconcat, a, [])