Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multiple targets #11

Closed
wants to merge 1 commit into from
Closed
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
214 changes: 131 additions & 83 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
--------
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -234,41 +243,80 @@ configuration. The conventional name for the configuration file is
`install.conf.yaml`.

```yaml
- clean: ['~']

- link:
~/.dotfiles: ''
~/.tmux.conf: tmux.conf
~/.vim: vim/
~/.vimrc: vimrc

- shell:
- [git update-submodules, Installing/updating submodules]
common:
- clean: ['~']

- link:
~/.dotfiles: ''
~/.tmux.conf: tmux.conf
~/.vim: vim/
~/.vimrc: vimrc

- 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
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
Expand Down
23 changes: 16 additions & 7 deletions dotbot/cli.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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():
Expand All @@ -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:
Expand Down
16 changes: 14 additions & 2 deletions dotbot/config.py
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -17,3 +26,6 @@ def get_config(self):

class ReadingError(Exception):
pass

class ConfigurationError(Exception):
pass