1
0
mirror of https://github.com/deadc0de6/dotdrop.git synced 2026-02-04 09:51:45 +00:00

add -K --dkey

This commit is contained in:
deadc0de6
2024-11-08 14:43:55 +01:00
committed by deadc0de
parent 452271bcae
commit 2ad7bb3009
6 changed files with 168 additions and 14 deletions

9
docs/usage.md vendored
View File

@@ -40,8 +40,13 @@ $ dotdrop import ~/.xinitrc
1 file(s) imported.
```
You can control how the dotfile key is generated in the config file
with the following [config entries](config/config-config.md):
You can explicitely provide the key dotdrop should use for the dotfile entry
in the config file with the `-K --dkey` cli switch. Note that the provided
string will be sanitized for yaml. Also if the key already exists,
it will be appended with `_<incremental_number>` to avoid duplicates.
If the key is not provided, it will be automatically created based on the
following [config entries](config/config-config.md):
* `longkey`
* `false` (default): take the shortest unique path

View File

@@ -9,6 +9,7 @@ import os
import shlex
import platform
import distro
import re
# local imports
@@ -24,6 +25,8 @@ from dotdrop.exceptions import UndefinedException, YamlException, \
TILD = '~'
YAML_OK = '[^0-9a-zA-Z.\-_]+'
YAML_REPL = '_'
class CfgAggregator:
@@ -69,15 +72,18 @@ class CfgAggregator:
return self.cfgyaml.del_dotfile_from_profile(dotfile.key, profile.key)
def new_dotfile(self, src, dst, link, chmod=None,
trans_install=None, trans_update=None):
trans_install=None, trans_update=None,
forcekey=None):
"""
import a new dotfile
@src: path in dotpath
@dst: path in FS
@link: LinkType
@chmod: file permission
@trans_install: read transformation
@trans_update: write transformation
@forcekey: dotfile key
"""
dst = self.path_to_dotfile_dst(dst)
dotfile = self.get_dotfile_by_src_dst(src, dst)
@@ -85,7 +91,8 @@ class CfgAggregator:
# add the dotfile
dotfile = self._create_new_dotfile(src, dst, link, chmod=chmod,
trans_install=trans_install,
trans_update=trans_update)
trans_update=trans_update,
forcekey=forcekey)
if not dotfile:
return False
@@ -237,10 +244,15 @@ class CfgAggregator:
########################################################
def _create_new_dotfile(self, src, dst, link, chmod=None,
trans_install=None, trans_update=None):
"""create a new dotfile"""
trans_install=None, trans_update=None,
forcekey=None):
"""
create a new dotfile
"""
# get a new dotfile with a unique key
key = self._get_new_dotfile_key(dst)
key = self._get_new_dotfile_key(dst, forcekey=forcekey)
if not key:
return None
self.log.dbg(f'new dotfile key: {key}')
# add the dotfile
trans_install_key = trans_update_key = None
@@ -282,6 +294,9 @@ class CfgAggregator:
self.key_prefix = self.settings.key_prefix
self.key_separator = self.settings.key_separator
# clean key separator
self.key_separator = re.sub(YAML_OK, YAML_REPL, self.key_separator)
# dotfiles
self.log.dbg('parsing dotfiles')
self.dotfiles = Dotfile.parse_dict(self.cfgyaml.dotfiles)
@@ -427,10 +442,17 @@ class CfgAggregator:
# dotfile key
########################################################
def _get_new_dotfile_key(self, dst):
def _get_new_dotfile_key(self, dst, forcekey=None):
"""return a new unique dotfile key"""
path = os.path.expanduser(dst)
existing_keys = self.cfgyaml.get_all_dotfile_keys()
# use provided key
if forcekey:
key = self._norm_key_elem(forcekey)
return self._uniq_key(key, existing_keys)
# key creation
path = os.path.expanduser(dst)
if self.settings.longkey:
return self._get_long_key(path, existing_keys)
return self._get_short_key(path, existing_keys)
@@ -440,6 +462,7 @@ class CfgAggregator:
"""normalize path element for sanity"""
elem = elem.lstrip('.')
elem = elem.replace(' ', '-')
elem = re.sub(YAML_OK, YAML_REPL, elem)
return elem.lower()
def _get_long_key(self, path, keys):

View File

@@ -537,7 +537,8 @@ def cmd_importer(opts):
dry=opts.dry, safe=opts.safe,
debug=opts.debug,
keepdot=opts.keepdot,
ignore=opts.import_ignore)
ignore=opts.import_ignore,
forcekey=opts.import_force_key)
for path in paths:
tmpret = importer.import_path(path,

View File

@@ -25,10 +25,10 @@ class Importer:
def __init__(self, profile, conf, dotpath, diff_cmd,
variables, dry=False, safe=True, debug=False,
keepdot=True, ignore=None):
keepdot=True, ignore=None, forcekey=None):
"""constructor
@profile: the selected profile
@conf: configuration manager
@conf: configuration manager (CfgAggregator)
@dotpath: dotfiles dotpath
@diff_cmd: diff command to use
@variables: dictionary of variables for the templates
@@ -37,6 +37,8 @@ class Importer:
@debug: enable debug
@keepdot: keep dot prefix
@ignore: patterns to ignore when importing
@forcekey: force the use of a specific dotfile key
This may raise UndefinedException
"""
self.profile = profile
@@ -51,6 +53,7 @@ class Importer:
self.debug = debug
self.keepdot = keepdot
self.ignore = []
self.forcekey = forcekey
self.log = Logger(debug=self.debug)
# patch ignore patterns
@@ -197,7 +200,8 @@ class Importer:
# add file to config file
retconf = self.conf.new_dotfile(src, dst, linktype, chmod=chmod,
trans_install=trans_install,
trans_update=trans_update)
trans_update=trans_update,
forcekey=self.forcekey)
if not retconf:
self.log.warn(f'\"{path}\" ignored during import')
return 0

View File

@@ -61,7 +61,8 @@ USAGE = f"""
Usage:
dotdrop install [-VbtfndDaWR] [-c <path>] [-p <profile>]
[-w <nb>] [<key>...]
dotdrop import [-Vbdfm] [-c <path>] [-p <profile>] [-i <pattern>...]
dotdrop import [-Vbdfm] [-c <path>] [-p <profile>]
[-i <pattern>...] [--dkey=<key>]
[--transr=<key>] [--transw=<key>]
[-l <link>] [-s <path>] <path>...
dotdrop compare [-LVbz] [-c <path>] [-p <profile>]
@@ -88,6 +89,7 @@ Options:
-G --grepable Grepable output.
-i --ignore=<pattern> Pattern to ignore.
-k --key Treat <path> as a dotfile key.
-K --dkey=<key> Set the dotfile key.
-l --link=<link> Link option (nolink|absolute|relative|link_children).
-L --file-only Do not show diff but only the files that differ.
-m --preserve-mode Insert a chmod entry in the dotfile with its mode.
@@ -337,6 +339,7 @@ class Options(AttrMonitor):
self.import_ignore = uniq_list(self.import_ignore)
self.import_trans_install = self.args['--transr']
self.import_trans_update = self.args['--transw']
self.import_force_key = self.args['--dkey']
def _apply_args_update(self):
"""update specifics"""

118
tests-ng/import-with-key.sh vendored Executable file
View File

@@ -0,0 +1,118 @@
#!/usr/bin/env bash
# author: deadc0de6 (https://github.com/deadc0de6)
# Copyright (c) 2024, deadc0de6
#
# test with user provided key
# --dkey
#
## start-cookie
set -eu -o errtrace -o pipefail
cur=$(cd "$(dirname "${0}")" && pwd)
ddpath="${cur}/../"
PPATH="{PYTHONPATH:-}"
export PYTHONPATH="${ddpath}:${PPATH}"
altbin="python3 -m dotdrop.dotdrop"
if hash coverage 2>/dev/null; then
mkdir -p coverages/
altbin="coverage run -p --data-file coverages/coverage --source=dotdrop -m dotdrop.dotdrop"
fi
bin="${DT_BIN:-${altbin}}"
# shellcheck source=tests-ng/helpers
source "${cur}"/helpers
echo -e "$(tput setaf 6)==> RUNNING $(basename "${BASH_SOURCE[0]}") <==$(tput sgr0)"
## end-cookie
################################################################
# 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
# create the config file
cfg="${tmps}/config.yaml"
cat > "${cfg}" << _EOF
config:
backup: true
create: true
dotpath: dotfiles
dotfiles:
profiles:
_EOF
#cat ${cfg}
# import
dkey="myfile1"
cd "${ddpath}" | ${bin} import -f -c "${cfg}" -p p1 -V --dkey "${dkey}" "${tmpd}"/file1
cat "${cfg}"
# test
[ ! -e "${tmps}"/dotfiles/"${tmpd}"/file1 ] && echo "not imported in dotpath" && exit 1
cat "${cfg}" | grep "${dkey}:" &>/dev/null || ( echo "bad key 1" && exit 1 )
# import 2 files
echo "firstfile" > "${tmpd}"/firstfile
echo "secondfile" > "${tmpd}"/secondfile
dkey="nfile"
cd "${ddpath}" | ${bin} import -f -c "${cfg}" -p p1 -V --dkey "${dkey}" "${tmpd}"/firstfile "${tmpd}"/secondfile
cat "${cfg}"
# test
[ ! -e "${tmps}"/dotfiles/"${tmpd}"/firstfile ] && echo "not imported in dotpath" && exit 1
[ ! -e "${tmps}"/dotfiles/"${tmpd}"/secondfile ] && echo "not imported in dotpath" && exit 1
cat "${cfg}" | grep "${dkey}:" &>/dev/null || ( echo "bad key 2a" && exit 1 )
cat "${cfg}" | grep "${dkey}_1:" &>/dev/null || ( echo "bad key 2b" && exit 1 )
# import 2 files with bad chars
echo "file-1.1" > "${tmpd}"/file-1.1
echo "file-2.2" > "${tmpd}"/file-2.2
dkey=".bad-key.1 2"
dkey_clean="bad-key.1-2"
cd "${ddpath}" | ${bin} import -f -c "${cfg}" -p p1 -V --dkey "${dkey}" "${tmpd}"/file-1.1 "${tmpd}"/file-2.2
cat "${cfg}"
# test
[ ! -e "${tmps}"/dotfiles/"${tmpd}"/file-1.1 ] && echo "not imported in dotpath" && exit 1
[ ! -e "${tmps}"/dotfiles/"${tmpd}"/file-2.2 ] && echo "not imported in dotpath" && exit 1
cat "${cfg}" | grep "${dkey_clean}:" &>/dev/null || ( echo "bad key 3a" && exit 1 )
cat "${cfg}" | grep "${dkey_clean}_1:" &>/dev/null || ( echo "bad key 3b" && exit 1 )
# re-import
echo "lastfile" > "${tmpd}"/lastfile
dkey="nfile"
cd "${ddpath}" | ${bin} import -f -c "${cfg}" -p p1 -V --dkey "${dkey}" "${tmpd}"/lastfile
cat "${cfg}"
# test
[ ! -e "${tmps}"/dotfiles/"${tmpd}"/lastfile ] && echo "not imported in dotpath" && exit 1
cat "${cfg}" | grep "${dkey}_2:" &>/dev/null || ( echo "bad key 4" && exit 1 )
# bad char
echo "firstfile" > "${tmpd}"/badchar
dkey=".key@#\$ˆ&*()abc0032"
dkey_clean="key_abc0032"
cd "${ddpath}" | ${bin} import -f -c "${cfg}" -p p1 -V --dkey "${dkey}" "${tmpd}"/badchar
cat "${cfg}"
# test
[ ! -e "${tmps}"/dotfiles/"${tmpd}"/badchar ] && echo "not imported in dotpath" && exit 1
cat "${cfg}" | grep "${dkey_clean}:" &>/dev/null || ( echo "bad key 5" && exit 1 )
echo "OK"
exit 0