Add option to clean recursively

Anish Athalye 4 years ago
parent 81f0d74955
commit e38e021ab3
  1. 8
  2. 11
  3. 34

@ -303,7 +303,9 @@ Clean commands are specified as an array of directories to be cleaned.
Clean commands support an extended configuration syntax. In this type of
configuration, commands are specified as directory paths mapping to options. If
the `force` option is set to `true`, dead links are removed even if they don't
point to a file inside the dotfiles directory.
point to a file inside the dotfiles directory. If `recursive` is set to `true`,
the directory is traversed recursively (not recommended for `~` because it will
be slow).
#### Example
@ -311,8 +313,10 @@ point to a file inside the dotfiles directory.
- clean: ['~']
- clean:
force: true
recursive: true
### Defaults

@ -20,16 +20,18 @@ class Clean(dotbot.Plugin):
defaults = self._context.defaults().get(self._directive, {})
for target in targets:
force = defaults.get('force', False)
recursive = defaults.get('recursive', False)
if isinstance(targets, dict) and isinstance(targets[target], dict):
force = targets[target].get('force', force)
success &= self._clean(target, force)
recursive = targets[target].get('recursive', recursive)
success &= self._clean(target, force, recursive)
if success:'All targets have been cleaned')
self._log.error('Some targets were not successfully cleaned')
return success
def _clean(self, target, force):
def _clean(self, target, force, recursive):
Cleans all the broken symbolic links in target if they point to
a subdirectory of the base directory or if forced to clean.
@ -39,6 +41,11 @@ class Clean(dotbot.Plugin):
return True
for item in os.listdir(os.path.expandvars(os.path.expanduser(target))):
path = os.path.join(os.path.expandvars(os.path.expanduser(target)), item)
if recursive and os.path.isdir(path):
# isdir implies not islink -- we don't want to descend into
# symlinked directories. okay to do a recursive call here
# because depth should be fairly limited
self._clean(path, force, recursive)
if not os.path.exists(path) and os.path.islink(path):
points_at = os.path.join(os.path.dirname(path), os.readlink(path))
if self._in_directory(path, self._context.base_directory()) or force:

@ -0,0 +1,34 @@
test_description='clean removes recursively'
. '../test-lib.bash'
test_expect_success 'setup' '
mkdir -p ~/a/b
ln -s /nowhere ~/c
ln -s /nowhere ~/a/d
ln -s /nowhere ~/a/b/e
test_expect_success 'run' '
run_dotbot <<EOF
- clean:
force: true
test_expect_success 'test' '
! test -h ~/c && test -h ~/a/d && test -h ~/a/b/e
test_expect_success 'run 2' '
run_dotbot <<EOF
- clean:
force: true
recursive: true
test_expect_success 'test 2' '
! test -h ~/a/d && ! test -h ~/a/b/e