Add Cleaner executor
This commit is contained in:
parent
24f49603c8
commit
abb825048b
3 changed files with 58 additions and 3 deletions
11
README.md
11
README.md
|
@ -88,8 +88,9 @@ Configuration
|
|||
-------------
|
||||
|
||||
Dotbot uses json-formatted configuration files to let you specify how to set up
|
||||
your dotfiles. Currently, Dotbot knows how to `link` files and execute `shell`
|
||||
commands. Dotbot executes tasks in the order that they are specified in.
|
||||
your dotfiles. Currently, Dotbot knows how to `link` files, execute `shell`
|
||||
commands, and `clean` directories of broken symbolic links. Dotbot executes
|
||||
tasks in the order that they are specified in.
|
||||
|
||||
**Ideally, bootstrap configurations should be idempotent. That is, the
|
||||
installer should be able to be run multiple times without causing any
|
||||
|
@ -100,7 +101,8 @@ dictionary that contains a command name mapping to data for that command. For
|
|||
`link`, you specify how files should be linked in a dictionary. For `shell`,
|
||||
you specify an array consisting of commands, where each command is an array
|
||||
consisting of the shell command as the first element and a description as the
|
||||
second.
|
||||
second. For `clean`, you specify an array consisting of targets, where each
|
||||
target is a path to a directory.
|
||||
|
||||
Dotbot is aware of a base directory (that is specified when running the
|
||||
installer), so link targets can be specified relative to that, and shell
|
||||
|
@ -111,6 +113,9 @@ started. The convention for configuration file names is `install.conf.json`.
|
|||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"clean": ["~"]
|
||||
},
|
||||
{
|
||||
"link": {
|
||||
"~/.tmux.conf": "tmux.conf",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from .executor import Executor
|
||||
from .linker import Linker
|
||||
from .cleaner import Cleaner
|
||||
from .commandrunner import CommandRunner
|
||||
|
|
49
dotbot/executor/cleaner.py
Normal file
49
dotbot/executor/cleaner.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
import os
|
||||
from . import Executor
|
||||
|
||||
class Cleaner(Executor):
|
||||
'''
|
||||
Cleans broken symbolic links.
|
||||
'''
|
||||
|
||||
_directive = 'clean'
|
||||
|
||||
def can_handle(self, directive):
|
||||
return directive == self._directive
|
||||
|
||||
def handle(self, directive, data):
|
||||
if directive != self._directive:
|
||||
raise ValueError('Cleaner cannot handle directive %s' % directive)
|
||||
return self._process_clean(data)
|
||||
|
||||
def _process_clean(self, targets):
|
||||
success = True
|
||||
for target in targets:
|
||||
success &= self._clean(target)
|
||||
if success:
|
||||
self._log.info('All targets have been cleaned')
|
||||
else:
|
||||
self._log.error('Some targets were not succesfully cleaned')
|
||||
return success
|
||||
|
||||
def _clean(self, target):
|
||||
'''
|
||||
Cleans all the broken symbolic links in target that point to
|
||||
a subdirectory of the base directory.
|
||||
'''
|
||||
for item in os.listdir(os.path.expanduser(target)):
|
||||
path = os.path.join(os.path.expanduser(target), item)
|
||||
if not os.path.exists(path) and os.path.islink(path):
|
||||
if self._in_directory(path, self._base_directory):
|
||||
self._log.lowinfo('Removing invalid link %s -> %s' %
|
||||
(path, os.path.join(os.path.dirname(path), os.readlink(path))))
|
||||
os.remove(path)
|
||||
return True
|
||||
|
||||
def _in_directory(self, path, directory):
|
||||
'''
|
||||
Returns true if the path is in the directory.
|
||||
'''
|
||||
directory = os.path.join(os.path.realpath(directory), '')
|
||||
path = os.path.realpath(path)
|
||||
return os.path.commonprefix([path, directory]) == directory
|
Loading…
Reference in a new issue