mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-10 02:19:14 +00:00
Add option to symlink only child files
This commit is contained in:
@@ -15,6 +15,7 @@ from dotdrop.templategen import Templategen
|
|||||||
from dotdrop.logger import Logger
|
from dotdrop.logger import Logger
|
||||||
from dotdrop.action import Action, Transform
|
from dotdrop.action import Action, Transform
|
||||||
from dotdrop.utils import *
|
from dotdrop.utils import *
|
||||||
|
from dotdrop.linktypes import LinkTypes
|
||||||
|
|
||||||
|
|
||||||
class Cfg:
|
class Cfg:
|
||||||
@@ -67,7 +68,7 @@ class Cfg:
|
|||||||
default_backup = True
|
default_backup = True
|
||||||
default_create = True
|
default_create = True
|
||||||
default_banner = True
|
default_banner = True
|
||||||
default_link = False
|
default_link = LinkTypes.NOLINK
|
||||||
default_longkey = False
|
default_longkey = False
|
||||||
default_keepdot = False
|
default_keepdot = False
|
||||||
default_showdiff = False
|
default_showdiff = False
|
||||||
@@ -212,10 +213,13 @@ class Cfg:
|
|||||||
# ensures the dotfiles entry is a dict
|
# ensures the dotfiles entry is a dict
|
||||||
self.content[self.key_dotfiles] = {}
|
self.content[self.key_dotfiles] = {}
|
||||||
for k, v in self.content[self.key_dotfiles].items():
|
for k, v in self.content[self.key_dotfiles].items():
|
||||||
src = v[self.key_dotfiles_src]
|
src = os.path.normpath(v[self.key_dotfiles_src])
|
||||||
dst = v[self.key_dotfiles_dst]
|
dst = os.path.normpath(v[self.key_dotfiles_dst])
|
||||||
link = v[self.key_dotfiles_link] if self.key_dotfiles_link \
|
link = LinkTypes.PARENTS \
|
||||||
in v else self.default_link
|
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 \
|
noempty = v[self.key_dotfiles_noempty] if \
|
||||||
self.key_dotfiles_noempty \
|
self.key_dotfiles_noempty \
|
||||||
in v else self.lnk_settings[self.key_ignoreempty]
|
in v else self.lnk_settings[self.key_ignoreempty]
|
||||||
@@ -264,7 +268,7 @@ class Cfg:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# disable transformation when link is true
|
# 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 = 'transformations disabled for \"{}\"'.format(dst)
|
||||||
msg += ' because link is True'
|
msg += ' because link is True'
|
||||||
self.log.warn(msg)
|
self.log.warn(msg)
|
||||||
@@ -520,7 +524,7 @@ class Cfg:
|
|||||||
return False, self._get_long_key(path)
|
return False, self._get_long_key(path)
|
||||||
return False, self._get_short_key(path, self.dotfiles.keys())
|
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
|
"""import new dotfile
|
||||||
dotfile key will change and can be empty"""
|
dotfile key will change and can be empty"""
|
||||||
# keep it short
|
# keep it short
|
||||||
@@ -569,9 +573,9 @@ class Cfg:
|
|||||||
self.key_dotfiles_dst: dotfile.dst,
|
self.key_dotfiles_dst: dotfile.dst,
|
||||||
self.key_dotfiles_src: dotfile.src,
|
self.key_dotfiles_src: dotfile.src,
|
||||||
}
|
}
|
||||||
if link:
|
if link != LinkTypes.NOLINK:
|
||||||
# set the link flag
|
# 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
|
# link it to this profile in the yaml file
|
||||||
pro = self.content[self.key_profiles][profile]
|
pro = self.content[self.key_profiles][profile]
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ entry point
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
|
||||||
import socket
|
import socket
|
||||||
from docopt import docopt
|
from docopt import docopt
|
||||||
|
|
||||||
@@ -20,7 +19,8 @@ from dotdrop.updater import Updater
|
|||||||
from dotdrop.comparator import Comparator
|
from dotdrop.comparator import Comparator
|
||||||
from dotdrop.dotfile import Dotfile
|
from dotdrop.dotfile import Dotfile
|
||||||
from dotdrop.config import Cfg
|
from dotdrop.config import Cfg
|
||||||
from dotdrop.utils import *
|
import dotdrop.utils as dd
|
||||||
|
from dotdrop.linktypes import LinkTypes
|
||||||
|
|
||||||
LOG = Logger()
|
LOG = Logger()
|
||||||
ENV_PROFILE = 'DOTDROP_PROFILE'
|
ENV_PROFILE = 'DOTDROP_PROFILE'
|
||||||
@@ -92,7 +92,7 @@ def cmd_install(opts, conf, temporary=False, keys=[]):
|
|||||||
variables=opts['variables'], debug=opts['debug'])
|
variables=opts['variables'], debug=opts['debug'])
|
||||||
tmpdir = None
|
tmpdir = None
|
||||||
if temporary:
|
if temporary:
|
||||||
tmpdir = get_tmpdir()
|
tmpdir = dd.get_tmpdir()
|
||||||
inst = Installer(create=opts['create'], backup=opts['backup'],
|
inst = Installer(create=opts['create'], backup=opts['backup'],
|
||||||
dry=opts['dry'], safe=opts['safe'],
|
dry=opts['dry'], safe=opts['safe'],
|
||||||
base=opts['dotpath'], workdir=opts['workdir'],
|
base=opts['dotpath'], workdir=opts['workdir'],
|
||||||
@@ -106,8 +106,10 @@ def cmd_install(opts, conf, temporary=False, keys=[]):
|
|||||||
preactions.append(action)
|
preactions.append(action)
|
||||||
if opts['debug']:
|
if opts['debug']:
|
||||||
LOG.dbg('installing {}'.format(dotfile))
|
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)
|
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:
|
else:
|
||||||
src = dotfile.src
|
src = dotfile.src
|
||||||
tmp = None
|
tmp = None
|
||||||
@@ -121,7 +123,7 @@ def cmd_install(opts, conf, temporary=False, keys=[]):
|
|||||||
if tmp:
|
if tmp:
|
||||||
tmp = os.path.join(opts['dotpath'], tmp)
|
tmp = os.path.join(opts['dotpath'], tmp)
|
||||||
if os.path.exists(tmp):
|
if os.path.exists(tmp):
|
||||||
remove(tmp)
|
dd.remove(tmp)
|
||||||
if len(r) > 0:
|
if len(r) > 0:
|
||||||
if Cfg.key_actions_post in dotfile.actions:
|
if Cfg.key_actions_post in dotfile.actions:
|
||||||
actions = dotfile.actions[Cfg.key_actions_post]
|
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
|
# clean tmp transformed dotfile if any
|
||||||
tmpsrc = os.path.join(opts['dotpath'], tmpsrc)
|
tmpsrc = os.path.join(opts['dotpath'], tmpsrc)
|
||||||
if os.path.exists(tmpsrc):
|
if os.path.exists(tmpsrc):
|
||||||
remove(tmpsrc)
|
dd.remove(tmpsrc)
|
||||||
if diff == '':
|
if diff == '':
|
||||||
if opts['debug']:
|
if opts['debug']:
|
||||||
LOG.dbg('diffing \"{}\" VS \"{}\"'.format(dotfile.key,
|
LOG.dbg('diffing \"{}\" VS \"{}\"'.format(dotfile.key,
|
||||||
@@ -243,7 +245,7 @@ def cmd_importer(opts, conf, paths):
|
|||||||
continue
|
continue
|
||||||
dst = path.rstrip(os.sep)
|
dst = path.rstrip(os.sep)
|
||||||
dst = os.path.abspath(dst)
|
dst = os.path.abspath(dst)
|
||||||
src = strip_home(dst)
|
src = dd.strip_home(dst)
|
||||||
strip = '.' + os.sep
|
strip = '.' + os.sep
|
||||||
if opts['keepdot']:
|
if opts['keepdot']:
|
||||||
strip = os.sep
|
strip = os.sep
|
||||||
@@ -262,7 +264,7 @@ def cmd_importer(opts, conf, paths):
|
|||||||
if opts['dry']:
|
if opts['dry']:
|
||||||
LOG.dry('would run: {}'.format(' '.join(cmd)))
|
LOG.dry('would run: {}'.format(' '.join(cmd)))
|
||||||
else:
|
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:
|
if not r:
|
||||||
LOG.err('importing \"{}\" failed!'.format(path))
|
LOG.err('importing \"{}\" failed!'.format(path))
|
||||||
ret = False
|
ret = False
|
||||||
@@ -273,13 +275,13 @@ def cmd_importer(opts, conf, paths):
|
|||||||
if linkit:
|
if linkit:
|
||||||
LOG.dry('would symlink {} to {}'.format(srcf, dst))
|
LOG.dry('would symlink {} to {}'.format(srcf, dst))
|
||||||
else:
|
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:
|
if not r:
|
||||||
LOG.err('importing \"{}\" failed!'.format(path))
|
LOG.err('importing \"{}\" failed!'.format(path))
|
||||||
ret = False
|
ret = False
|
||||||
continue
|
continue
|
||||||
if linkit:
|
if linkit:
|
||||||
remove(dst)
|
dd.remove(dst)
|
||||||
os.symlink(srcf, dst)
|
os.symlink(srcf, dst)
|
||||||
retconf, dotfile = conf.new(dotfile, opts['profile'],
|
retconf, dotfile = conf.new(dotfile, opts['profile'],
|
||||||
link=linkit, debug=opts['debug'])
|
link=linkit, debug=opts['debug'])
|
||||||
@@ -400,7 +402,7 @@ def apply_trans(opts, dotfile):
|
|||||||
msg = 'transformation \"{}\" failed for {}'
|
msg = 'transformation \"{}\" failed for {}'
|
||||||
LOG.err(msg.format(trans.key, dotfile.key))
|
LOG.err(msg.format(trans.key, dotfile.key))
|
||||||
if new_src and os.path.exists(new_src):
|
if new_src and os.path.exists(new_src):
|
||||||
remove(new_src)
|
dd.remove(new_src)
|
||||||
return None
|
return None
|
||||||
return new_src
|
return new_src
|
||||||
|
|
||||||
@@ -469,12 +471,12 @@ def main():
|
|||||||
# compare local dotfiles with dotfiles stored in dotdrop
|
# compare local dotfiles with dotfiles stored in dotdrop
|
||||||
if opts['debug']:
|
if opts['debug']:
|
||||||
LOG.dbg('running cmd: compare')
|
LOG.dbg('running cmd: compare')
|
||||||
tmp = get_tmpdir()
|
tmp = dd.get_tmpdir()
|
||||||
opts['dopts'] = args['--dopts']
|
opts['dopts'] = args['--dopts']
|
||||||
ret = cmd_compare(opts, conf, tmp, focus=args['--file'],
|
ret = cmd_compare(opts, conf, tmp, focus=args['--file'],
|
||||||
ignore=args['--ignore'])
|
ignore=args['--ignore'])
|
||||||
# clean tmp directory
|
# clean tmp directory
|
||||||
remove(tmp)
|
dd.remove(tmp)
|
||||||
|
|
||||||
elif args['import']:
|
elif args['import']:
|
||||||
# import dotfile(s)
|
# import dotfile(s)
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ Copyright (c) 2017, deadc0de6
|
|||||||
represents a dotfile in dotdrop
|
represents a dotfile in dotdrop
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from dotdrop.linktypes import LinkTypes
|
||||||
|
|
||||||
|
|
||||||
class Dotfile:
|
class Dotfile:
|
||||||
|
|
||||||
def __init__(self, key, dst, src,
|
def __init__(self, key, dst, src,
|
||||||
actions={}, trans_r=None, trans_w=None,
|
actions={}, trans_r=None, trans_w=None,
|
||||||
link=False, cmpignore=[], noempty=False):
|
link=LinkTypes.NOLINK, cmpignore=[], noempty=False):
|
||||||
# key of dotfile in the config
|
# key of dotfile in the config
|
||||||
self.key = key
|
self.key = key
|
||||||
# path where to install this dotfile
|
# path where to install this dotfile
|
||||||
@@ -32,10 +34,11 @@ class Dotfile:
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = 'key:\"{}\", src:\"{}\", dst:\"{}\", link:\"{}\"'
|
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):
|
def __eq__(self, other):
|
||||||
return self.__dict__ == other.__dict__
|
return self.__dict__ == other.__dict__
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(self.dst) ^ hash(self.src) ^ hash(self.key)
|
return hash(self.dst) ^ hash(self.src) ^ hash(self.key)
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class Installer:
|
|||||||
if not os.path.exists(src):
|
if not os.path.exists(src):
|
||||||
self.log.err('source dotfile does not exist: {}'.format(src))
|
self.log.err('source dotfile does not exist: {}'.format(src))
|
||||||
return []
|
return []
|
||||||
dst = os.path.expanduser(dst)
|
dst = os.path.normpath(os.path.expanduser(dst))
|
||||||
if self.totemp:
|
if self.totemp:
|
||||||
# ignore actions
|
# ignore actions
|
||||||
return self.install(templater, src, dst, actions=[])
|
return self.install(templater, src, dst, actions=[])
|
||||||
@@ -80,6 +80,42 @@ class Installer:
|
|||||||
src = tmp
|
src = tmp
|
||||||
return self._link(src, dst, actions=actions)
|
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=[]):
|
def _link(self, src, dst, actions=[]):
|
||||||
"""set src as a link target of dst"""
|
"""set src as a link target of dst"""
|
||||||
if os.path.lexists(dst):
|
if os.path.lexists(dst):
|
||||||
@@ -308,3 +344,4 @@ class Installer:
|
|||||||
self.comparing = False
|
self.comparing = False
|
||||||
self.create = createsaved
|
self.create = createsaved
|
||||||
return ret, tmpdst
|
return ret, tmpdst
|
||||||
|
|
||||||
|
|||||||
8
dotdrop/linktypes.py
Normal file
8
dotdrop/linktypes.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from enum import IntEnum
|
||||||
|
|
||||||
|
|
||||||
|
class LinkTypes(IntEnum):
|
||||||
|
NOLINK = 0
|
||||||
|
PARENTS = 1
|
||||||
|
CHILDREN = 2
|
||||||
|
|
||||||
@@ -10,11 +10,12 @@ import tempfile
|
|||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
import shlex
|
import shlex
|
||||||
|
import functools
|
||||||
|
import operator
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
|
|
||||||
# local import
|
# local import
|
||||||
from dotdrop.logger import Logger
|
from dotdrop.logger import Logger
|
||||||
from dotdrop.version import __version__ as VERSION
|
|
||||||
|
|
||||||
LOG = Logger()
|
LOG = Logger()
|
||||||
|
|
||||||
@@ -109,3 +110,9 @@ def strip_home(path):
|
|||||||
if path.startswith(home):
|
if path.startswith(home):
|
||||||
path = path[len(home):]
|
path = path[len(home):]
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def flatten(a):
|
||||||
|
"""flatten list"""
|
||||||
|
return functools.reduce(operator.iconcat, a, [])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user