mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-04 11:36: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.
|
1 file(s) imported.
|
||||||
```
|
```
|
||||||
|
|
||||||
You can control how the dotfile key is generated in the config file
|
You can explicitely provide the key dotdrop should use for the dotfile entry
|
||||||
with the following [config entries](config/config-config.md):
|
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`
|
* `longkey`
|
||||||
* `false` (default): take the shortest unique path
|
* `false` (default): take the shortest unique path
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import os
|
|||||||
import shlex
|
import shlex
|
||||||
import platform
|
import platform
|
||||||
import distro
|
import distro
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
@@ -24,6 +25,8 @@ from dotdrop.exceptions import UndefinedException, YamlException, \
|
|||||||
|
|
||||||
|
|
||||||
TILD = '~'
|
TILD = '~'
|
||||||
|
YAML_OK = '[^0-9a-zA-Z.\-_]+'
|
||||||
|
YAML_REPL = '_'
|
||||||
|
|
||||||
|
|
||||||
class CfgAggregator:
|
class CfgAggregator:
|
||||||
@@ -69,15 +72,18 @@ class CfgAggregator:
|
|||||||
return self.cfgyaml.del_dotfile_from_profile(dotfile.key, profile.key)
|
return self.cfgyaml.del_dotfile_from_profile(dotfile.key, profile.key)
|
||||||
|
|
||||||
def new_dotfile(self, src, dst, link, chmod=None,
|
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
|
import a new dotfile
|
||||||
|
|
||||||
@src: path in dotpath
|
@src: path in dotpath
|
||||||
@dst: path in FS
|
@dst: path in FS
|
||||||
@link: LinkType
|
@link: LinkType
|
||||||
@chmod: file permission
|
@chmod: file permission
|
||||||
@trans_install: read transformation
|
@trans_install: read transformation
|
||||||
@trans_update: write transformation
|
@trans_update: write transformation
|
||||||
|
@forcekey: dotfile key
|
||||||
"""
|
"""
|
||||||
dst = self.path_to_dotfile_dst(dst)
|
dst = self.path_to_dotfile_dst(dst)
|
||||||
dotfile = self.get_dotfile_by_src_dst(src, dst)
|
dotfile = self.get_dotfile_by_src_dst(src, dst)
|
||||||
@@ -85,7 +91,8 @@ class CfgAggregator:
|
|||||||
# add the dotfile
|
# add the dotfile
|
||||||
dotfile = self._create_new_dotfile(src, dst, link, chmod=chmod,
|
dotfile = self._create_new_dotfile(src, dst, link, chmod=chmod,
|
||||||
trans_install=trans_install,
|
trans_install=trans_install,
|
||||||
trans_update=trans_update)
|
trans_update=trans_update,
|
||||||
|
forcekey=forcekey)
|
||||||
|
|
||||||
if not dotfile:
|
if not dotfile:
|
||||||
return False
|
return False
|
||||||
@@ -237,10 +244,15 @@ class CfgAggregator:
|
|||||||
########################################################
|
########################################################
|
||||||
|
|
||||||
def _create_new_dotfile(self, src, dst, link, chmod=None,
|
def _create_new_dotfile(self, src, dst, link, chmod=None,
|
||||||
trans_install=None, trans_update=None):
|
trans_install=None, trans_update=None,
|
||||||
"""create a new dotfile"""
|
forcekey=None):
|
||||||
|
"""
|
||||||
|
create a new 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, forcekey=forcekey)
|
||||||
|
if not key:
|
||||||
|
return None
|
||||||
self.log.dbg(f'new dotfile key: {key}')
|
self.log.dbg(f'new dotfile key: {key}')
|
||||||
# add the dotfile
|
# add the dotfile
|
||||||
trans_install_key = trans_update_key = None
|
trans_install_key = trans_update_key = None
|
||||||
@@ -282,6 +294,9 @@ class CfgAggregator:
|
|||||||
self.key_prefix = self.settings.key_prefix
|
self.key_prefix = self.settings.key_prefix
|
||||||
self.key_separator = self.settings.key_separator
|
self.key_separator = self.settings.key_separator
|
||||||
|
|
||||||
|
# clean key separator
|
||||||
|
self.key_separator = re.sub(YAML_OK, YAML_REPL, self.key_separator)
|
||||||
|
|
||||||
# dotfiles
|
# dotfiles
|
||||||
self.log.dbg('parsing dotfiles')
|
self.log.dbg('parsing dotfiles')
|
||||||
self.dotfiles = Dotfile.parse_dict(self.cfgyaml.dotfiles)
|
self.dotfiles = Dotfile.parse_dict(self.cfgyaml.dotfiles)
|
||||||
@@ -427,10 +442,17 @@ class CfgAggregator:
|
|||||||
# dotfile key
|
# dotfile key
|
||||||
########################################################
|
########################################################
|
||||||
|
|
||||||
def _get_new_dotfile_key(self, dst):
|
def _get_new_dotfile_key(self, dst, forcekey=None):
|
||||||
"""return a new unique dotfile key"""
|
"""return a new unique dotfile key"""
|
||||||
path = os.path.expanduser(dst)
|
|
||||||
existing_keys = self.cfgyaml.get_all_dotfile_keys()
|
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:
|
if self.settings.longkey:
|
||||||
return self._get_long_key(path, existing_keys)
|
return self._get_long_key(path, existing_keys)
|
||||||
return self._get_short_key(path, existing_keys)
|
return self._get_short_key(path, existing_keys)
|
||||||
@@ -440,6 +462,7 @@ class CfgAggregator:
|
|||||||
"""normalize path element for sanity"""
|
"""normalize path element for sanity"""
|
||||||
elem = elem.lstrip('.')
|
elem = elem.lstrip('.')
|
||||||
elem = elem.replace(' ', '-')
|
elem = elem.replace(' ', '-')
|
||||||
|
elem = re.sub(YAML_OK, YAML_REPL, elem)
|
||||||
return elem.lower()
|
return elem.lower()
|
||||||
|
|
||||||
def _get_long_key(self, path, keys):
|
def _get_long_key(self, path, keys):
|
||||||
|
|||||||
@@ -537,7 +537,8 @@ def cmd_importer(opts):
|
|||||||
dry=opts.dry, safe=opts.safe,
|
dry=opts.dry, safe=opts.safe,
|
||||||
debug=opts.debug,
|
debug=opts.debug,
|
||||||
keepdot=opts.keepdot,
|
keepdot=opts.keepdot,
|
||||||
ignore=opts.import_ignore)
|
ignore=opts.import_ignore,
|
||||||
|
forcekey=opts.import_force_key)
|
||||||
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
tmpret = importer.import_path(path,
|
tmpret = importer.import_path(path,
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ class Importer:
|
|||||||
|
|
||||||
def __init__(self, profile, conf, dotpath, diff_cmd,
|
def __init__(self, profile, conf, dotpath, diff_cmd,
|
||||||
variables, dry=False, safe=True, debug=False,
|
variables, dry=False, safe=True, debug=False,
|
||||||
keepdot=True, ignore=None):
|
keepdot=True, ignore=None, forcekey=None):
|
||||||
"""constructor
|
"""constructor
|
||||||
@profile: the selected profile
|
@profile: the selected profile
|
||||||
@conf: configuration manager
|
@conf: configuration manager (CfgAggregator)
|
||||||
@dotpath: dotfiles dotpath
|
@dotpath: dotfiles dotpath
|
||||||
@diff_cmd: diff command to use
|
@diff_cmd: diff command to use
|
||||||
@variables: dictionary of variables for the templates
|
@variables: dictionary of variables for the templates
|
||||||
@@ -37,6 +37,8 @@ class Importer:
|
|||||||
@debug: enable debug
|
@debug: enable debug
|
||||||
@keepdot: keep dot prefix
|
@keepdot: keep dot prefix
|
||||||
@ignore: patterns to ignore when importing
|
@ignore: patterns to ignore when importing
|
||||||
|
@forcekey: force the use of a specific dotfile key
|
||||||
|
|
||||||
This may raise UndefinedException
|
This may raise UndefinedException
|
||||||
"""
|
"""
|
||||||
self.profile = profile
|
self.profile = profile
|
||||||
@@ -51,6 +53,7 @@ class Importer:
|
|||||||
self.debug = debug
|
self.debug = debug
|
||||||
self.keepdot = keepdot
|
self.keepdot = keepdot
|
||||||
self.ignore = []
|
self.ignore = []
|
||||||
|
self.forcekey = forcekey
|
||||||
self.log = Logger(debug=self.debug)
|
self.log = Logger(debug=self.debug)
|
||||||
|
|
||||||
# patch ignore patterns
|
# patch ignore patterns
|
||||||
@@ -197,7 +200,8 @@ class Importer:
|
|||||||
# add file to config file
|
# add file to config file
|
||||||
retconf = self.conf.new_dotfile(src, dst, linktype, chmod=chmod,
|
retconf = self.conf.new_dotfile(src, dst, linktype, chmod=chmod,
|
||||||
trans_install=trans_install,
|
trans_install=trans_install,
|
||||||
trans_update=trans_update)
|
trans_update=trans_update,
|
||||||
|
forcekey=self.forcekey)
|
||||||
if not retconf:
|
if not retconf:
|
||||||
self.log.warn(f'\"{path}\" ignored during import')
|
self.log.warn(f'\"{path}\" ignored during import')
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ USAGE = f"""
|
|||||||
Usage:
|
Usage:
|
||||||
dotdrop install [-VbtfndDaWR] [-c <path>] [-p <profile>]
|
dotdrop install [-VbtfndDaWR] [-c <path>] [-p <profile>]
|
||||||
[-w <nb>] [<key>...]
|
[-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>]
|
[--transr=<key>] [--transw=<key>]
|
||||||
[-l <link>] [-s <path>] <path>...
|
[-l <link>] [-s <path>] <path>...
|
||||||
dotdrop compare [-LVbz] [-c <path>] [-p <profile>]
|
dotdrop compare [-LVbz] [-c <path>] [-p <profile>]
|
||||||
@@ -88,6 +89,7 @@ Options:
|
|||||||
-G --grepable Grepable output.
|
-G --grepable Grepable output.
|
||||||
-i --ignore=<pattern> Pattern to ignore.
|
-i --ignore=<pattern> Pattern to ignore.
|
||||||
-k --key Treat <path> as a dotfile key.
|
-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 --link=<link> Link option (nolink|absolute|relative|link_children).
|
||||||
-L --file-only Do not show diff but only the files that differ.
|
-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.
|
-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_ignore = uniq_list(self.import_ignore)
|
||||||
self.import_trans_install = self.args['--transr']
|
self.import_trans_install = self.args['--transr']
|
||||||
self.import_trans_update = self.args['--transw']
|
self.import_trans_update = self.args['--transw']
|
||||||
|
self.import_force_key = self.args['--dkey']
|
||||||
|
|
||||||
def _apply_args_update(self):
|
def _apply_args_update(self):
|
||||||
"""update specifics"""
|
"""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