mirror of
1
0
Fork 0

Add multiple targets

Allow the user to define multiple targets to execute a consistent group of tasks.
This commit is contained in:
Morgan Courbet 2015-02-03 18:08:57 +01:00
parent 38c0f65801
commit d4f744952e
3 changed files with 159 additions and 90 deletions

210
README.md
View File

@ -33,7 +33,7 @@ without the annoyance of having to manually copy or link files.
Dotbot itself is entirely self contained and requires no installation (it's
self-bootstrapping), so it's not necessary to install any software before you
provision a new machine! All you have to do is download your dotfiles and then
run `./install`.
run `./install -t <list of targets>`.
Template
--------
@ -94,10 +94,10 @@ installer should be able to be run multiple times without causing any
problems.** This makes a lot of things easier to do (in particular, syncing
updates between machines becomes really easy).
Dotbot configuration files are YAML (or JSON) arrays of tasks, where each task
is a dictionary that contains a command name mapping to data for that command.
Tasks are run in the order in which they are specified. Commands within a task
do not have a defined ordering.
Dotbot configuration files are YAML (or JSON) dictionaries of targets with
arrays of tasks, where each task is a dictionary that contains a command name
mapping to data for that command. Tasks are run in the order in which they are
specified. Commands within a task do not have a defined ordering.
### Link
@ -122,34 +122,37 @@ should be forcibly linked.
##### Example (YAML)
```yaml
- link:
~/.config/terminator:
create: true
path: config/terminator/
~/.vim: vim/
~/.vimrc: vimrc
~/.zshrc:
force: true
path: zshrc
work:
- link:
~/.config/terminator:
create: true
path: config/terminator/
~/.vim: vim/
~/.vimrc: vimrc
~/.zshrc:
force: true
path: zshrc
```
##### Example (JSON)
```json
[{
"link": {
"~/.config/terminator": {
"create": true,
"path": "config/terminator/"
},
"~/.vim": "vim/",
"~/.vimrc": "vimrc",
"~/.zshrc": {
"force": true,
"path": "zshrc"
{
"work": [{
"link": {
"~/.config/terminator": {
"create": true,
"path": "config/terminator/"
},
"~/.vim": "vim/",
"~/.vimrc": "vimrc",
"~/.zshrc": {
"force": true,
"path": "zshrc"
}
}
}
}]
}]
}
```
### Shell
@ -171,36 +174,39 @@ this syntax, all keys are optional except for the command itself.
##### Example (YAML)
```yaml
- shell:
- mkdir -p ~/src
- [mkdir -p ~/downloads, Creating downloads directory]
-
command: read var && echo Your variable is $var
stdin: true
stdout: true
-
command: read fail
stderr: true
work:
- shell:
- mkdir -p ~/src
- [mkdir -p ~/downloads, Creating downloads directory]
-
command: read var && echo Your variable is $var
stdin: true
stdout: true
-
command: read fail
stderr: true
```
##### Example (JSON)
```json
[{
"shell": [
"mkdir -p ~/src",
["mkdir -p ~/downloads", "Creating downloads directory"],
{
"command": "read var && echo Your variable is $var",
"stdin": true,
"stdout": true
},
{
"command": "read fail",
"stderr": true
}
]
}]
{
"work": [{
"shell": [
"mkdir -p ~/src",
["mkdir -p ~/downloads", "Creating downloads directory"],
{
"command": "read var && echo Your variable is $var",
"stdin": true,
"stdout": true
},
{
"command": "read fail",
"stderr": true
}
]
}]
}
```
### Clean
@ -216,15 +222,18 @@ Clean commands are specified as an array of directories to be cleaned.
##### Example (YAML)
```yaml
- clean: ['~']
work:
- clean: ['~']
```
##### Example (JSON)
```json
[{
"clean": ["~"]
}]
{
"work": [{
"clean": ["~"]
}]
}
```
### Full Example
@ -234,16 +243,26 @@ configuration. The conventional name for the configuration file is
`install.conf.yaml`.
```yaml
- clean: ['~']
common:
- clean: ['~']
- link:
~/.dotfiles: ''
~/.tmux.conf: tmux.conf
~/.vim: vim/
~/.vimrc: vimrc
- link:
~/.dotfiles: ''
~/.tmux.conf: tmux.conf
~/.vim: vim/
~/.vimrc: vimrc
- shell:
- [git update-submodules, Installing/updating submodules]
- shell:
- [git update-submodules, Installing/updating submodules]
laptop:
- shell:
- [sudo apt-get install vim, Installing vim]
server:
- shell:
- [sudo apt-get install tmux, Installing tmux]
- [echo 'Europe/Paris' | sudo tee /etc/timezone > /dev/null && sudo dpkg-reconfigure -f noninteractive tzdata, Configuring timezone]
```
The configuration file can also be written in JSON. Here is the JSON equivalent
@ -251,24 +270,53 @@ of the YAML configuration given above. The conventional name for this file is
`install.conf.json`.
```json
[
{
"clean": ["~"]
},
{
"link": {
"~/.dotfiles": "",
"~/.tmux.conf": "tmux.conf",
"~/.vim": "vim/",
"~/.vimrc": "vimrc"
{
"common": [
{
"clean": ["~"]
},
{
"link": {
"~/.dotfiles": "",
"~/.tmux.conf": "tmux.conf",
"~/.vim": "vim/",
"~/.vimrc": "vimrc"
}
},
{
"shell": [
["git submodule update --init --recursive", "Installing submodules"]
]
}
},
{
"shell": [
["git submodule update --init --recursive", "Installing submodules"]
]
}
]
],
"laptop": [
{
"clean": []
},
{
"link": {}
},
{
"shell": [
["sudo apt-get install vim", "Installing vim"]
]
}
],
"server": [
{
"clean": []
},
{
"link": {}
},
{
"shell": [
["sudo apt-get install tmux", "Installing tmux"],
["echo 'Europe/Paris' | sudo tee /etc/timezone > /dev/null && sudo dpkg-reconfigure -f noninteractive tzdata", "Configuring timezone"]
]
}
]
}
```
Contributing

View File

@ -1,5 +1,5 @@
from argparse import ArgumentParser
from .config import ConfigReader, ReadingError
from .config import ConfigReader, ReadingError, ConfigurationError
from .dispatcher import Dispatcher, DispatchError
from .messenger import Messenger
from .messenger import Level
@ -17,9 +17,12 @@ def add_options(parser):
parser.add_argument('-c', '--config-file', nargs = 1, dest = 'config_file',
help = 'run commands given in CONFIGFILE', metavar = 'CONFIGFILE',
required = True)
parser.add_argument('-t', '--targets', nargs = '*', dest = 'targets',
help = 'set the target environments defined in the configuration file', metavar = 'TARGET',
required = True)
def read_config(config_file):
reader = ConfigReader(config_file)
def read_config(config_file, target):
reader = ConfigReader(config_file, target)
return reader.get_config()
def main():
@ -34,14 +37,20 @@ def main():
log.set_level(Level.INFO)
if (options.verbose):
log.set_level(Level.DEBUG)
tasks = read_config(options.config_file[0])
dispatcher = Dispatcher(options.base_directory[0])
success = dispatcher.dispatch(tasks)
targets = options.targets
target_tasks = read_config(options.config_file[0], targets)
success = True
for target, tasks in target_tasks.iteritems():
log.info('\nExecuting tasks for target %s' % target)
dispatcher = Dispatcher(options.base_directory[0])
success &= dispatcher.dispatch(tasks)
if success:
log.info('\n==> All tasks executed successfully')
else:
raise DispatchError('\n==> Some tasks were not executed successfully')
except (ReadingError, DispatchError) as e:
except (ReadingError, DispatchError, ConfigurationError) as e:
log.error('%s' % e)
exit(1)
except KeyboardInterrupt:

View File

@ -1,8 +1,17 @@
import yaml
from .messenger import Messenger
class ConfigReader(object):
def __init__(self, config_file_path):
self._config = self._read(config_file_path)
def __init__(self, config_file_path, targets):
complete_config = self._read(config_file_path)
target_configs = {}
for target in targets:
if not complete_config.has_key(target):
raise ConfigurationError('The target %s is not defined in the configuration file' % target)
target_configs[target] = complete_config.get(target)
self._config = target_configs
def _read(self, config_file_path):
try:
@ -17,3 +26,6 @@ class ConfigReader(object):
class ReadingError(Exception):
pass
class ConfigurationError(Exception):
pass