mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-12 23:55:20 +00:00
remove un-managed for #403
This commit is contained in:
1
docs/usage.md
vendored
1
docs/usage.md
vendored
@@ -97,6 +97,7 @@ Some available options:
|
|||||||
* `-a`/`--force-actions`: Force the execution of actions even if the dotfiles are not installed (see [Fake dotfile and actions](config/config-actions.md#fake-dotfile-and-actions) as an alternative)
|
* `-a`/`--force-actions`: Force the execution of actions even if the dotfiles are not installed (see [Fake dotfile and actions](config/config-actions.md#fake-dotfile-and-actions) as an alternative)
|
||||||
* `-f`/`--force`: Do not ask for any confirmation
|
* `-f`/`--force`: Do not ask for any confirmation
|
||||||
* `-W`/`--workdir-clear`: Clear the `workdir` before installing dotfiles (see [the config entry](config/config-config.md) `clear_workdir`)
|
* `-W`/`--workdir-clear`: Clear the `workdir` before installing dotfiles (see [the config entry](config/config-config.md) `clear_workdir`)
|
||||||
|
* `-R`/`remove-existing`: Applies to directory dotfiles only (`nolink`) and will remove files not managed by dotdrop in the destination directory
|
||||||
|
|
||||||
To ignore specific patterns during installation, see [the ignore patterns](config/config-file.md#ignore-patterns).
|
To ignore specific patterns during installation, see [the ignore patterns](config/config-file.md#ignore-patterns).
|
||||||
|
|
||||||
|
|||||||
@@ -720,7 +720,8 @@ def _get_install_installer(opts, tmpdir=None):
|
|||||||
totemp=tmpdir,
|
totemp=tmpdir,
|
||||||
showdiff=opts.install_showdiff,
|
showdiff=opts.install_showdiff,
|
||||||
backup_suffix=opts.install_backup_suffix,
|
backup_suffix=opts.install_backup_suffix,
|
||||||
diff_cmd=opts.diff_command)
|
diff_cmd=opts.diff_command,
|
||||||
|
remove_existing_in_dir=opts.install_remove_existing)
|
||||||
return inst
|
return inst
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ class Installer:
|
|||||||
def __init__(self, base='.', create=True, backup=True,
|
def __init__(self, base='.', create=True, backup=True,
|
||||||
dry=False, safe=False, workdir='~/.config/dotdrop',
|
dry=False, safe=False, workdir='~/.config/dotdrop',
|
||||||
debug=False, diff=True, totemp=None, showdiff=False,
|
debug=False, diff=True, totemp=None, showdiff=False,
|
||||||
backup_suffix='.dotdropbak', diff_cmd=''):
|
backup_suffix='.dotdropbak', diff_cmd='',
|
||||||
|
remove_existing_in_dir=False):
|
||||||
"""
|
"""
|
||||||
@base: directory path where to search for templates
|
@base: directory path where to search for templates
|
||||||
@create: create directory hierarchy if missing when installing
|
@create: create directory hierarchy if missing when installing
|
||||||
@@ -42,6 +43,8 @@ class Installer:
|
|||||||
@showdiff: show the diff before overwriting (or asking for)
|
@showdiff: show the diff before overwriting (or asking for)
|
||||||
@backup_suffix: suffix for dotfile backup file
|
@backup_suffix: suffix for dotfile backup file
|
||||||
@diff_cmd: diff command to use
|
@diff_cmd: diff command to use
|
||||||
|
@remove_existing_in_dir: remove file in dir dotfiles
|
||||||
|
if not managed by dotdrop
|
||||||
"""
|
"""
|
||||||
self.create = create
|
self.create = create
|
||||||
self.backup = backup
|
self.backup = backup
|
||||||
@@ -60,6 +63,7 @@ class Installer:
|
|||||||
self.backup_suffix = backup_suffix
|
self.backup_suffix = backup_suffix
|
||||||
self.diff_cmd = diff_cmd
|
self.diff_cmd = diff_cmd
|
||||||
self.action_executed = False
|
self.action_executed = False
|
||||||
|
self.remove_existing_in_dir = remove_existing_in_dir
|
||||||
# avoids printing file copied logs
|
# avoids printing file copied logs
|
||||||
# when using install_to_tmp for comparing
|
# when using install_to_tmp for comparing
|
||||||
self.comparing = False
|
self.comparing = False
|
||||||
@@ -130,10 +134,12 @@ class Installer:
|
|||||||
if linktype == LinkTypes.NOLINK:
|
if linktype == LinkTypes.NOLINK:
|
||||||
# normal file
|
# normal file
|
||||||
if isdir:
|
if isdir:
|
||||||
ret, err = self._copy_dir(templater, src, dst,
|
ret, err, ins = self._copy_dir(templater, src, dst,
|
||||||
actionexec=actionexec,
|
actionexec=actionexec,
|
||||||
noempty=noempty, ignore=ignore,
|
noempty=noempty, ignore=ignore,
|
||||||
is_template=is_template)
|
is_template=is_template)
|
||||||
|
if self.remove_existing_in_dir and ins:
|
||||||
|
self._remove_existing_in_dir(dst, ins)
|
||||||
else:
|
else:
|
||||||
ret, err = self._copy_file(templater, src, dst,
|
ret, err = self._copy_file(templater, src, dst,
|
||||||
actionexec=actionexec,
|
actionexec=actionexec,
|
||||||
@@ -583,10 +589,15 @@ class Installer:
|
|||||||
- False, error_msg : error
|
- False, error_msg : error
|
||||||
- False, None : ignored
|
- False, None : ignored
|
||||||
- False, 'aborted' : user aborted
|
- False, 'aborted' : user aborted
|
||||||
|
|
||||||
|
third arg returned is the list managed dotfiles
|
||||||
|
in the destination or an empty list if anything
|
||||||
|
fails
|
||||||
"""
|
"""
|
||||||
self.log.dbg(f'deploy dir {src}')
|
self.log.dbg(f'deploy dir {src}')
|
||||||
# default to nothing installed and no error
|
# default to nothing installed and no error
|
||||||
ret = False, None
|
ret = False
|
||||||
|
dst_dotfiles = []
|
||||||
|
|
||||||
# handle all files in dir
|
# handle all files in dir
|
||||||
for entry in os.listdir(src):
|
for entry in os.listdir(src):
|
||||||
@@ -594,35 +605,75 @@ class Installer:
|
|||||||
self.log.dbg(f'deploy sub from {dst}: {entry}')
|
self.log.dbg(f'deploy sub from {dst}: {entry}')
|
||||||
if not os.path.isdir(fpath):
|
if not os.path.isdir(fpath):
|
||||||
# is file
|
# is file
|
||||||
|
fdst = os.path.join(dst, entry)
|
||||||
|
dst_dotfiles.append(fdst)
|
||||||
res, err = self._copy_file(templater, fpath,
|
res, err = self._copy_file(templater, fpath,
|
||||||
os.path.join(dst, entry),
|
fdst,
|
||||||
actionexec=actionexec,
|
actionexec=actionexec,
|
||||||
noempty=noempty,
|
noempty=noempty,
|
||||||
ignore=ignore,
|
ignore=ignore,
|
||||||
is_template=is_template)
|
is_template=is_template)
|
||||||
if not res and err:
|
if not res and err:
|
||||||
# error occured
|
# error occured
|
||||||
return res, err
|
return res, err, []
|
||||||
|
|
||||||
if res:
|
if res:
|
||||||
# something got installed
|
# something got installed
|
||||||
ret = True, None
|
|
||||||
|
ret = True
|
||||||
else:
|
else:
|
||||||
# is directory
|
# is directory
|
||||||
res, err = self._copy_dir(templater, fpath,
|
dpath = os.path.join(dst, entry)
|
||||||
os.path.join(dst, entry),
|
dst_dotfiles.append(dpath)
|
||||||
actionexec=actionexec,
|
res, err, subs = self._copy_dir(templater, fpath,
|
||||||
noempty=noempty,
|
dpath,
|
||||||
ignore=ignore,
|
actionexec=actionexec,
|
||||||
is_template=is_template)
|
noempty=noempty,
|
||||||
|
ignore=ignore,
|
||||||
|
is_template=is_template)
|
||||||
|
dst_dotfiles.extend(subs)
|
||||||
if not res and err:
|
if not res and err:
|
||||||
# error occured
|
# error occured
|
||||||
return res, err
|
return res, err, []
|
||||||
|
|
||||||
if res:
|
if res:
|
||||||
# something got installed
|
# something got installed
|
||||||
ret = True, None
|
ret = True
|
||||||
return ret
|
|
||||||
|
return ret, None, dst_dotfiles
|
||||||
|
|
||||||
|
def _is_path_in(self, path, paths):
|
||||||
|
"""return true if path is in paths"""
|
||||||
|
return any(samefile(path, p) for p in paths)
|
||||||
|
|
||||||
|
def _remove_existing_in_dir(self, directory, installed_files=None):
|
||||||
|
"""
|
||||||
|
with --remove-existing this will remove
|
||||||
|
any file in managed directory which
|
||||||
|
are not handled by dotdrop
|
||||||
|
"""
|
||||||
|
if not installed_files:
|
||||||
|
return
|
||||||
|
if not os.path.exists(directory) or not os.path.isdir(directory):
|
||||||
|
return
|
||||||
|
to_remove = []
|
||||||
|
for root, dirs, files in os.walk(directory):
|
||||||
|
for name in files:
|
||||||
|
path = os.path.join(root, name)
|
||||||
|
if self._is_path_in(path, installed_files):
|
||||||
|
continue
|
||||||
|
to_remove.append(os.path.abspath(path))
|
||||||
|
for name in dirs:
|
||||||
|
path = os.path.join(root, name)
|
||||||
|
if self._is_path_in(path, installed_files):
|
||||||
|
continue
|
||||||
|
to_remove.append(os.path.abspath(path))
|
||||||
|
for path in to_remove:
|
||||||
|
if self.dry:
|
||||||
|
self.log.dry(f'would remove {path}')
|
||||||
|
continue
|
||||||
|
self.log.dbg(f'removing not managed: {path}')
|
||||||
|
removepath(path, logger=self.log)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _write_content_to_file(cls, content, src, dst):
|
def _write_content_to_file(cls, content, src, dst):
|
||||||
|
|||||||
@@ -58,19 +58,19 @@ USAGE = f"""
|
|||||||
{BANNER}
|
{BANNER}
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
dotdrop install [-VbtfndDaW] [-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>...]
|
||||||
[--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>]
|
||||||
[-w <nb>] [-C <file>...] [-i <pattern>...]
|
[-w <nb>] [-C <file>...] [-i <pattern>...]
|
||||||
dotdrop update [-VbfdkPz] [-c <path>] [-p <profile>]
|
dotdrop update [-VbfdkPz] [-c <path>] [-p <profile>]
|
||||||
[-w <nb>] [-i <pattern>...] [<path>...]
|
[-w <nb>] [-i <pattern>...] [<path>...]
|
||||||
dotdrop remove [-Vbfdk] [-c <path>] [-p <profile>] [<path>...]
|
dotdrop remove [-Vbfdk] [-c <path>] [-p <profile>] [<path>...]
|
||||||
dotdrop files [-VbTG] [-c <path>] [-p <profile>]
|
dotdrop files [-VbTG] [-c <path>] [-p <profile>]
|
||||||
dotdrop detail [-Vb] [-c <path>] [-p <profile>] [<key>...]
|
dotdrop detail [-Vb] [-c <path>] [-p <profile>] [<key>...]
|
||||||
dotdrop profiles [-VbG] [-c <path>]
|
dotdrop profiles [-VbG] [-c <path>]
|
||||||
dotdrop --help
|
dotdrop --help
|
||||||
dotdrop --version
|
dotdrop --version
|
||||||
|
|
||||||
@@ -91,6 +91,7 @@ Options:
|
|||||||
-n --nodiff Do not diff when installing.
|
-n --nodiff Do not diff when installing.
|
||||||
-p --profile=<profile> Specify the profile to use [default: {PROFILE}].
|
-p --profile=<profile> Specify the profile to use [default: {PROFILE}].
|
||||||
-P --show-patch Provide a one-liner to manually patch template.
|
-P --show-patch Provide a one-liner to manually patch template.
|
||||||
|
-R --remove-existing Remove existing file on install directory.
|
||||||
-s --as=<path> Import as a different path from actual path.
|
-s --as=<path> Import as a different path from actual path.
|
||||||
--transr=<key> Associate trans_read key on import.
|
--transr=<key> Associate trans_read key on import.
|
||||||
--transw=<key> Apply trans_write key on import.
|
--transw=<key> Apply trans_write key on import.
|
||||||
@@ -297,6 +298,7 @@ class Options(AttrMonitor):
|
|||||||
self.install_force_chmod = self.force_chmod
|
self.install_force_chmod = self.force_chmod
|
||||||
self.install_clear_workdir = self.args['--workdir-clear'] or \
|
self.install_clear_workdir = self.args['--workdir-clear'] or \
|
||||||
self.clear_workdir
|
self.clear_workdir
|
||||||
|
self.install_remove_existing = self.args['--remove-existing']
|
||||||
|
|
||||||
def _apply_args_compare(self):
|
def _apply_args_compare(self):
|
||||||
"""compare specifics"""
|
"""compare specifics"""
|
||||||
|
|||||||
80
tests-ng/install-and-remove.sh
vendored
Executable file
80
tests-ng/install-and-remove.sh
vendored
Executable file
@@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# author: deadc0de6 (https://github.com/deadc0de6)
|
||||||
|
# Copyright (c) 2023, deadc0de6
|
||||||
|
#
|
||||||
|
# test install and remove existing file in fs
|
||||||
|
# returns 1 in case of error
|
||||||
|
#
|
||||||
|
|
||||||
|
## start-cookie
|
||||||
|
set -euo errtrace 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
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# dotdrop directory
|
||||||
|
basedir=$(mktemp -d --suffix='-dotdrop-tests' || mktemp -d)
|
||||||
|
mkdir -p "${basedir}"/dotfiles
|
||||||
|
echo "[+] dotdrop dir: ${basedir}"
|
||||||
|
echo "[+] dotpath dir: ${basedir}/dotfiles"
|
||||||
|
tmpd=$(mktemp -d --suffix='-dotdrop-tests' || mktemp -d)
|
||||||
|
|
||||||
|
clear_on_exit "${basedir}"
|
||||||
|
clear_on_exit "${tmpd}"
|
||||||
|
|
||||||
|
# create the config file
|
||||||
|
cfg="${basedir}/config.yaml"
|
||||||
|
cat > "${cfg}" << _EOF
|
||||||
|
config:
|
||||||
|
backup: true
|
||||||
|
create: true
|
||||||
|
dotpath: dotfiles
|
||||||
|
dotfiles:
|
||||||
|
d_dir:
|
||||||
|
src: dir
|
||||||
|
dst: ${tmpd}/dir
|
||||||
|
profiles:
|
||||||
|
p1:
|
||||||
|
dotfiles:
|
||||||
|
- d_dir
|
||||||
|
_EOF
|
||||||
|
|
||||||
|
# create the file in dotpath
|
||||||
|
mkdir -p "${basedir}"/dotfiles/dir
|
||||||
|
echo "content" > "${basedir}"/dotfiles/dir/file
|
||||||
|
|
||||||
|
# create the file in fs
|
||||||
|
mkdir -p "${tmpd}"/dir
|
||||||
|
echo "content" > "${tmpd}"/dir/existing
|
||||||
|
|
||||||
|
echo "[+] install"
|
||||||
|
cd "${ddpath}" | ${bin} install -c "${cfg}" -f -p p1 --verbose
|
||||||
|
[ "$?" != "0" ] && exit 1
|
||||||
|
|
||||||
|
[ ! -e "${tmpd}"/dir/file ] && echo "d_dir file not installed" && exit 1
|
||||||
|
[ ! -e "${tmpd}"/dir/existing ] && echo "existing removed" && exit 1
|
||||||
|
|
||||||
|
echo "[+] install with remove"
|
||||||
|
cd "${ddpath}" | ${bin} install --remove-existing -c "${cfg}" -f -p p1 --verbose
|
||||||
|
[ "$?" != "0" ] && exit 1
|
||||||
|
|
||||||
|
[ ! -e "${tmpd}"/dir/file ] && echo "d_dir file not installed" && exit 1
|
||||||
|
[ -e "${tmpd}"/dir/existing ] && echo "existing not removed" && exit 1
|
||||||
|
|
||||||
|
echo "OK"
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user