From 7974939e688eb4b8cf64ddbb128468fcbcd3f790 Mon Sep 17 00:00:00 2001 From: deadc0de6 Date: Sat, 3 Sep 2022 08:44:47 +0200 Subject: [PATCH] add ability to import to no profile with ALL --- docs/usage.md | 3 + dotdrop/cfg_aggregator.py | 39 +++++---- dotdrop/cfg_yaml.py | 18 +++++ tests-ng/duplicate-key.sh | 2 +- tests-ng/import-to-no-profile.sh | 133 +++++++++++++++++++++++++++++++ 5 files changed, 180 insertions(+), 15 deletions(-) create mode 100755 tests-ng/import-to-no-profile.sh diff --git a/docs/usage.md b/docs/usage.md index b5383a4..1ef0d1d 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -69,6 +69,9 @@ dotfile management. $ dotdrop import ~/.zshrc --as=~/.zshrc.test ``` +By importing a path using the profile special keyword `ALL`, a dotfile will be created +in the config but won't be linked to any profile. + To ignore specific patterns during import, see [the ignore patterns](config-file.md#ignore-patterns). For more options, see the usage with `dotdrop --help`. diff --git a/dotdrop/cfg_aggregator.py b/dotdrop/cfg_aggregator.py index 6b546ed..381a388 100644 --- a/dotdrop/cfg_aggregator.py +++ b/dotdrop/cfg_aggregator.py @@ -88,13 +88,15 @@ class CfgAggregator: if not dotfile: return False + ret = dotfile is not None - # add to profile - key = dotfile.key - ret = self.cfgyaml.add_dotfile_to_profile(key, self.profile_key) - if ret: - msg = f'new dotfile {key} to profile {self.profile_key}' - self.log.dbg(msg) + if self.profile_key != self.cfgyaml.key_all: + # add to profile + key = dotfile.key + ret = self.cfgyaml.add_dotfile_to_profile(key, self.profile_key) + if ret: + msg = f'new dotfile {key} to profile {self.profile_key}' + self.log.dbg(msg) # save the config and reload it if ret: @@ -141,15 +143,18 @@ class CfgAggregator: @src: dotfile src (in dotpath) @dst: dotfile dst (on filesystem) """ - try: - src = self.cfgyaml.resolve_dotfile_src(src) - except UndefinedException as exc: - err = f'unable to resolve {src}: {exc}' - self.log.err(err) - return None + if not os.path.isabs(src): + # ensures we have an absolute path + try: + src = self.cfgyaml.resolve_dotfile_src(src) + except UndefinedException as exc: + err = f'unable to resolve {src}: {exc}' + self.log.err(err) + return None dotfiles = self.get_dotfile_by_dst(dst) for dotfile in dotfiles: - if dotfile.src == src: + dsrc = self.cfgyaml.resolve_dotfile_src(dotfile.src) + if dsrc == src: return dotfile return None @@ -196,7 +201,13 @@ class CfgAggregator: return res def get_dotfiles(self, profile_key=None): - """get all dotfiles for this profile or specified profile key""" + """ + get all dotfiles for the current profile if None + or the specified profile_key if defined + or all dotfiles if profile_key is ALL + """ + if profile_key == self.cfgyaml.key_all: + return self.dotfiles dotfiles = [] profile = self.get_profile(key=profile_key) if not profile: diff --git a/dotdrop/cfg_yaml.py b/dotdrop/cfg_yaml.py index 1b32e21..8eed556 100644 --- a/dotdrop/cfg_yaml.py +++ b/dotdrop/cfg_yaml.py @@ -351,6 +351,8 @@ class CfgYaml: """ # create the profile if it doesn't exist self._new_profile(profile_key) + if profile_key not in self.profiles: + return False profile = self.profiles[profile_key] # ensure profile dotfiles list is not None @@ -724,6 +726,14 @@ class CfgYaml: return profiles new = {} for k, val in profiles.items(): + if k == self.key_all: + msg = f'\"{self.key_all}\" is a special profile name, ' + msg += 'consider renaming to avoid any issue.' + self._log.warn(msg) + if not k: + msg = 'empty profile name' + self._log.warn(msg) + continue if not val: # no dotfiles continue @@ -1097,6 +1107,14 @@ class CfgYaml: def _new_profile(self, key): """add a new profile if it doesn't exist""" + if key == self.key_all: + err = f'profile key \"{key}\" is reserved' + self._log.warn(err) + raise YamlException(err) + if not key: + err = 'empty profile key' + self._log.warn(err) + raise YamlException(err) if key not in self.profiles.keys(): # update yaml_dict self._yaml_dict[self.key_profiles][key] = { diff --git a/tests-ng/duplicate-key.sh b/tests-ng/duplicate-key.sh index 59b0a87..f40f798 100755 --- a/tests-ng/duplicate-key.sh +++ b/tests-ng/duplicate-key.sh @@ -99,7 +99,7 @@ cd ${ddpath} | ${bin} import -f --verbose -c ${cfg} -p p2 \ # count dotfiles for p2 cnt=`cd ${ddpath} | ${bin} files --verbose -c ${cfg} -p p2 -b | grep '^f_' | wc -l` -[ "${cnt}" != "4" ] && exit 1 +[ "${cnt}" != "4" ] && echo "bad count for p2: ${cnt} != 4" && exit 1 echo "OK" exit 0 diff --git a/tests-ng/import-to-no-profile.sh b/tests-ng/import-to-no-profile.sh new file mode 100755 index 0000000..1dbc3fa --- /dev/null +++ b/tests-ng/import-to-no-profile.sh @@ -0,0 +1,133 @@ +#!/usr/bin/env bash +# author: deadc0de6 (https://github.com/deadc0de6) +# Copyright (c) 2022, deadc0de6 +# +# test import to no profile (using ALL keyword) +# + +# 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" +hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true + +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 +################################################################ + +# 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}" + +clear_on_exit "${tmps}" +clear_on_exit "${tmpd}" + +# create the dotfile +echo "file1" > ${tmpd}/file1 +echo "file2" > ${tmpd}/file2 + +# create the config file +cfg="${tmps}/config.yaml" + +cat > ${cfg} << _EOF +config: + backup: true + create: true + dotpath: dotfiles +dotfiles: +profiles: +_EOF +#cat ${cfg} + +noprofile="ALL" + +################################## +# import with profile from arg +cd ${ddpath} | ${bin} import -f -c ${cfg} -p "${noprofile}" -V ${tmpd}/file1 +cat ${cfg} + +# ensure exists and is not link +[ ! -e ${tmps}/dotfiles/${tmpd}/file1 ] && echo "file not imported" && exit 1 +# ensure present in config +cat ${cfg} | grep ${tmpd}/file1 >/dev/null 2>&1 + +nb=`cat ${cfg} | grep f_file1 | wc -l` +[ "${nb}" != "1" ] && echo 'bad config' && exit 1 + +cntpre=`find ${tmps}/dotfiles -type f | wc -l` + +# reimport +set +e +cd ${ddpath} | ${bin} import -f -c ${cfg} -p "${noprofile}" -V ${tmpd}/file1 +set -e +cat ${cfg} + +cntpost=`find ${tmps}/dotfiles -type f | wc -l` +[ "${cntpost}" != "${cntpre}" ] && echo "imported twice" && exit 1 + +nb=`cat ${cfg} | grep "dst: ${tmpd}/file1" | wc -l` +[ "${nb}" != "1" ] && echo 'imported twice in config' && exit 1 + +################################## +# import with profile from env +export DOTDROP_PROFILE="${noprofile}" +cd ${ddpath} | ${bin} import -f -c ${cfg} -V ${tmpd}/file2 +cat ${cfg} + +# ensure exists and is not link +[ ! -e ${tmps}/dotfiles/${tmpd}/file2 ] && echo "file not imported" && exit 1 +# ensure present in config +cat ${cfg} | grep ${tmpd}/file2 >/dev/null 2>&1 + +nb=`cat ${cfg} | grep f_file2 | wc -l` +[ "${nb}" != "1" ] && echo 'bad config' && exit 1 + +cntpre=`find ${tmps}/dotfiles -type f | wc -l` + +# reimport +set +e +cd ${ddpath} | ${bin} import -f -c ${cfg} -V ${tmpd}/file2 +set -e +cat ${cfg} + +cntpost=`find ${tmps}/dotfiles -type f | wc -l` +[ "${cntpost}" != "${cntpre}" ] && echo "imported twice" && exit 1 + +nb=`cat ${cfg} | grep "dst: ${tmpd}/file2" | wc -l` +[ "${nb}" != "1" ] && echo 'imported twice in config' && exit 1 + +echo "OK" +exit 0