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:
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
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 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, [])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user