mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-15 09:55:06 +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)
|
||||
* `-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`)
|
||||
* `-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).
|
||||
|
||||
|
||||
@@ -720,7 +720,8 @@ def _get_install_installer(opts, tmpdir=None):
|
||||
totemp=tmpdir,
|
||||
showdiff=opts.install_showdiff,
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@ class Installer:
|
||||
def __init__(self, base='.', create=True, backup=True,
|
||||
dry=False, safe=False, workdir='~/.config/dotdrop',
|
||||
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
|
||||
@create: create directory hierarchy if missing when installing
|
||||
@@ -42,6 +43,8 @@ class Installer:
|
||||
@showdiff: show the diff before overwriting (or asking for)
|
||||
@backup_suffix: suffix for dotfile backup file
|
||||
@diff_cmd: diff command to use
|
||||
@remove_existing_in_dir: remove file in dir dotfiles
|
||||
if not managed by dotdrop
|
||||
"""
|
||||
self.create = create
|
||||
self.backup = backup
|
||||
@@ -60,6 +63,7 @@ class Installer:
|
||||
self.backup_suffix = backup_suffix
|
||||
self.diff_cmd = diff_cmd
|
||||
self.action_executed = False
|
||||
self.remove_existing_in_dir = remove_existing_in_dir
|
||||
# avoids printing file copied logs
|
||||
# when using install_to_tmp for comparing
|
||||
self.comparing = False
|
||||
@@ -130,10 +134,12 @@ class Installer:
|
||||
if linktype == LinkTypes.NOLINK:
|
||||
# normal file
|
||||
if isdir:
|
||||
ret, err = self._copy_dir(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
noempty=noempty, ignore=ignore,
|
||||
is_template=is_template)
|
||||
ret, err, ins = self._copy_dir(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
noempty=noempty, ignore=ignore,
|
||||
is_template=is_template)
|
||||
if self.remove_existing_in_dir and ins:
|
||||
self._remove_existing_in_dir(dst, ins)
|
||||
else:
|
||||
ret, err = self._copy_file(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
@@ -583,10 +589,15 @@ class Installer:
|
||||
- False, error_msg : error
|
||||
- False, None : ignored
|
||||
- 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}')
|
||||
# default to nothing installed and no error
|
||||
ret = False, None
|
||||
ret = False
|
||||
dst_dotfiles = []
|
||||
|
||||
# handle all files in dir
|
||||
for entry in os.listdir(src):
|
||||
@@ -594,35 +605,75 @@ class Installer:
|
||||
self.log.dbg(f'deploy sub from {dst}: {entry}')
|
||||
if not os.path.isdir(fpath):
|
||||
# is file
|
||||
fdst = os.path.join(dst, entry)
|
||||
dst_dotfiles.append(fdst)
|
||||
res, err = self._copy_file(templater, fpath,
|
||||
os.path.join(dst, entry),
|
||||
fdst,
|
||||
actionexec=actionexec,
|
||||
noempty=noempty,
|
||||
ignore=ignore,
|
||||
is_template=is_template)
|
||||
if not res and err:
|
||||
# error occured
|
||||
return res, err
|
||||
return res, err, []
|
||||
|
||||
if res:
|
||||
# something got installed
|
||||
ret = True, None
|
||||
|
||||
ret = True
|
||||
else:
|
||||
# is directory
|
||||
res, err = self._copy_dir(templater, fpath,
|
||||
os.path.join(dst, entry),
|
||||
actionexec=actionexec,
|
||||
noempty=noempty,
|
||||
ignore=ignore,
|
||||
is_template=is_template)
|
||||
dpath = os.path.join(dst, entry)
|
||||
dst_dotfiles.append(dpath)
|
||||
res, err, subs = self._copy_dir(templater, fpath,
|
||||
dpath,
|
||||
actionexec=actionexec,
|
||||
noempty=noempty,
|
||||
ignore=ignore,
|
||||
is_template=is_template)
|
||||
dst_dotfiles.extend(subs)
|
||||
if not res and err:
|
||||
# error occured
|
||||
return res, err
|
||||
return res, err, []
|
||||
|
||||
if res:
|
||||
# something got installed
|
||||
ret = True, None
|
||||
return ret
|
||||
ret = True
|
||||
|
||||
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
|
||||
def _write_content_to_file(cls, content, src, dst):
|
||||
|
||||
@@ -58,19 +58,19 @@ USAGE = f"""
|
||||
{BANNER}
|
||||
|
||||
Usage:
|
||||
dotdrop install [-VbtfndDaW] [-c <path>] [-p <profile>]
|
||||
[-w <nb>] [<key>...]
|
||||
dotdrop import [-Vbdfm] [-c <path>] [-p <profile>] [-i <pattern>...]
|
||||
[--transr=<key>] [--transw=<key>]
|
||||
[-l <link>] [-s <path>] <path>...
|
||||
dotdrop compare [-LVbz] [-c <path>] [-p <profile>]
|
||||
[-w <nb>] [-C <file>...] [-i <pattern>...]
|
||||
dotdrop update [-VbfdkPz] [-c <path>] [-p <profile>]
|
||||
[-w <nb>] [-i <pattern>...] [<path>...]
|
||||
dotdrop remove [-Vbfdk] [-c <path>] [-p <profile>] [<path>...]
|
||||
dotdrop files [-VbTG] [-c <path>] [-p <profile>]
|
||||
dotdrop detail [-Vb] [-c <path>] [-p <profile>] [<key>...]
|
||||
dotdrop profiles [-VbG] [-c <path>]
|
||||
dotdrop install [-VbtfndDaWR] [-c <path>] [-p <profile>]
|
||||
[-w <nb>] [<key>...]
|
||||
dotdrop import [-Vbdfm] [-c <path>] [-p <profile>] [-i <pattern>...]
|
||||
[--transr=<key>] [--transw=<key>]
|
||||
[-l <link>] [-s <path>] <path>...
|
||||
dotdrop compare [-LVbz] [-c <path>] [-p <profile>]
|
||||
[-w <nb>] [-C <file>...] [-i <pattern>...]
|
||||
dotdrop update [-VbfdkPz] [-c <path>] [-p <profile>]
|
||||
[-w <nb>] [-i <pattern>...] [<path>...]
|
||||
dotdrop remove [-Vbfdk] [-c <path>] [-p <profile>] [<path>...]
|
||||
dotdrop files [-VbTG] [-c <path>] [-p <profile>]
|
||||
dotdrop detail [-Vb] [-c <path>] [-p <profile>] [<key>...]
|
||||
dotdrop profiles [-VbG] [-c <path>]
|
||||
dotdrop --help
|
||||
dotdrop --version
|
||||
|
||||
@@ -91,6 +91,7 @@ Options:
|
||||
-n --nodiff Do not diff when installing.
|
||||
-p --profile=<profile> Specify the profile to use [default: {PROFILE}].
|
||||
-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.
|
||||
--transr=<key> Associate trans_read 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_clear_workdir = self.args['--workdir-clear'] or \
|
||||
self.clear_workdir
|
||||
self.install_remove_existing = self.args['--remove-existing']
|
||||
|
||||
def _apply_args_compare(self):
|
||||
"""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