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
|
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`
|
your dotfiles. Currently, Dotbot knows how to `link` files, execute `shell`
|
||||||
commands. Dotbot executes tasks in the order that they are specified in.
|
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
|
**Ideally, bootstrap configurations should be idempotent. That is, the
|
||||||
installer should be able to be run multiple times without causing any
|
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`,
|
`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
|
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
|
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
|
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
|
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
|
```json
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"clean": ["~"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"link": {
|
"link": {
|
||||||
"~/.tmux.conf": "tmux.conf",
|
"~/.tmux.conf": "tmux.conf",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
from .executor import Executor
|
from .executor import Executor
|
||||||
from .linker import Linker
|
from .linker import Linker
|
||||||
|
from .cleaner import Cleaner
|
||||||
from .commandrunner import CommandRunner
|
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