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

Merge pull request #222 from deadc0de6/import-as

Import as
This commit is contained in:
deadc0de
2020-04-16 18:23:20 +02:00
committed by GitHub
8 changed files with 293 additions and 68 deletions

View File

@@ -147,6 +147,16 @@ class CfgAggregator:
"""remove this dotfile from this profile"""
return self.cfgyaml.del_dotfile_from_profile(dotfile.key, profile.key)
def _create_new_dotfile(self, src, dst, link):
"""create a new dotfile"""
# get a new dotfile with a unique key
key = self._get_new_dotfile_key(dst)
if self.debug:
self.log.dbg('new dotfile key: {}'.format(key))
# add the dotfile
self.cfgyaml.add_dotfile(key, src, dst, link)
return Dotfile(key, dst, src)
def new(self, src, dst, link):
"""
import a new dotfile
@@ -155,34 +165,31 @@ class CfgAggregator:
@link: LinkType
"""
dst = self.path_to_dotfile_dst(dst)
dotfile = self.get_dotfile_by_dst(dst)
dotfile = self.get_dotfile_by_src_dst(src, dst)
if not dotfile:
# get a new dotfile with a unique key
key = self._get_new_dotfile_key(dst)
if self.debug:
self.log.dbg('new dotfile key: {}'.format(key))
# add the dotfile
self.cfgyaml.add_dotfile(key, src, dst, link)
dotfile = Dotfile(key, dst, src)
dotfile = self._create_new_dotfile(src, dst, link)
key = dotfile.key
ret = self.cfgyaml.add_dotfile_to_profile(key, self.profile_key)
if self.debug:
if ret and self.debug:
msg = 'new dotfile {} to profile {}'
self.log.dbg(msg.format(key, self.profile_key))
# reload
self.cfgyaml.save()
if self.debug:
self.log.dbg('reloading config')
self._load()
if ret:
# reload
if self.debug:
self.log.dbg('reloading config')
olddebug = self.debug
self.debug = False
self._load()
self.debug = olddebug
return ret
def _get_new_dotfile_key(self, dst):
"""return a new unique dotfile key"""
path = os.path.expanduser(dst)
existing_keys = [x.key for x in self.dotfiles]
existing_keys = self.cfgyaml.get_all_dotfile_keys()
if self.settings.longkey:
return self._get_long_key(path, existing_keys)
return self._get_short_key(path, existing_keys)
@@ -257,11 +264,28 @@ class CfgAggregator:
return path
def get_dotfile_by_dst(self, dst):
"""get a dotfile by dst"""
"""
get a list of dotfiles by dst
@dst: dotfile dst (on filesystem)
"""
dotfiles = []
dst = self._norm_path(dst)
for d in self.dotfiles:
left = self._norm_path(d.dst)
if left == dst:
dotfiles.append(d)
return dotfiles
def get_dotfile_by_src_dst(self, src, dst):
"""
get a dotfile by src and dst
@src: dotfile src (in dotpath)
@dst: dotfile dst (on filesystem)
"""
src = self.cfgyaml.resolve_dotfile_src(src)
dotfiles = self.get_dotfile_by_dst(dst)
for d in dotfiles:
if d.src == src:
return d
return None

View File

@@ -818,6 +818,10 @@ class CfgYaml:
self.dirty = True
return self.dirty
def get_all_dotfile_keys(self):
"""return all existing dotfile keys"""
return self.dotfiles.keys()
def add_dotfile(self, key, src, dst, link):
"""add a new dotfile"""
if key in self.dotfiles.keys():

View File

@@ -341,7 +341,17 @@ def cmd_importer(o):
continue
dst = path.rstrip(os.sep)
dst = os.path.abspath(dst)
src = strip_home(dst)
if o.import_as:
# handle import as
src = os.path.expanduser(o.import_as)
src = src.rstrip(os.sep)
src = os.path.abspath(src)
src = strip_home(src)
if o.debug:
LOG.dbg('import src for {} as {}'.format(dst, src))
strip = '.' + os.sep
if o.keepdot:
strip = os.sep
@@ -358,6 +368,25 @@ def cmd_importer(o):
if o.debug:
LOG.dbg('import dotfile: src:{} dst:{}'.format(src, dst))
# test no other dotfile exists with same
# dst for this profile but different src
dfs = o.conf.get_dotfile_by_dst(dst)
if dfs:
invalid = False
for df in dfs:
profiles = o.conf.get_profiles_by_dotfile_key(df.key)
profiles = [x.key for x in profiles]
if o.profile in profiles and \
not o.conf.get_dotfile_by_src_dst(src, dst):
# same profile
# different src
LOG.err('duplicate dotfile for this profile')
ret = False
invalid = True
break
if invalid:
continue
# prepare hierarchy for dotfile
srcf = os.path.join(o.dotpath, src)
overwrite = not os.path.exists(srcf)
@@ -481,52 +510,54 @@ def cmd_remove(o):
for key in paths:
if not iskey:
# by path
dotfile = o.conf.get_dotfile_by_dst(key)
if not dotfile:
dotfiles = o.conf.get_dotfile_by_dst(key)
if not dotfiles:
LOG.warn('{} ignored, does not exist'.format(key))
continue
k = dotfile.key
else:
# by key
dotfile = o.conf.get_dotfile(key)
if not dotfile:
LOG.warn('{} ignored, does not exist'.format(key))
continue
k = key
dotfiles = [dotfile]
# ignore if uses any type of link
if dotfile.link != LinkTypes.NOLINK:
LOG.warn('dotfile uses link, remove manually')
continue
for dotfile in dotfiles:
k = dotfile.key
# ignore if uses any type of link
if dotfile.link != LinkTypes.NOLINK:
LOG.warn('dotfile uses link, remove manually')
continue
if o.debug:
LOG.dbg('removing {}'.format(key))
if o.debug:
LOG.dbg('removing {}'.format(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 \"{}\" from all these profiles: {}'.format(k, 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):
# make sure is part of the profile
if dotfile.key not in [d.key for d in o.dotfiles]:
msg = '{} ignored, not associated to this profile'
LOG.warn(msg.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 \"{}\" from all these profiles: {}'.format(k, pkeys)
if o.safe and not LOG.ask(msg):
return False
if not o.conf.del_dotfile(dotfile):
return False
if o.debug:
LOG.dbg('remove dotfile: {}'.format(dotfile))
# remove dotfile from dotpath
dtpath = os.path.join(o.dotpath, dotfile.src)
remove(dtpath)
removed.append(dotfile.key)
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:')

View File

@@ -52,7 +52,7 @@ USAGE = """
Usage:
dotdrop install [-VbtfndDa] [-c <path>] [-p <profile>] [<key>...]
dotdrop import [-Vbdf] [-c <path>] [-p <profile>]
dotdrop import [-Vbdf] [-c <path>] [-p <profile>] [-s <path>]
[-l <link>] <path>...
dotdrop compare [-Vb] [-c <path>] [-p <profile>]
[-C <file>...] [-i <pattern>...]
@@ -72,6 +72,7 @@ Options:
-i --ignore=<pattern> Pattern to ignore.
-l --link=<link> Link option (nolink|link|link_children).
-p --profile=<profile> Specify the profile to use [default: {}].
-s --as=<path> Import as a different path from actual path.
-b --no-banner Do not display the banner.
-d --dry Dry run.
-D --showdiff Show a diff before overwriting.
@@ -237,6 +238,7 @@ class Options(AttrMonitor):
self.compare_ignore = uniq_list(self.compare_ignore)
# "import" specifics
self.import_path = self.args['<path>']
self.import_as = self.args['--as']
# "update" specifics
self.update_path = self.args['<path>']
self.update_iskey = self.args['--key']

View File

@@ -61,12 +61,21 @@ class Updater:
if not os.path.lexists(path):
self.log.err('\"{}\" does not exist!'.format(path))
return False
dotfile = self.dotfile_dst_getter(path)
if not dotfile:
dotfiles = self.dotfile_dst_getter(path)
if not dotfiles:
return False
if self.debug:
self.log.dbg('updating {} from path \"{}\"'.format(dotfile, path))
return self._update(path, dotfile)
for dotfile in dotfiles:
if not dotfile:
msg = 'invalid dotfile for update: {}'
self.log.err(msg.format(dotfile.key))
return False
if self.debug:
msg = 'updating {} from path \"{}\"'
self.log.dbg(msg.format(dotfile, path))
if not self._update(path, dotfile):
return False
return True
def update_key(self, key):
"""update the dotfile referenced by key"""

149
tests-ng/import-as.sh Executable file
View File

@@ -0,0 +1,149 @@
#!/usr/bin/env bash
# author: deadc0de6 (https://github.com/deadc0de6)
# Copyright (c) 2019, deadc0de6
#
# test basic import
#
# 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 "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)"
################################################################
# this is the test
################################################################
clean()
{
rm -rf ${tmps} ${tmpd} ~/.dotdrop.test ~/.dotdrop-dotfiles-test
}
# the dotfile source
tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d`
mkdir -p ${tmps}/dotfiles
# the dotfile destination
tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d`
#echo "dotfile destination: ${tmpd}"
# create the dotfile
mkdir -p ${tmpd}/adir
echo "adir/file1" > ${tmpd}/adir/file1
echo "adir/fil2" > ${tmpd}/adir/file2
echo "file3" > ${tmpd}/file3
# create the config file
cfg="${tmps}/config.yaml"
cat > ${cfg} << _EOF
config:
backup: true
create: true
dotpath: dotfiles
dotfiles:
profiles:
_EOF
#cat ${cfg}
# import
cd ${ddpath} | ${bin} import -c ${cfg} -p p1 -V ${tmpd}/adir
cd ${ddpath} | ${bin} import -c ${cfg} -p p1 -V ${tmpd}/file3
echo "import --as dotfiles"
cd ${ddpath} | ${bin} import -c ${cfg} -p p2 -V ${tmpd}/adir --as ~/config/adir
cd ${ddpath} | ${bin} import -c ${cfg} -p p2 -V ${tmpd}/file3 --as ~/config2/file3
cat ${cfg}
set +e
cd ${ddpath} | ${bin} import -c ${cfg} -p p2 -V ${tmpd}/adir --as ~/config/should_not && echo "dual dst imported" && exit 1
set -e
cat ${cfg} | grep should_not && echo "dual dst imported" && exit 1
cat ${cfg}
echo "ensure exists and is not link"
[ ! -d ${tmps}/dotfiles/${tmpd}/adir ] && echo "not a directory" && exit 1
[ ! -e ${tmps}/dotfiles/${tmpd}/adir/file1 ] && echo "not exist" && exit 1
[ ! -e ${tmps}/dotfiles/${tmpd}/adir/file2 ] && echo "not exist" && exit 1
[ ! -e ${tmps}/dotfiles/${tmpd}/file3 ] && echo "not a file" && exit 1
echo "ensure --as are correctly imported"
[ ! -d ${tmps}/dotfiles/config/adir ] && echo "not a directory" && exit 1
[ ! -e ${tmps}/dotfiles/config/adir/file1 ] && echo "not exist" && exit 1
[ ! -e ${tmps}/dotfiles/config/adir/file2 ] && echo "not exist" && exit 1
[ ! -e ${tmps}/dotfiles/config2/file3 ] && echo "not a file" && exit 1
cat ${cfg} | grep ${tmpd}/adir >/dev/null 2>&1
cat ${cfg} | grep ${tmpd}/file3 >/dev/null 2>&1
cat ${cfg} | grep config/adir >/dev/null 2>&1
cat ${cfg} | grep config2/file3 >/dev/null 2>&1
nb=`cat ${cfg} | grep d_adir | wc -l`
[ "${nb}" != "2" ] && echo 'bad config1' && exit 1
nb=`cat ${cfg} | grep f_file3 | wc -l`
[ "${nb}" != "2" ] && echo 'bad config2' && exit 1
cat ${cfg} | grep "src: config/adir" || exit 1
cat ${cfg} | grep "src: config2/file3" || exit 1
# test import from sub in home
mkdir -p ~/.dotdrop-dotfiles-test/{dotfiles,config}
cfg=~/.dotdrop-dotfiles-test/config/config.yaml
echo 'remove-me' > ~/.dotdrop.test
cat > ${cfg} << _EOF
config:
backup: true
banner: true
create: true
dotpath: ~/.dotdrop-dotfiles-test/dotfiles
keepdot: false
link_dotfile_default: nolink
link_on_import: nolink
longkey: true
dotfiles:
profiles:
_EOF
cd ${ddpath} | ${bin} import -b -c ${cfg} -p test -V ~/.dotdrop.test --as=~/.whatever
#cat ${cfg}
[ ! -e ~/.dotdrop-dotfiles-test/dotfiles/whatever ] && clean && echo 'tild imported' && exit 1
cat ${cfg} | grep '~/.whatever' && clean && echo 'import with tild failed' && exit 1
## CLEANING
clean
echo "OK"
exit 0

View File

@@ -44,6 +44,11 @@ echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)"
# this is the test
################################################################
clean()
{
rm -rf ${tmps} ${tmpd} ~/.dotdrop-test
}
# the dotfile source
tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d`
mkdir -p ${tmps}/dotfiles
@@ -73,11 +78,11 @@ cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/testfile
cat ${cfg}
# ensure exists and is not link
[ ! -e ${tmps}/dotfiles/${tmpd}/testfile ] && echo "does not exist" && exit 1
[ ! -e ${tmps}/dotfiles/${tmpd}/testfile ] && echo "does not exist" && clean && exit 1
cat ${cfg} | grep ${tmpd}/testfile >/dev/null 2>&1
grep 'original' ${tmps}/dotfiles/${tmpd}/testfile
nb=`cat ${cfg} | grep ${tmpd}/testfile | wc -l`
[ "${nb}" != "1" ] && echo 'not 1 entry' && exit 1
[ "${nb}" != "1" ] && echo 'not 1 entry' && clean && exit 1
# re-import without changing
echo "[+] re-import without changes"
@@ -85,11 +90,11 @@ cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/testfile
cat ${cfg}
# test is only once
[ ! -e ${tmps}/dotfiles/${tmpd}/testfile ] && echo "does not exist" && exit 1
[ ! -e ${tmps}/dotfiles/${tmpd}/testfile ] && echo "does not exist" && clean && exit 1
cat ${cfg} | grep ${tmpd}/testfile >/dev/null 2>&1
grep 'original' ${tmps}/dotfiles/${tmpd}/testfile
nb=`cat ${cfg} | grep ${tmpd}/testfile | wc -l`
[ "${nb}" != "1" ] && echo 'two entries!' && exit 1
[ "${nb}" != "1" ] && echo 'two entries!' && clean && exit 1
# re-import with changes
echo "[+] re-import with changes"
@@ -98,11 +103,11 @@ cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/testfile
cat ${cfg}
# test is only once
[ ! -e ${tmps}/dotfiles/${tmpd}/testfile ] && echo "does not exist" && exit 1
[ ! -e ${tmps}/dotfiles/${tmpd}/testfile ] && echo "does not exist" && clean && exit 1
cat ${cfg} | grep ${tmpd}/testfile >/dev/null 2>&1
grep 'modified' ${tmps}/dotfiles/${tmpd}/testfile
nb=`cat ${cfg} | grep ${tmpd}/testfile | wc -l`
[ "${nb}" != "1" ] && echo 'two entries!' && exit 1
[ "${nb}" != "1" ] && echo 'two entries!' && clean && exit 1
# ###################################################
@@ -113,11 +118,11 @@ cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ~/.dotdrop.test
cat ${cfg}
# ensure exists and is not link
[ ! -e "${tmps}/dotfiles/dotdrop.test" ] && echo "does not exist" && exit 1
[ ! -e "${tmps}/dotfiles/dotdrop.test" ] && echo "does not exist" && clean && exit 1
cat ${cfg} | grep "~/.dotdrop.test" >/dev/null 2>&1
grep 'original' ${tmps}/dotfiles/dotdrop.test
nb=`cat ${cfg} | grep "~/.dotdrop.test" | wc -l`
[ "${nb}" != "1" ] && echo 'not 1 entry' && exit 1
[ "${nb}" != "1" ] && echo 'not 1 entry' && clean && exit 1
# re-import without changing
echo "[+] re-import without changes in home"
@@ -125,11 +130,11 @@ cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ~/.dotdrop.test
cat ${cfg}
# test is only once
[ ! -e "${tmps}/dotfiles/dotdrop.test" ] && echo "does not exist" && exit 1
[ ! -e "${tmps}/dotfiles/dotdrop.test" ] && echo "does not exist" && clean && exit 1
cat ${cfg} | grep "~/.dotdrop.test" >/dev/null 2>&1
grep 'original' ${tmps}/dotfiles/dotdrop.test
nb=`cat ${cfg} | grep "~/.dotdrop.test" | wc -l`
[ "${nb}" != "1" ] && echo 'two entries!' && exit 1
[ "${nb}" != "1" ] && echo 'two entries!' && clean && exit 1
# re-import with changes
echo "[+] re-import with changes in home"
@@ -138,14 +143,14 @@ cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ~/.dotdrop.test
cat ${cfg}
# test is only once
[ ! -e "${tmps}/dotfiles/dotdrop.test" ] && echo "does not exist" && exit 1
[ ! -e "${tmps}/dotfiles/dotdrop.test" ] && echo "does not exist" && clean && exit 1
cat ${cfg} | grep "~/.dotdrop.test" >/dev/null 2>&1
grep 'modified' ${tmps}/dotfiles/dotdrop.test
nb=`cat ${cfg} | grep "~/.dotdrop.test" | wc -l`
[ "${nb}" != "1" ] && echo 'two entries!' && exit 1
[ "${nb}" != "1" ] && echo 'two entries!' && clean && exit 1
## CLEANING
rm -rf ${tmps} ${tmpd} ~/.dotdrop-test
clean
echo "OK"
exit 0

View File

@@ -129,6 +129,7 @@ def _fake_args():
args['--show-patch'] = False
args['--force-actions'] = False
args['--grepable'] = False
args['--as'] = None
# cmds
args['profiles'] = False
args['files'] = False