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:
9
docs/usage.md
vendored
9
docs/usage.md
vendored
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
118
tests-ng/import-with-key.sh
vendored
Executable 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
|
||||
Reference in New Issue
Block a user