mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-09 08:19:17 +00:00
adding "remove" option for #47 and fix a few issues/bugs
This commit is contained in:
@@ -38,6 +38,7 @@ _dotdrop ()
|
|||||||
'import'
|
'import'
|
||||||
'compare'
|
'compare'
|
||||||
'update'
|
'update'
|
||||||
|
'remove'
|
||||||
'listfiles'
|
'listfiles'
|
||||||
'detail'
|
'detail'
|
||||||
'list'
|
'list'
|
||||||
@@ -59,6 +60,9 @@ _dotdrop ()
|
|||||||
update)
|
update)
|
||||||
_dotdrop-update
|
_dotdrop-update
|
||||||
;;
|
;;
|
||||||
|
remove)
|
||||||
|
_dotdrop-remove
|
||||||
|
;;
|
||||||
listfiles)
|
listfiles)
|
||||||
_dotdrop-listfiles
|
_dotdrop-listfiles
|
||||||
;;
|
;;
|
||||||
@@ -97,7 +101,7 @@ _dotdrop-install ()
|
|||||||
'(-D)-D' \
|
'(-D)-D' \
|
||||||
'(--showdiff)--showdiff' \
|
'(--showdiff)--showdiff' \
|
||||||
'(-a)-a' \
|
'(-a)-a' \
|
||||||
'(--force-action)--force-action' \
|
'(--force-actions)--force-actions' \
|
||||||
'(-c=-)-c=-' \
|
'(-c=-)-c=-' \
|
||||||
'(--cfg=-)--cfg=-' \
|
'(--cfg=-)--cfg=-' \
|
||||||
'(-p=-)-p=-' \
|
'(-p=-)-p=-' \
|
||||||
@@ -193,6 +197,35 @@ _dotdrop-update ()
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_dotdrop-remove ()
|
||||||
|
{
|
||||||
|
local context state state_descr line
|
||||||
|
typeset -A opt_args
|
||||||
|
|
||||||
|
if [[ $words[$CURRENT] == -* ]] ; then
|
||||||
|
_arguments -C \
|
||||||
|
':command:->command' \
|
||||||
|
'(-V)-V' \
|
||||||
|
'(--verbose)--verbose' \
|
||||||
|
'(-b)-b' \
|
||||||
|
'(--no-banner)--no-banner' \
|
||||||
|
'(-f)-f' \
|
||||||
|
'(--force)--force' \
|
||||||
|
'(-d)-d' \
|
||||||
|
'(--dry)--dry' \
|
||||||
|
'(-k)-k' \
|
||||||
|
'(--key)--key' \
|
||||||
|
'(-c=-)-c=-' \
|
||||||
|
'(--cfg=-)--cfg=-' \
|
||||||
|
'(-p=-)-p=-' \
|
||||||
|
'(--profile=-)--profile=-' \
|
||||||
|
|
||||||
|
else
|
||||||
|
myargs=('<path>')
|
||||||
|
_message_next_arg
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
_dotdrop-listfiles ()
|
_dotdrop-listfiles ()
|
||||||
{
|
{
|
||||||
local context state state_descr line
|
local context state state_descr line
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ _dotdrop.sh ()
|
|||||||
'import'
|
'import'
|
||||||
'compare'
|
'compare'
|
||||||
'update'
|
'update'
|
||||||
|
'remove'
|
||||||
'listfiles'
|
'listfiles'
|
||||||
'detail'
|
'detail'
|
||||||
'list'
|
'list'
|
||||||
@@ -59,6 +60,9 @@ _dotdrop.sh ()
|
|||||||
update)
|
update)
|
||||||
_dotdrop.sh-update
|
_dotdrop.sh-update
|
||||||
;;
|
;;
|
||||||
|
remove)
|
||||||
|
_dotdrop.sh-remove
|
||||||
|
;;
|
||||||
listfiles)
|
listfiles)
|
||||||
_dotdrop.sh-listfiles
|
_dotdrop.sh-listfiles
|
||||||
;;
|
;;
|
||||||
@@ -97,7 +101,7 @@ _dotdrop.sh-install ()
|
|||||||
'(-D)-D' \
|
'(-D)-D' \
|
||||||
'(--showdiff)--showdiff' \
|
'(--showdiff)--showdiff' \
|
||||||
'(-a)-a' \
|
'(-a)-a' \
|
||||||
'(--force-action)--force-action' \
|
'(--force-actions)--force-actions' \
|
||||||
'(-c=-)-c=-' \
|
'(-c=-)-c=-' \
|
||||||
'(--cfg=-)--cfg=-' \
|
'(--cfg=-)--cfg=-' \
|
||||||
'(-p=-)-p=-' \
|
'(-p=-)-p=-' \
|
||||||
@@ -193,6 +197,35 @@ _dotdrop.sh-update ()
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_dotdrop.sh-remove ()
|
||||||
|
{
|
||||||
|
local context state state_descr line
|
||||||
|
typeset -A opt_args
|
||||||
|
|
||||||
|
if [[ $words[$CURRENT] == -* ]] ; then
|
||||||
|
_arguments -C \
|
||||||
|
':command:->command' \
|
||||||
|
'(-V)-V' \
|
||||||
|
'(--verbose)--verbose' \
|
||||||
|
'(-b)-b' \
|
||||||
|
'(--no-banner)--no-banner' \
|
||||||
|
'(-f)-f' \
|
||||||
|
'(--force)--force' \
|
||||||
|
'(-d)-d' \
|
||||||
|
'(--dry)--dry' \
|
||||||
|
'(-k)-k' \
|
||||||
|
'(--key)--key' \
|
||||||
|
'(-c=-)-c=-' \
|
||||||
|
'(--cfg=-)--cfg=-' \
|
||||||
|
'(-p=-)-p=-' \
|
||||||
|
'(--profile=-)--profile=-' \
|
||||||
|
|
||||||
|
else
|
||||||
|
myargs=('<path>')
|
||||||
|
_message_next_arg
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
_dotdrop.sh-listfiles ()
|
_dotdrop.sh-listfiles ()
|
||||||
{
|
{
|
||||||
local context state state_descr line
|
local context state state_descr line
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ _dotdrop()
|
|||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
|
||||||
if [ $COMP_CWORD -eq 1 ]; then
|
if [ $COMP_CWORD -eq 1 ]; then
|
||||||
COMPREPLY=( $( compgen -W '-h --help -v --version install import compare update listfiles detail list' -- $cur) )
|
COMPREPLY=( $( compgen -W '-h --help -v --version install import compare update remove listfiles detail list' -- $cur) )
|
||||||
else
|
else
|
||||||
case ${COMP_WORDS[1]} in
|
case ${COMP_WORDS[1]} in
|
||||||
install)
|
install)
|
||||||
@@ -19,6 +19,9 @@ _dotdrop()
|
|||||||
;;
|
;;
|
||||||
update)
|
update)
|
||||||
_dotdrop_update
|
_dotdrop_update
|
||||||
|
;;
|
||||||
|
remove)
|
||||||
|
_dotdrop_remove
|
||||||
;;
|
;;
|
||||||
listfiles)
|
listfiles)
|
||||||
_dotdrop_listfiles
|
_dotdrop_listfiles
|
||||||
@@ -40,7 +43,7 @@ _dotdrop_install()
|
|||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
|
||||||
if [ $COMP_CWORD -ge 2 ]; then
|
if [ $COMP_CWORD -ge 2 ]; then
|
||||||
COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -t --temp -f --force -n --nodiff -d --dry -D --showdiff -a --force-action -c= --cfg= -p= --profile= ' -- $cur) )
|
COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -t --temp -f --force -n --nodiff -d --dry -D --showdiff -a --force-actions -c= --cfg= -p= --profile= ' -- $cur) )
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +77,16 @@ _dotdrop_update()
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_dotdrop_remove()
|
||||||
|
{
|
||||||
|
local cur
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
|
||||||
|
if [ $COMP_CWORD -ge 2 ]; then
|
||||||
|
COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -f --force -d --dry -k --key -c= --cfg= -p= --profile= ' -- $cur) )
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
_dotdrop_listfiles()
|
_dotdrop_listfiles()
|
||||||
{
|
{
|
||||||
local cur
|
local cur
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ _dotdropsh()
|
|||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
|
||||||
if [ $COMP_CWORD -eq 1 ]; then
|
if [ $COMP_CWORD -eq 1 ]; then
|
||||||
COMPREPLY=( $( compgen -W '-h --help -v --version install import compare update listfiles detail list' -- $cur) )
|
COMPREPLY=( $( compgen -W '-h --help -v --version install import compare update remove listfiles detail list' -- $cur) )
|
||||||
else
|
else
|
||||||
case ${COMP_WORDS[1]} in
|
case ${COMP_WORDS[1]} in
|
||||||
install)
|
install)
|
||||||
@@ -19,6 +19,9 @@ _dotdropsh()
|
|||||||
;;
|
;;
|
||||||
update)
|
update)
|
||||||
_dotdropsh_update
|
_dotdropsh_update
|
||||||
|
;;
|
||||||
|
remove)
|
||||||
|
_dotdropsh_remove
|
||||||
;;
|
;;
|
||||||
listfiles)
|
listfiles)
|
||||||
_dotdropsh_listfiles
|
_dotdropsh_listfiles
|
||||||
@@ -40,7 +43,7 @@ _dotdropsh_install()
|
|||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
|
||||||
if [ $COMP_CWORD -ge 2 ]; then
|
if [ $COMP_CWORD -ge 2 ]; then
|
||||||
COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -t --temp -f --force -n --nodiff -d --dry -D --showdiff -a --force-action -c= --cfg= -p= --profile= ' -- $cur) )
|
COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -t --temp -f --force -n --nodiff -d --dry -D --showdiff -a --force-actions -c= --cfg= -p= --profile= ' -- $cur) )
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +77,16 @@ _dotdropsh_update()
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_dotdropsh_remove()
|
||||||
|
{
|
||||||
|
local cur
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
|
||||||
|
if [ $COMP_CWORD -ge 2 ]; then
|
||||||
|
COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -f --force -d --dry -k --key -c= --cfg= -p= --profile= ' -- $cur) )
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
_dotdropsh_listfiles()
|
_dotdropsh_listfiles()
|
||||||
{
|
{
|
||||||
local cur
|
local cur
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ from dotdrop.logger import Logger
|
|||||||
from dotdrop.utils import strip_home
|
from dotdrop.utils import strip_home
|
||||||
|
|
||||||
|
|
||||||
|
TILD = '~'
|
||||||
|
|
||||||
|
|
||||||
class CfgAggregator:
|
class CfgAggregator:
|
||||||
|
|
||||||
file_prefix = 'f'
|
file_prefix = 'f'
|
||||||
@@ -99,9 +102,9 @@ class CfgAggregator:
|
|||||||
|
|
||||||
# patch trans_w/trans_r in dotfiles
|
# patch trans_w/trans_r in dotfiles
|
||||||
self._patch_keys_to_objs(self.dotfiles,
|
self._patch_keys_to_objs(self.dotfiles,
|
||||||
"trans_r", self.get_trans_r)
|
"trans_r", self._get_trans_r)
|
||||||
self._patch_keys_to_objs(self.dotfiles,
|
self._patch_keys_to_objs(self.dotfiles,
|
||||||
"trans_w", self.get_trans_w)
|
"trans_w", self._get_trans_w)
|
||||||
|
|
||||||
def _patch_keys_to_objs(self, containers, keys, get_by_key):
|
def _patch_keys_to_objs(self, containers, keys, get_by_key):
|
||||||
"""
|
"""
|
||||||
@@ -128,6 +131,14 @@ class CfgAggregator:
|
|||||||
self.log.dbg('patching {}.{} with {}'.format(c, keys, objects))
|
self.log.dbg('patching {}.{} with {}'.format(c, keys, objects))
|
||||||
setattr(c, keys, objects)
|
setattr(c, keys, objects)
|
||||||
|
|
||||||
|
def del_dotfile(self, dotfile):
|
||||||
|
"""remove this dotfile from the config"""
|
||||||
|
return self.cfgyaml.del_dotfile(dotfile.key)
|
||||||
|
|
||||||
|
def del_dotfile_from_profile(self, dotfile, profile):
|
||||||
|
"""remove this dotfile from this profile"""
|
||||||
|
return self.cfgyaml.del_dotfile_from_profile(dotfile.key, profile.key)
|
||||||
|
|
||||||
def new(self, src, dst, link, profile_key):
|
def new(self, src, dst, link, profile_key):
|
||||||
"""
|
"""
|
||||||
import a new dotfile
|
import a new dotfile
|
||||||
@@ -136,10 +147,9 @@ class CfgAggregator:
|
|||||||
@link: LinkType
|
@link: LinkType
|
||||||
@profile_key: to which profile
|
@profile_key: to which profile
|
||||||
"""
|
"""
|
||||||
home = os.path.expanduser('~')
|
dst = self.path_to_dotfile_dst(dst)
|
||||||
dst = dst.replace(home, '~', 1)
|
|
||||||
|
|
||||||
dotfile = self._get_dotfile_by_dst(dst)
|
dotfile = self.get_dotfile_by_dst(dst)
|
||||||
if not dotfile:
|
if not dotfile:
|
||||||
# get a new dotfile with a unique key
|
# get a new dotfile with a unique key
|
||||||
key = self._get_new_dotfile_key(dst)
|
key = self._get_new_dotfile_key(dst)
|
||||||
@@ -228,8 +238,22 @@ class CfgAggregator:
|
|||||||
cnt += 1
|
cnt += 1
|
||||||
return newkey
|
return newkey
|
||||||
|
|
||||||
def _get_dotfile_by_dst(self, dst):
|
def path_to_dotfile_dst(self, path):
|
||||||
|
"""normalize the path to match dotfile dst"""
|
||||||
|
path = os.path.expanduser(path)
|
||||||
|
path = os.path.expandvars(path)
|
||||||
|
path = os.path.abspath(path)
|
||||||
|
home = os.path.expanduser(TILD) + os.sep
|
||||||
|
|
||||||
|
# normalize the path
|
||||||
|
if path.startswith(home):
|
||||||
|
path = path[len(home):]
|
||||||
|
path = os.path.join(TILD, path)
|
||||||
|
return path
|
||||||
|
|
||||||
|
def get_dotfile_by_dst(self, dst):
|
||||||
"""get a dotfile by dst"""
|
"""get a dotfile by dst"""
|
||||||
|
dst = self.path_to_dotfile_dst(dst)
|
||||||
try:
|
try:
|
||||||
return next(d for d in self.dotfiles if d.dst == dst)
|
return next(d for d in self.dotfiles if d.dst == dst)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
@@ -262,12 +286,24 @@ class CfgAggregator:
|
|||||||
except StopIteration:
|
except StopIteration:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_profiles_by_dotfile_key(self, key):
|
||||||
|
"""return all profiles having this dotfile"""
|
||||||
|
res = []
|
||||||
|
for p in self.profiles:
|
||||||
|
keys = [d.key for d in p.dotfiles]
|
||||||
|
if key in keys:
|
||||||
|
res.append(p)
|
||||||
|
return res
|
||||||
|
|
||||||
def get_dotfiles(self, profile=None):
|
def get_dotfiles(self, profile=None):
|
||||||
"""return dotfiles dict for this profile key"""
|
"""return dotfiles dict for this profile key"""
|
||||||
if not profile:
|
if not profile:
|
||||||
return self.dotfiles
|
return self.dotfiles
|
||||||
try:
|
try:
|
||||||
return next(x.dotfiles for x in self.profiles if x.key == profile)
|
pro = self.get_profile(profile)
|
||||||
|
if not pro:
|
||||||
|
return []
|
||||||
|
return pro.dotfiles
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -278,7 +314,7 @@ class CfgAggregator:
|
|||||||
except StopIteration:
|
except StopIteration:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_action(self, key):
|
def _get_action(self, key):
|
||||||
"""return action by key"""
|
"""return action by key"""
|
||||||
try:
|
try:
|
||||||
return next(x for x in self.actions if x.key == key)
|
return next(x for x in self.actions if x.key == key)
|
||||||
@@ -293,19 +329,19 @@ class CfgAggregator:
|
|||||||
key, *args = fields
|
key, *args = fields
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('action with parm: {} and {}'.format(key, args))
|
self.log.dbg('action with parm: {} and {}'.format(key, args))
|
||||||
action = self.get_action(key).copy(args)
|
action = self._get_action(key).copy(args)
|
||||||
else:
|
else:
|
||||||
action = self.get_action(key)
|
action = self._get_action(key)
|
||||||
return action
|
return action
|
||||||
|
|
||||||
def get_trans_r(self, key):
|
def _get_trans_r(self, key):
|
||||||
"""return the trans_r with this key"""
|
"""return the trans_r with this key"""
|
||||||
try:
|
try:
|
||||||
return next(x for x in self.trans_r if x.key == key)
|
return next(x for x in self.trans_r if x.key == key)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_trans_w(self, key):
|
def _get_trans_w(self, key):
|
||||||
"""return the trans_w with this key"""
|
"""return the trans_w with this key"""
|
||||||
try:
|
try:
|
||||||
return next(x for x in self.trans_w if x.key == key)
|
return next(x for x in self.trans_w if x.key == key)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ handle lower level of the config file
|
|||||||
import os
|
import os
|
||||||
import yaml
|
import yaml
|
||||||
import glob
|
import glob
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from dotdrop.settings import Settings
|
from dotdrop.settings import Settings
|
||||||
@@ -97,9 +98,10 @@ class CfgYaml:
|
|||||||
|
|
||||||
def _parse_main_yaml(self, dic):
|
def _parse_main_yaml(self, dic):
|
||||||
"""parse the different blocks"""
|
"""parse the different blocks"""
|
||||||
self.ori_settings = self._get_entry(self.yaml_dict, self.key_settings)
|
self.ori_settings = self._get_entry(dic, self.key_settings)
|
||||||
self.settings = Settings(None).serialize().get(self.key_settings)
|
self.settings = Settings(None).serialize().get(self.key_settings)
|
||||||
self.settings.update(self.ori_settings)
|
self.settings.update(self.ori_settings)
|
||||||
|
|
||||||
# resolve settings paths
|
# resolve settings paths
|
||||||
p = self._resolve_path(self.settings[self.key_settings_dotpath])
|
p = self._resolve_path(self.settings[self.key_settings_dotpath])
|
||||||
self.settings[self.key_settings_dotpath] = p
|
self.settings[self.key_settings_dotpath] = p
|
||||||
@@ -109,50 +111,60 @@ class CfgYaml:
|
|||||||
self.log.dbg('settings: {}'.format(self.settings))
|
self.log.dbg('settings: {}'.format(self.settings))
|
||||||
|
|
||||||
# dotfiles
|
# dotfiles
|
||||||
self.dotfiles = self._get_entry(self.yaml_dict, self.key_dotfiles)
|
self.ori_dotfiles = self._get_entry(dic, self.key_dotfiles)
|
||||||
|
self.dotfiles = deepcopy(self.ori_dotfiles)
|
||||||
|
keys = self.dotfiles.keys()
|
||||||
|
if len(keys) != len(list(set(keys))):
|
||||||
|
dups = [x for x in keys if x not in list(set(keys))]
|
||||||
|
raise Exception('duplicate dotfile keys found: {}'.format(dups))
|
||||||
self.dotfiles = self._norm_dotfiles(self.dotfiles)
|
self.dotfiles = self._norm_dotfiles(self.dotfiles)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('dotfiles: {}'.format(self.dotfiles))
|
self.log.dbg('dotfiles: {}'.format(self.dotfiles))
|
||||||
|
|
||||||
# profiles
|
# profiles
|
||||||
self.profiles = self._get_entry(self.yaml_dict, self.key_profiles)
|
self.ori_profiles = self._get_entry(dic, self.key_profiles)
|
||||||
|
self.profiles = deepcopy(self.ori_profiles)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('profiles: {}'.format(self.profiles))
|
self.log.dbg('profiles: {}'.format(self.profiles))
|
||||||
|
|
||||||
# actions
|
# actions
|
||||||
self.actions = self._get_entry(self.yaml_dict, self.key_actions,
|
self.ori_actions = self._get_entry(dic, self.key_actions,
|
||||||
mandatory=False)
|
mandatory=False)
|
||||||
|
self.actions = deepcopy(self.ori_actions)
|
||||||
self.actions = self._norm_actions(self.actions)
|
self.actions = self._norm_actions(self.actions)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('actions: {}'.format(self.actions))
|
self.log.dbg('actions: {}'.format(self.actions))
|
||||||
|
|
||||||
# trans_r
|
# trans_r
|
||||||
if self.old_key_trans_r in self.yaml_dict:
|
key = self.key_trans_r
|
||||||
|
if self.old_key_trans_r in dic:
|
||||||
self.log.warn('\"trans\" is deprecated, please use \"trans_read\"')
|
self.log.warn('\"trans\" is deprecated, please use \"trans_read\"')
|
||||||
self.yaml_dict[self.key_trans_r] = self.yaml_dict.pop(
|
key = self.old_key_trans_r
|
||||||
self.old_key_trans_r
|
self.ori_trans_r = self._get_entry(dic, key, mandatory=False)
|
||||||
)
|
self.trans_r = deepcopy(self.ori_trans_r)
|
||||||
self.dirty = True
|
|
||||||
self.trans_r = self._get_entry(self.yaml_dict, self.key_trans_r,
|
|
||||||
mandatory=False)
|
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('trans_r: {}'.format(self.trans_r))
|
self.log.dbg('trans_r: {}'.format(self.trans_r))
|
||||||
|
|
||||||
# trans_w
|
# trans_w
|
||||||
self.trans_w = self._get_entry(self.yaml_dict, self.key_trans_w,
|
self.ori_trans_w = self._get_entry(dic, self.key_trans_w,
|
||||||
mandatory=False)
|
mandatory=False)
|
||||||
|
self.trans_w = deepcopy(self.ori_trans_w)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('trans_w: {}'.format(self.trans_w))
|
self.log.dbg('trans_w: {}'.format(self.trans_w))
|
||||||
|
|
||||||
# variables
|
# variables
|
||||||
self.variables = self._get_entry(self.yaml_dict, self.key_variables,
|
self.ori_variables = self._get_entry(dic,
|
||||||
mandatory=False)
|
self.key_variables,
|
||||||
|
mandatory=False)
|
||||||
|
self.variables = deepcopy(self.ori_variables)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('variables: {}'.format(self.variables))
|
self.log.dbg('variables: {}'.format(self.variables))
|
||||||
|
|
||||||
# dynvariables
|
# dynvariables
|
||||||
self.dvariables = self._get_entry(self.yaml_dict, self.key_dvariables,
|
self.ori_dvariables = self._get_entry(dic,
|
||||||
mandatory=False)
|
self.key_dvariables,
|
||||||
|
mandatory=False)
|
||||||
|
self.dvariables = deepcopy(self.ori_dvariables)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('dvariables: {}'.format(self.dvariables))
|
self.log.dbg('dvariables: {}'.format(self.dvariables))
|
||||||
|
|
||||||
@@ -493,7 +505,7 @@ class CfgYaml:
|
|||||||
values[self.key_profiles_dotfiles] = uniq_list(current)
|
values[self.key_profiles_dotfiles] = uniq_list(current)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
dfs = values[self.key_profiles_dotfiles]
|
dfs = values[self.key_profiles_dotfiles]
|
||||||
self.log.dbg('profile dfs after include: {}'.format(dfs))
|
self.log.dbg('{} dfs after include: {}'.format(profile, dfs))
|
||||||
return values.get(self.key_profiles_dotfiles, [])
|
return values.get(self.key_profiles_dotfiles, [])
|
||||||
|
|
||||||
def _resolve_path(self, path):
|
def _resolve_path(self, path):
|
||||||
@@ -538,21 +550,21 @@ class CfgYaml:
|
|||||||
"""merge low into high"""
|
"""merge low into high"""
|
||||||
# won't work in python3.4
|
# won't work in python3.4
|
||||||
# return {**low, **high}
|
# return {**low, **high}
|
||||||
new = low.copy()
|
new = deepcopy(low)
|
||||||
new.update(high)
|
new.update(high)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _get_entry(self, yaml_dict, key, mandatory=True):
|
def _get_entry(self, dic, key, mandatory=True):
|
||||||
"""return entry from yaml dictionary"""
|
"""return entry from yaml dictionary"""
|
||||||
if key not in yaml_dict:
|
if key not in dic:
|
||||||
if mandatory:
|
if mandatory:
|
||||||
raise Exception('invalid config: no {} found'.format(key))
|
raise Exception('invalid config: no {} found'.format(key))
|
||||||
yaml_dict[key] = {}
|
dic[key] = {}
|
||||||
return yaml_dict[key]
|
return dic[key]
|
||||||
if mandatory and not yaml_dict[key]:
|
if mandatory and not dic[key]:
|
||||||
# ensure is not none
|
# ensure is not none
|
||||||
yaml_dict[key] = {}
|
dic[key] = {}
|
||||||
return yaml_dict[key]
|
return dic[key]
|
||||||
|
|
||||||
def _load_yaml(self, path):
|
def _load_yaml(self, path):
|
||||||
"""load a yaml file to a dict"""
|
"""load a yaml file to a dict"""
|
||||||
@@ -609,6 +621,40 @@ class CfgYaml:
|
|||||||
self.yaml_dict[self.key_dotfiles][key] = df_dict
|
self.yaml_dict[self.key_dotfiles][key] = df_dict
|
||||||
self.dirty = True
|
self.dirty = True
|
||||||
|
|
||||||
|
def del_dotfile(self, key):
|
||||||
|
"""remove this dotfile from config"""
|
||||||
|
if key not in self.yaml_dict[self.key_dotfiles]:
|
||||||
|
self.log.err('key not in dotfiles: {}'.format(key))
|
||||||
|
return False
|
||||||
|
if self.debug:
|
||||||
|
self.log.dbg('remove dotfile: {}'.format(key))
|
||||||
|
del self.yaml_dict[self.key_dotfiles][key]
|
||||||
|
if self.debug:
|
||||||
|
dfs = self.yaml_dict[self.key_dotfiles]
|
||||||
|
self.log.dbg('new dotfiles: {}'.format(dfs))
|
||||||
|
self.dirty = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
def del_dotfile_from_profile(self, df_key, pro_key):
|
||||||
|
"""remove this dotfile from that profile"""
|
||||||
|
if df_key not in self.dotfiles.keys():
|
||||||
|
self.log.err('key not in dotfiles: {}'.format(df_key))
|
||||||
|
return False
|
||||||
|
if pro_key not in self.profiles.keys():
|
||||||
|
self.log.err('key not in profiles: {}'.format(pro_key))
|
||||||
|
return False
|
||||||
|
profiles = self.yaml_dict[self.key_profiles][pro_key]
|
||||||
|
if self.debug:
|
||||||
|
dfs = profiles[self.key_profiles_dotfiles]
|
||||||
|
self.log.dbg('{} profile dotfiles: {}'.format(pro_key, dfs))
|
||||||
|
self.log.dbg('remove {} from profile {}'.format(df_key, pro_key))
|
||||||
|
profiles[self.key_profiles_dotfiles].remove(df_key)
|
||||||
|
if self.debug:
|
||||||
|
dfs = profiles[self.key_profiles_dotfiles]
|
||||||
|
self.log.dbg('{} profile dotfiles: {}'.format(pro_key, dfs))
|
||||||
|
self.dirty = True
|
||||||
|
return True
|
||||||
|
|
||||||
def _fix_deprecated(self, yamldict):
|
def _fix_deprecated(self, yamldict):
|
||||||
"""fix deprecated entries"""
|
"""fix deprecated entries"""
|
||||||
self._fix_deprecated_link_by_default(yamldict)
|
self._fix_deprecated_link_by_default(yamldict)
|
||||||
@@ -671,9 +717,9 @@ class CfgYaml:
|
|||||||
newv = v
|
newv = v
|
||||||
if isinstance(v, dict):
|
if isinstance(v, dict):
|
||||||
newv = self._clear_none(v)
|
newv = self._clear_none(v)
|
||||||
if v is None:
|
if newv is None:
|
||||||
continue
|
continue
|
||||||
if not v:
|
if not newv:
|
||||||
continue
|
continue
|
||||||
new[k] = newv
|
new[k] = newv
|
||||||
return new
|
return new
|
||||||
|
|||||||
@@ -268,7 +268,10 @@ def cmd_update(o):
|
|||||||
if o.debug:
|
if o.debug:
|
||||||
LOG.dbg('dotfile to update: {}'.format(paths))
|
LOG.dbg('dotfile to update: {}'.format(paths))
|
||||||
|
|
||||||
updater = Updater(o.dotpath, o.dotfiles, o.variables,
|
updater = Updater(o.dotpath, o.variables,
|
||||||
|
o.conf.get_dotfile,
|
||||||
|
o.conf.get_dotfile_by_dst,
|
||||||
|
o.conf.path_to_dotfile_dst,
|
||||||
dry=o.dry, safe=o.safe, debug=o.debug,
|
dry=o.dry, safe=o.safe, debug=o.debug,
|
||||||
ignore=ignore, showpatch=showpatch)
|
ignore=ignore, showpatch=showpatch)
|
||||||
if not iskey:
|
if not iskey:
|
||||||
@@ -403,6 +406,67 @@ def cmd_detail(o):
|
|||||||
LOG.log('')
|
LOG.log('')
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_remove(o):
|
||||||
|
"""remove dotfile from dotpath and from config"""
|
||||||
|
paths = o.remove_path
|
||||||
|
iskey = o.remove_iskey
|
||||||
|
|
||||||
|
if not paths:
|
||||||
|
LOG.log('no dotfile to remove')
|
||||||
|
return False
|
||||||
|
if o.debug:
|
||||||
|
LOG.dbg('dotfile to remove: {}'.format(paths))
|
||||||
|
|
||||||
|
removed = []
|
||||||
|
for key in paths:
|
||||||
|
if o.debug:
|
||||||
|
LOG.dbg('removing {}'.format(key))
|
||||||
|
if not iskey:
|
||||||
|
# by path
|
||||||
|
dotfile = o.conf.get_dotfile_by_dst(key)
|
||||||
|
if not dotfile:
|
||||||
|
LOG.warn('{} ignored, does not exist'.format(key))
|
||||||
|
continue
|
||||||
|
k = dotfile.key
|
||||||
|
else:
|
||||||
|
# by key
|
||||||
|
dotfile = o.conf.get_dotfile(key)
|
||||||
|
k = key
|
||||||
|
# make sure is part of the profile
|
||||||
|
if dotfile.key not in [d.key for d in o.dotfiles]:
|
||||||
|
LOG.warn('{} ignored, not associated to this profile'.format(key))
|
||||||
|
continue
|
||||||
|
profiles = o.conf.get_profiles_by_dotfile_key(k)
|
||||||
|
pkeys = ','.join([p.key for p in profiles])
|
||||||
|
if o.dry:
|
||||||
|
LOG.dry('would remove {} from {}'.format(dotfile, pkeys))
|
||||||
|
continue
|
||||||
|
msg = 'Remove dotfile from all these profiles: {}'.format(pkeys)
|
||||||
|
if o.safe and not LOG.ask(msg):
|
||||||
|
return False
|
||||||
|
if o.debug:
|
||||||
|
LOG.dbg('remove dotfile: {}'.format(dotfile))
|
||||||
|
|
||||||
|
for profile in profiles:
|
||||||
|
if not o.conf.del_dotfile_from_profile(dotfile, profile):
|
||||||
|
return False
|
||||||
|
if not o.conf.del_dotfile(dotfile):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# remove dotfile from dotpath
|
||||||
|
dtpath = os.path.join(o.dotpath, dotfile.src)
|
||||||
|
remove(dtpath)
|
||||||
|
removed.append(dotfile.key)
|
||||||
|
|
||||||
|
if o.dry:
|
||||||
|
LOG.dry('new config file would be:')
|
||||||
|
LOG.raw(o.conf.dump())
|
||||||
|
else:
|
||||||
|
o.conf.save()
|
||||||
|
LOG.log('\ndotfile(s) removed: {}'.format(','.join(removed)))
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
# helpers
|
# helpers
|
||||||
###########################################################
|
###########################################################
|
||||||
@@ -444,8 +508,10 @@ def _select(selections, dotfiles):
|
|||||||
|
|
||||||
|
|
||||||
def apply_trans(dotpath, dotfile, debug=False):
|
def apply_trans(dotpath, dotfile, debug=False):
|
||||||
"""apply the read transformation to the dotfile
|
"""
|
||||||
return None if fails and new source if succeed"""
|
apply the read transformation to the dotfile
|
||||||
|
return None if fails and new source if succeed
|
||||||
|
"""
|
||||||
src = dotfile.src
|
src = dotfile.src
|
||||||
new_src = '{}.{}'.format(src, TRANS_SUFFIX)
|
new_src = '{}.{}'.format(src, TRANS_SUFFIX)
|
||||||
for trans in dotfile.trans_r:
|
for trans in dotfile.trans_r:
|
||||||
@@ -523,6 +589,12 @@ def main():
|
|||||||
LOG.dbg('running cmd: detail')
|
LOG.dbg('running cmd: detail')
|
||||||
cmd_detail(o)
|
cmd_detail(o)
|
||||||
|
|
||||||
|
elif o.cmd_remove:
|
||||||
|
# remove dotfile
|
||||||
|
if o.debug:
|
||||||
|
LOG.dbg('running cmd: remove')
|
||||||
|
cmd_remove(o)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
LOG.err('interrupted')
|
LOG.err('interrupted')
|
||||||
ret = False
|
ret = False
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ Usage:
|
|||||||
[-o <opts>] [-C <file>...] [-i <pattern>...]
|
[-o <opts>] [-C <file>...] [-i <pattern>...]
|
||||||
dotdrop update [-VbfdkP] [-c <path>] [-p <profile>]
|
dotdrop update [-VbfdkP] [-c <path>] [-p <profile>]
|
||||||
[-i <pattern>...] [<path>...]
|
[-i <pattern>...] [<path>...]
|
||||||
|
dotdrop remove [-Vbfdk] [-c <path>] [-p <profile>] [<path>...]
|
||||||
dotdrop listfiles [-VbT] [-c <path>] [-p <profile>]
|
dotdrop listfiles [-VbT] [-c <path>] [-p <profile>]
|
||||||
dotdrop detail [-Vb] [-c <path>] [-p <profile>] [<key>...]
|
dotdrop detail [-Vb] [-c <path>] [-p <profile>] [<key>...]
|
||||||
dotdrop list [-Vb] [-c <path>]
|
dotdrop list [-Vb] [-c <path>]
|
||||||
@@ -193,6 +194,7 @@ class Options(AttrMonitor):
|
|||||||
self.cmd_import = self.args['import']
|
self.cmd_import = self.args['import']
|
||||||
self.cmd_update = self.args['update']
|
self.cmd_update = self.args['update']
|
||||||
self.cmd_detail = self.args['detail']
|
self.cmd_detail = self.args['detail']
|
||||||
|
self.cmd_remove = self.args['remove']
|
||||||
|
|
||||||
# adapt attributes based on arguments
|
# adapt attributes based on arguments
|
||||||
self.dry = self.args['--dry']
|
self.dry = self.args['--dry']
|
||||||
@@ -236,6 +238,9 @@ class Options(AttrMonitor):
|
|||||||
self.update_showpatch = self.args['--show-patch']
|
self.update_showpatch = self.args['--show-patch']
|
||||||
# "detail" specifics
|
# "detail" specifics
|
||||||
self.detail_keys = self.args['<key>']
|
self.detail_keys = self.args['<key>']
|
||||||
|
# "remove" specifics
|
||||||
|
self.remove_path = self.args['<path>']
|
||||||
|
self.remove_iskey = self.args['--key']
|
||||||
|
|
||||||
def _fill_attr(self):
|
def _fill_attr(self):
|
||||||
"""create attributes from conf"""
|
"""create attributes from conf"""
|
||||||
|
|||||||
@@ -20,12 +20,17 @@ TILD = '~'
|
|||||||
|
|
||||||
class Updater:
|
class Updater:
|
||||||
|
|
||||||
def __init__(self, dotpath, dotfiles, variables, dry=False, safe=True,
|
def __init__(self, dotpath, variables,
|
||||||
|
dotfile_key_getter, dotfile_dst_getter,
|
||||||
|
dotfile_path_normalizer,
|
||||||
|
dry=False, safe=True,
|
||||||
debug=False, ignore=[], showpatch=False):
|
debug=False, ignore=[], showpatch=False):
|
||||||
"""constructor
|
"""constructor
|
||||||
@dotpath: path where dotfiles are stored
|
@dotpath: path where dotfiles are stored
|
||||||
@dotfiles: dotfiles for this profile
|
|
||||||
@variables: dictionary of variables for the templates
|
@variables: dictionary of variables for the templates
|
||||||
|
@dotfile_key_getter: func to get a dotfile by key
|
||||||
|
@dotfile_dst_getter: func to get a dotfile by dst
|
||||||
|
@dotfile_path_normalizer: func to normalize dotfile dst
|
||||||
@dry: simulate
|
@dry: simulate
|
||||||
@safe: ask for overwrite if True
|
@safe: ask for overwrite if True
|
||||||
@debug: enable debug
|
@debug: enable debug
|
||||||
@@ -33,8 +38,10 @@ class Updater:
|
|||||||
@showpatch: show patch if dotfile to update is a template
|
@showpatch: show patch if dotfile to update is a template
|
||||||
"""
|
"""
|
||||||
self.dotpath = dotpath
|
self.dotpath = dotpath
|
||||||
self.dotfiles = dotfiles
|
|
||||||
self.variables = variables
|
self.variables = variables
|
||||||
|
self.dotfile_key_getter = dotfile_key_getter
|
||||||
|
self.dotfile_dst_getter = dotfile_dst_getter
|
||||||
|
self.dotfile_path_normalizer = dotfile_path_normalizer
|
||||||
self.dry = dry
|
self.dry = dry
|
||||||
self.safe = safe
|
self.safe = safe
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
@@ -48,8 +55,7 @@ class Updater:
|
|||||||
if not os.path.lexists(path):
|
if not os.path.lexists(path):
|
||||||
self.log.err('\"{}\" does not exist!'.format(path))
|
self.log.err('\"{}\" does not exist!'.format(path))
|
||||||
return False
|
return False
|
||||||
path = self._normalize(path)
|
dotfile = self.dotfile_dst_getter(path)
|
||||||
dotfile = self._get_dotfile_by_path(path)
|
|
||||||
if not dotfile:
|
if not dotfile:
|
||||||
return False
|
return False
|
||||||
if self.debug:
|
if self.debug:
|
||||||
@@ -58,12 +64,12 @@ class Updater:
|
|||||||
|
|
||||||
def update_key(self, key):
|
def update_key(self, key):
|
||||||
"""update the dotfile referenced by key"""
|
"""update the dotfile referenced by key"""
|
||||||
dotfile = self._get_dotfile_by_key(key)
|
dotfile = self.dotfile_key_getter(key)
|
||||||
if not dotfile:
|
if not dotfile:
|
||||||
return False
|
return False
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('updating {} from key \"{}\"'.format(dotfile, key))
|
self.log.dbg('updating {} from key \"{}\"'.format(dotfile, key))
|
||||||
path = self._normalize(dotfile.dst)
|
path = self.dotfile_path_normalizer(dotfile.dst)
|
||||||
return self._update(path, dotfile)
|
return self._update(path, dotfile)
|
||||||
|
|
||||||
def _update(self, path, dotfile):
|
def _update(self, path, dotfile):
|
||||||
@@ -111,45 +117,6 @@ class Updater:
|
|||||||
return None
|
return None
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
def _normalize(self, path):
|
|
||||||
"""normalize the path to match dotfile"""
|
|
||||||
path = os.path.expanduser(path)
|
|
||||||
path = os.path.expandvars(path)
|
|
||||||
path = os.path.abspath(path)
|
|
||||||
home = os.path.expanduser(TILD) + os.sep
|
|
||||||
|
|
||||||
# normalize the path
|
|
||||||
if path.startswith(home):
|
|
||||||
path = path[len(home):]
|
|
||||||
path = os.path.join(TILD, path)
|
|
||||||
return path
|
|
||||||
|
|
||||||
def _get_dotfile_by_key(self, key):
|
|
||||||
"""get the dotfile matching this key"""
|
|
||||||
dotfiles = self.dotfiles
|
|
||||||
subs = [d for d in dotfiles if d.key == key]
|
|
||||||
if not subs:
|
|
||||||
self.log.err('key \"{}\" not found!'.format(key))
|
|
||||||
return None
|
|
||||||
if len(subs) > 1:
|
|
||||||
found = ','.join([d.src for d in dotfiles])
|
|
||||||
self.log.err('multiple dotfiles found: {}'.format(found))
|
|
||||||
return None
|
|
||||||
return subs[0]
|
|
||||||
|
|
||||||
def _get_dotfile_by_path(self, path):
|
|
||||||
"""get the dotfile matching this path"""
|
|
||||||
dotfiles = self.dotfiles
|
|
||||||
subs = [d for d in dotfiles if d.dst == path]
|
|
||||||
if not subs:
|
|
||||||
self.log.err('\"{}\" is not managed!'.format(path))
|
|
||||||
return None
|
|
||||||
if len(subs) > 1:
|
|
||||||
found = ','.join([d.src for d in dotfiles])
|
|
||||||
self.log.err('multiple dotfiles found: {}'.format(found))
|
|
||||||
return None
|
|
||||||
return subs[0]
|
|
||||||
|
|
||||||
def _is_template(self, path):
|
def _is_template(self, path):
|
||||||
if not Templategen.is_template(path):
|
if not Templategen.is_template(path):
|
||||||
if self.debug:
|
if self.debug:
|
||||||
|
|||||||
179
tests-ng/remove.sh
Executable file
179
tests-ng/remove.sh
Executable file
@@ -0,0 +1,179 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# author: deadc0de6 (https://github.com/deadc0de6)
|
||||||
|
# Copyright (c) 2019, deadc0de6
|
||||||
|
#
|
||||||
|
# test remove
|
||||||
|
# returns 1 in case of error
|
||||||
|
#
|
||||||
|
|
||||||
|
# exit on first error
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# all this crap to get current path
|
||||||
|
rl="readlink -f"
|
||||||
|
if ! ${rl} "${0}" >/dev/null 2>&1; then
|
||||||
|
rl="realpath"
|
||||||
|
|
||||||
|
if ! hash ${rl}; then
|
||||||
|
echo "\"${rl}\" not found !" && exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
cur=$(dirname "$(${rl} "${0}")")
|
||||||
|
|
||||||
|
#hash dotdrop >/dev/null 2>&1
|
||||||
|
#[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1
|
||||||
|
|
||||||
|
#echo "called with ${1}"
|
||||||
|
|
||||||
|
# dotdrop path can be pass as argument
|
||||||
|
ddpath="${cur}/../"
|
||||||
|
[ "${1}" != "" ] && ddpath="${1}"
|
||||||
|
[ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1
|
||||||
|
|
||||||
|
export PYTHONPATH="${ddpath}:${PYTHONPATH}"
|
||||||
|
bin="python3 -m dotdrop.dotdrop"
|
||||||
|
|
||||||
|
echo "dotdrop path: ${ddpath}"
|
||||||
|
echo "pythonpath: ${PYTHONPATH}"
|
||||||
|
|
||||||
|
# get the helpers
|
||||||
|
source ${cur}/helpers
|
||||||
|
|
||||||
|
echo -e "\e[96m\e[1m==> RUNNING $(basename $BASH_SOURCE) <==\e[0m"
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# this is the test
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# dotdrop directory
|
||||||
|
tmps=`mktemp -d --suffix='-dotdrop-tests'`
|
||||||
|
mkdir -p ${tmps}/dotfiles
|
||||||
|
# the dotfile to be imported
|
||||||
|
tmpd=`mktemp -d --suffix='-dotdrop-tests'`
|
||||||
|
|
||||||
|
# create the config file
|
||||||
|
cfg="${tmps}/config.yaml"
|
||||||
|
cat > ${cfg} << _EOF
|
||||||
|
config:
|
||||||
|
backup: true
|
||||||
|
create: true
|
||||||
|
dotpath: dotfiles
|
||||||
|
dotfiles:
|
||||||
|
f_abc:
|
||||||
|
dst: ${tmpd}/abc
|
||||||
|
src: abc
|
||||||
|
f_def:
|
||||||
|
dst: ${tmpd}/def
|
||||||
|
src: def
|
||||||
|
f_last:
|
||||||
|
dst: ${tmpd}/last
|
||||||
|
src: last
|
||||||
|
profiles:
|
||||||
|
p1:
|
||||||
|
dotfiles:
|
||||||
|
- f_abc
|
||||||
|
- f_def
|
||||||
|
p2:
|
||||||
|
dotfiles:
|
||||||
|
- f_def
|
||||||
|
last:
|
||||||
|
dotfiles:
|
||||||
|
- f_last
|
||||||
|
_EOF
|
||||||
|
cfgbak="${tmps}/config.yaml.bak"
|
||||||
|
cp ${cfg} ${cfgbak}
|
||||||
|
|
||||||
|
# create the dotfile
|
||||||
|
echo "abc" > ${tmps}/dotfiles/abc
|
||||||
|
echo "abc" > ${tmpd}/abc
|
||||||
|
|
||||||
|
echo "def" > ${tmps}/dotfiles/def
|
||||||
|
echo "def" > ${tmpd}/def
|
||||||
|
|
||||||
|
# remove with bad profile
|
||||||
|
cd ${ddpath} | ${bin} remove -f -k -p empty -c ${cfg} f_abc -V
|
||||||
|
[ ! -e ${tmps}/dotfiles/abc ] && echo "dotfile in dotpath deleted" && exit 1
|
||||||
|
[ ! -e ${tmpd}/abc ] && echo "source dotfile deleted" && exit 1
|
||||||
|
[ ! -e ${tmps}/dotfiles/def ] && echo "dotfile in dotpath deleted" && exit 1
|
||||||
|
[ ! -e ${tmpd}/def ] && echo "source dotfile deleted" && exit 1
|
||||||
|
# ensure config not altered
|
||||||
|
diff ${cfg} ${cfgbak}
|
||||||
|
|
||||||
|
# remove by key
|
||||||
|
echo "[+] remove f_abc by key"
|
||||||
|
cd ${ddpath} | ${bin} remove -p p1 -f -k -c ${cfg} f_abc -V
|
||||||
|
cat ${cfg}
|
||||||
|
echo "[+] remove f_def by key"
|
||||||
|
cd ${ddpath} | ${bin} remove -p p2 -f -k -c ${cfg} f_def -V
|
||||||
|
cat ${cfg}
|
||||||
|
|
||||||
|
# checks
|
||||||
|
[ -e ${tmps}/dotfiles/abc ] && echo "dotfile in dotpath not deleted" && exit 1
|
||||||
|
[ ! -e ${tmpd}/abc ] && echo "source dotfile deleted" && exit 1
|
||||||
|
|
||||||
|
[ -e ${tmps}/dotfiles/def ] && echo "dotfile in dotpath not deleted" && exit 1
|
||||||
|
[ ! -e ${tmpd}/def ] && echo "source dotfile deleted" && exit 1
|
||||||
|
|
||||||
|
echo "[+] ========="
|
||||||
|
|
||||||
|
# create the config file
|
||||||
|
cfg="${tmps}/config.yaml"
|
||||||
|
cat > ${cfg} << _EOF
|
||||||
|
config:
|
||||||
|
backup: true
|
||||||
|
create: true
|
||||||
|
dotpath: dotfiles
|
||||||
|
dotfiles:
|
||||||
|
f_abc:
|
||||||
|
dst: ${tmpd}/abc
|
||||||
|
src: abc
|
||||||
|
f_def:
|
||||||
|
dst: ${tmpd}/def
|
||||||
|
src: def
|
||||||
|
f_last:
|
||||||
|
dst: ${tmpd}/last
|
||||||
|
src: last
|
||||||
|
profiles:
|
||||||
|
p1:
|
||||||
|
dotfiles:
|
||||||
|
- f_abc
|
||||||
|
- f_def
|
||||||
|
p2:
|
||||||
|
dotfiles:
|
||||||
|
- f_def
|
||||||
|
last:
|
||||||
|
dotfiles:
|
||||||
|
- f_last
|
||||||
|
_EOF
|
||||||
|
cat ${cfg}
|
||||||
|
|
||||||
|
# create the dotfile
|
||||||
|
echo "abc" > ${tmps}/dotfiles/abc
|
||||||
|
echo "abc" > ${tmpd}/abc
|
||||||
|
|
||||||
|
echo "def" > ${tmps}/dotfiles/def
|
||||||
|
echo "def" > ${tmpd}/def
|
||||||
|
|
||||||
|
# remove by key
|
||||||
|
echo "[+] remove f_abc by path"
|
||||||
|
cd ${ddpath} | ${bin} remove -p p1 -f -c ${cfg} ${tmpd}/abc -V
|
||||||
|
cat ${cfg}
|
||||||
|
echo "[+] remove f_def by path"
|
||||||
|
cd ${ddpath} | ${bin} remove -p p2 -f -c ${cfg} ${tmpd}/def -V
|
||||||
|
cat ${cfg}
|
||||||
|
|
||||||
|
# checks
|
||||||
|
[ -e ${tmps}/dotfiles/abc ] && echo "(2) dotfile in dotpath not deleted" && exit 1
|
||||||
|
[ ! -e ${tmpd}/abc ] && echo "(2) source dotfile deleted" && exit 1
|
||||||
|
|
||||||
|
[ -e ${tmps}/dotfiles/def ] && echo "(2) dotfile in dotpath not deleted" && exit 1
|
||||||
|
[ ! -e ${tmpd}/def ] && echo "(2) source dotfile deleted" && exit 1
|
||||||
|
|
||||||
|
|
||||||
|
cat ${cfg}
|
||||||
|
|
||||||
|
## CLEANING
|
||||||
|
rm -rf ${tmps} ${tmpd}
|
||||||
|
|
||||||
|
echo "OK"
|
||||||
|
exit 0
|
||||||
@@ -136,6 +136,7 @@ def _fake_args():
|
|||||||
args['import'] = False
|
args['import'] = False
|
||||||
args['update'] = False
|
args['update'] = False
|
||||||
args['detail'] = False
|
args['detail'] = False
|
||||||
|
args['remove'] = False
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
@@ -172,8 +173,13 @@ def get_dotfile_from_yaml(dic, path):
|
|||||||
# path is not the file in dotpath but on the FS
|
# path is not the file in dotpath but on the FS
|
||||||
dotfiles = dic['dotfiles']
|
dotfiles = dic['dotfiles']
|
||||||
# src = get_path_strip_version(path)
|
# src = get_path_strip_version(path)
|
||||||
dotfile = [d for d in dotfiles.values() if d['dst'] == path][0]
|
home = os.path.expanduser('~')
|
||||||
return dotfile
|
if path.startswith(home):
|
||||||
|
path = path.replace(home, '~')
|
||||||
|
dotfile = [d for d in dotfiles.values() if d['dst'] == path]
|
||||||
|
if dotfile:
|
||||||
|
return dotfile[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def yaml_dashed_list(items, indent=0):
|
def yaml_dashed_list(items, indent=0):
|
||||||
@@ -261,6 +267,11 @@ def file_in_yaml(yaml_file, path, link=False):
|
|||||||
in_dst = path in (os.path.expanduser(x['dst']) for x in dotfiles)
|
in_dst = path in (os.path.expanduser(x['dst']) for x in dotfiles)
|
||||||
|
|
||||||
if link:
|
if link:
|
||||||
has_link = 'link' in get_dotfile_from_yaml(yaml_conf, path)
|
df = get_dotfile_from_yaml(yaml_conf, path)
|
||||||
|
has_link = False
|
||||||
|
if df:
|
||||||
|
has_link = 'link' in df
|
||||||
|
else:
|
||||||
|
return False
|
||||||
return in_src and in_dst and has_link
|
return in_src and in_dst and has_link
|
||||||
return in_src and in_dst
|
return in_src and in_dst
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ class TestImport(unittest.TestCase):
|
|||||||
self.assertFalse(any(a.endswith('ing') for a in actions))
|
self.assertFalse(any(a.endswith('ing') for a in actions))
|
||||||
|
|
||||||
# testing transformations
|
# testing transformations
|
||||||
transformations = y['trans_read'].keys()
|
transformations = y['trans'].keys()
|
||||||
self.assertTrue(all(t.endswith('ed') for t in transformations))
|
self.assertTrue(all(t.endswith('ed') for t in transformations))
|
||||||
self.assertFalse(any(t.endswith('ing') for t in transformations))
|
self.assertFalse(any(t.endswith('ing') for t in transformations))
|
||||||
transformations = y['trans_write'].keys()
|
transformations = y['trans_write'].keys()
|
||||||
@@ -394,7 +394,7 @@ class TestImport(unittest.TestCase):
|
|||||||
self.assertFalse(any(action.endswith('ed') for action in actions))
|
self.assertFalse(any(action.endswith('ed') for action in actions))
|
||||||
|
|
||||||
# testing transformations
|
# testing transformations
|
||||||
transformations = y['trans_read'].keys()
|
transformations = y['trans'].keys()
|
||||||
self.assertTrue(all(t.endswith('ing') for t in transformations))
|
self.assertTrue(all(t.endswith('ing') for t in transformations))
|
||||||
self.assertFalse(any(t.endswith('ed') for t in transformations))
|
self.assertFalse(any(t.endswith('ed') for t in transformations))
|
||||||
transformations = y['trans_write'].keys()
|
transformations = y['trans_write'].keys()
|
||||||
|
|||||||
@@ -486,7 +486,6 @@ exec bspwm
|
|||||||
# ensure dst is link
|
# ensure dst is link
|
||||||
self.assertTrue(os.path.islink(dst))
|
self.assertTrue(os.path.islink(dst))
|
||||||
# ensure dst not directly linked to src
|
# ensure dst not directly linked to src
|
||||||
# TODO: maybe check that its actually linked to template folder
|
|
||||||
self.assertNotEqual(os.path.realpath(dst), src)
|
self.assertNotEqual(os.path.realpath(dst), src)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user