Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions dotdrop/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,21 @@ def _handle_file(self, deployed_path, local_path,
return False
return True

def _prune_nested_paths(self, entries):
"""return only top-most paths to avoid redundant removals"""
pruned = []
kept = set()
normalized = [entry.rstrip(os.path.sep) for entry in entries]
for entry in sorted(normalized, key=lambda x: x.count(os.path.sep)):
if any(
entry == k or entry.startswith(k + os.path.sep)
for k in kept
):
continue
kept.add(entry)
pruned.append(entry)
return pruned

def _handle_dir(self, deployed_path, local_path,
dotfile, ignores):
"""sync path (local dir) and local_path (dotdrop dir path)"""
Expand All @@ -277,6 +292,7 @@ def _handle_dir(self, deployed_path, local_path,
lonly, ronly, common = local_tree.compare(deploy_tree)

# those only in dotpath
lonly = self._prune_nested_paths(lonly)
for i in lonly:
path = os.path.join(local_path, i)
if self.dry:
Expand Down
81 changes: 81 additions & 0 deletions tests-ng/update-prune-nested-remove.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
# author: deadc0de6 (https://github.com/deadc0de6)
# Copyright (c) 2026, deadc0de6
#
# ensure update prunes nested removals
# returns 1 in case of error
#

## 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
################################################################

# dotdrop directory
basedir=$(mktemp -d --suffix='-dotdrop-tests' || mktemp -d)
dotpath="${basedir}/dotfiles"

# deployed directory
deployed=$(mktemp -d --suffix='-dotdrop-fs' || mktemp -d)
rel_deployed="${deployed#/}"

# misc files to cleanup
outlog=$(mktemp -t dotdrop-update-XXXX || mktemp)

clear_on_exit "${basedir}"
clear_on_exit "${deployed}"
clear_on_exit "${outlog}"

# create the config file
cfg="${basedir}/config.yaml"
create_conf "${cfg}"

# create deployed content
mkdir -p "${deployed}/sub"
echo 'config' > "${deployed}/config.yaml"
echo 'style' > "${deployed}/style.css"
echo 'test' > "${deployed}/sub/test"

# import the directory
cd "${ddpath}" | ${bin} import -f -c "${cfg}" "${deployed}"

# remove the nested directory on the filesystem
rm -rf "${deployed}/sub"

# update dotpath and capture output
cd "${ddpath}" | ${bin} update -f --verbose -c "${cfg}" "${deployed}" | tee "${outlog}"

# ensure sub removed from dotpath
[ -d "${dotpath}/${rel_deployed}/sub" ] && echo "sub directory not removed from dotpath" && exit 1
[ -e "${dotpath}/${rel_deployed}/sub/test" ] && echo "nested file still present in dotpath" && exit 1

# ensure only top-level removal is logged once
count_sub=$( (grep -F "\"${dotpath}/${rel_deployed}/sub\" removed" "${outlog}" || true) | wc -l | tr -d ' ')
count_sub_slash=$( (grep -F "\"${dotpath}/${rel_deployed}/sub/\" removed" "${outlog}" || true) | wc -l | tr -d ' ')
count_nested=$( (grep -F "\"${dotpath}/${rel_deployed}/sub/test\" removed" "${outlog}" || true) | wc -l | tr -d ' ')
count_nested_slash=$( (grep -F "\"${dotpath}/${rel_deployed}/sub/test/\" removed" "${outlog}" || true) | wc -l | tr -d ' ')

total_sub=$((count_sub + count_sub_slash))
[ "${total_sub}" -ne 1 ] && echo "expected single removal of sub directory, got ${total_sub}" && exit 1

total_nested=$((count_nested + count_nested_slash))
[ "${total_nested}" -ne 0 ] && echo "unexpected nested removal logged" && exit 1

echo "OK"
exit 0
Loading