diff --git a/.idea/misc.xml b/.idea/misc.xml
index 5bbe586..0c153f3 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/.idea/snowsaw.iml b/.idea/snowsaw.iml
index 63526b9..76bb0d9 100644
--- a/.idea/snowsaw.iml
+++ b/.idea/snowsaw.iml
@@ -5,7 +5,7 @@
-
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4bfb5c4..f8e5ddc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,49 @@
---
+# 0.2.0
+*2017-01-28*
+## Improvements
+### Core Plugins
+❯ The `hosts` option format of the [`link`](https://github.com/arcticicestudio/snowsaw#link) core plugin has been changed as a result to a bug also fixed in this version.
+Example of the new format:
+```json
+[
+ {
+ "link": {
+ "~/.gitconfig": {
+ "hosts": {
+ "archlinux-home": "gitconfig.home",
+ "archlinux-work": "gitconfig.work"
+ }
+ }
+ }
+ }
+]
+```
+Further information can be found in the bug fixes section below and in the associated issue #18 and PR #19.
+
+### Documentation
+❯ Added a project [debugging guide](https://github.com/arcticicestudio/snowsaw#debugging) for [JetBrains PyCharm](https://www.jetbrains.com/pycharm). (@arcticicestudio, 9694b523)
+![](https://raw.githubusercontent.com/arcticicestudio/snowsaw/develop/assets/scrot-readme-debugging-run-configuration.png)
+
+❯ Added a table of content for the [project README](https://github.com/arcticicestudio/snowsaw/blob/develop/README.md). (@arcticicestudio, 1bd1510c)
+
+## Bug Fixes
+### Core Plugins
+❯ Fixed a bug where only the last duplicate link item in a snowblock configuration has been processed when using the
+host-specific option `hosts` although if the host doesn't match the current hostname.
+In some cases when the order of the link items has been changed also valid items for the current host have been marked
+as skippable instead of linking them.
+
+This bug was caused by an internal design conflict with the builtin Python type `dict` (dictionary) that only allows
+unique keys which has been broken by defining multiple link items with the same destination path.
+The new `hosts` option structure allows to define any amount of hosts with their associated target path.
+(@arcticicestudio, #18 / PR #19, b921c489)
+
+### Documentation
+❯ Fixed some Markdown formatting issues in the project README. (@arcticicestudio, 7d7c0104 / e318b8d7)
+
# 0.1.1
*2017-01-07*
## Bug Fixes
diff --git a/README.md b/README.md
index 5fe1251..5bf48ba 100644
--- a/README.md
+++ b/README.md
@@ -9,8 +9,31 @@
It does less than you think, because version control systems do more than you think.
Designed to be self-contained and extensible with no external dependencies and no installation required.
+ - [Getting started](#getting-started)
+ - [Integration](#integration)
+ - [Add the submodule](#add-the-submodule)
+ - [Create a bootstrap script](#create-a-bootstrap-script)
+ - [Version Update](#version-update)
+ - [Design Concept](#design-concept)
+ - [snowblocks](#snowblocks)
+ - [Repository Structure](#repository-structure)
+ - [Plugins](#plugins)
+ - [Plugins API](#plugin-api)
+ - [CLI](#cli)
+ - [Configuration](#configuration)
+ - [Core Tasks](#core-tasks)
+ - [`link`](#link)
+ - [`shell`](#shell)
+ - [`clean`](#clean)
+ - [Defaults](#defaults)
+ - [Development](#development)
+ - [Debugging](#debugging)
+ - [JetBrains PyCharm](#jetbrains-pycharm)
+ - [Contribution](#contribution)
+ - [Credits](#credits)
+
## Getting started
-To get started you should read the [design concept](#design-concept) and [configuration](#configuration) documentation sections before integrating snowsaw to understand the way it works.
+Plase make sure to read the [design concept](#design-concept) and [configuration](#configuration) documentation sections before integrating snowsaw to understand the way it works.
### Integration
#### Add the submodule
@@ -22,7 +45,7 @@ git submodule add https://github.com/arcticicestudio/snowsaw .snowsaw
This command will add the snowsaw project at the main development branch `develop`, but it is recommened to use a stable release version by running
```sh
cd .snowsaw
-git checkout v0.1.1
+git checkout v0.2.0
cd ..
```
and commit the changes in your dotfile repository to lock it on the specified version tag.
@@ -52,7 +75,7 @@ SNOWBLOCKSDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$SNOWBLOCKS_BASE_DI
```
The `${@}` allows to additionally specify supported options on the terminal for a dynamic script execution.
-### Update the version
+### Version Update
The snowsaw version can be simply updated by checking out to the desired version tag inside the submodule repository:
```sh
cd .snowsaw
@@ -70,18 +93,16 @@ This design allows a modular structured dotfile repository where each topic can
The structure plays well with the snowsaw feature that allows to specify one configuration file to only process a single `snowblock`.
### Repository Structure
-snowsaw does not need any specific repository structure expect the main `snowblock`s directory. A dotfile repository may also contain more than `snowblock` directories. This way the repository can be structured even more fine-grained.
+snowsaw does not need any specific repository structure expect the base snowblocks directory. A dotfile repository may also contain more than one base snowblock directory. This way the repository can be structured even more fine-grained.
The dotfile repository [Igloo](https://github.com/arcticicestudio/igloo) may be used as a repository structure reference.
```
-|-- .snowman
|-- .git
|-- .github
-|-- .gitignore
-|-- .gitmodules
-|-- bootstrap
+|-- .snowsaw
+|-- assets
|-- snowblocks
|-- atom
|-- config.cson
@@ -95,8 +116,12 @@ The dotfile repository [Igloo](https://github.com/arcticicestudio/igloo) may be
|-- vim
|-- snowblock.json
|-- vimrc
-|-- README.md
+|-- .gitignore
+|-- .gitmodules
|-- CHANGELOG.md
+|-- LICENSE.md
+|-- README.md
+|-- bootstrap
```
> Example directory tree of a dotfile repository
@@ -135,7 +160,7 @@ The [core plugins](https://github.com/arcticicestudio/snowsaw/tree/master/snowsa
snowsaw supports to specify CLI terminal parameters to dynamically control the execution.
A terminal help page can be shown by using the `-h`/`--help` option.
-| Option | Parameter(s) |Required | Description |
+| Option | Parameter(s) | Required | Description |
| --- | --- | --- | --- |
| `-Q`, `--super-quiet` | - | No | Suppress almost all output. |
| `-q`, `--quiet` | - | No | Suppress most output. |
@@ -162,13 +187,13 @@ Links support an optional extended configuration. In this type of configuration,
These dictionaries support the following options:
| Option | Values | Default Value | Required | Description |
-| --- | --- | --- |
+| --- | --- | --- | --- | --- |
| `create` | `true`, `false` | `false` | No | Specifies if the parent directory should be created if necessary. |
| `force` | `true`, `false` | `false` | No | Specifies if the file or directory should be forcibly linked. **This can cause irreversible data loss! Use with caution!** |
-| `host` | `string[]` | `[]` | No | Contains hostnames this link should be processed for. Links with an empty array will be processed irrespective of the host. |
+| `hosts` | `dict` | `{}` | No | Contains key-value entries with hostnames and their associated target path this link should be processed for. Links with an empty dictionary will be processed irrespective of the host. |
| `path` | `string`, `null` | `null` | No | The path to map the source path. If the path is omitted or `null`, snowsaw will use the basename of the destination, with a leading `.` stripped if present. |
-| `relink` | `true`, `false` | No | `false` | Specifies if incorrect symbolic links should be automatically overwritten. |
-| `relative` | `true`, `false` | No | `false` | Specifies if the symbolic link should have a relative path. |
+| `relink` | `true`, `false` | `false` | No | Specifies if incorrect symbolic links should be automatically overwritten. |
+| `relative` | `true`, `false` | `false` | No | Specifies if the symbolic link should have a relative path. |
##### Example
```json
@@ -177,12 +202,17 @@ These dictionaries support the following options:
"link": {
"~/.gitconfig": {
"create": true,
- "host": ["archlinux-work"],
- "path": "gitconfig-work"
+ "hosts": {
+ "archlinux-home": "gitconfig.home",
+ "archlinux-work": "gitconfig.work"
+ }
+ },
+ "~/.gitconfig_auth": {
+ "path": "gitconfig_auth.local"
},
"~/.gitignore": {
"force": true,
- "relink": true
+ "relink": true,
},
"~/.git-commit-message": {
"relative": true
@@ -204,7 +234,7 @@ Another way is to specify a two element array where the first element is the she
Shell tasks support an extended syntax as well, which provides more fine-grained control. A command can be specified as a dictionary that contains the following options:
| Option | Values | Default Value | Required | Description |
-| --- | --- | --- |
+| --- | --- | --- | --- | --- |
| `command` | `string` | - | Yes | The command to be run. |
| `description` | `string` | - | No | A human-readable description. |
| `stdin` | `true`, `false` | `false` | No | Specifies if the standard input stream is enabled. |
@@ -266,7 +296,23 @@ Defaults are specified as a dictionary mapping action names to settings, which a
```
## Development
-[![](https://img.shields.io/badge/Changelog-0.1.1-blue.svg)](https://github.com/arcticicestudio/snowsaw/blob/v0.1.1/CHANGELOG.md) [![](https://img.shields.io/badge/Workflow-gitflow--branching--model-blue.svg)](http://nvie.com/posts/a-successful-git-branching-model) [![](https://img.shields.io/badge/Versioning-ArcVer_0.8.0-blue.svg)](https://github.com/arcticicestudio/arcver)
+[![](https://img.shields.io/badge/Changelog-0.2.0-blue.svg)](https://github.com/arcticicestudio/snowsaw/blob/v0.2.0/CHANGELOG.md) [![](https://img.shields.io/badge/Workflow-gitflow--branching--model-blue.svg)](http://nvie.com/posts/a-successful-git-branching-model) [![](https://img.shields.io/badge/Versioning-ArcVer_0.8.0-blue.svg)](https://github.com/arcticicestudio/arcver)
+
+### Debugging
+#### JetBrains PyCharm
+snowsaw is developed using one of the awesome tools from JetBrains called [PyCharm](https://www.jetbrains.com/pycharm).
+The project files are located in the [`.idea`](https://github.com/arcticicestudio/snowsaw/tree/develop/.idea) directory.
+
+The included [run/debug configurations](https://www.jetbrains.com/help/pycharm/run-debug-configurations.html) for the script must be manually adjusted to match the paths to the main [`snmowsaw`](https://github.com/arcticicestudio/snowsaw/blob/develop/bin/snowsaw) script and the path parameter for the `-s` / `--snowblocks-directory` CLI option.
+![][scrot-readme-debugging-run-configuration]
+
+snowsaw can [run](https://www.jetbrains.com/help/pycharm/running-applications.html) in the debug mode by using the bug icon or from the menu via *Run* -> *Debug 'snowsaw'*.
+![][scrot-readme-debugging-run]
+![][scrot-readme-debugging-run-menu]
+
+The [debug window](https://www.jetbrains.com/help/pycharm/debug-tool-window.html) will automatically toggle to show [*Variables*](https://www.jetbrains.com/help/pycharm/debug-tool-window-variables.html) for defined debug breakpoints marked in the editor gutter.
+![][scrot-readme-debugging-code]
+![][scrot-readme-debugging-variables]
### Contribution
Please report issues/bugs, feature requests and suggestions for improvements to the [issue tracker](https://github.com/arcticicestudio/snowsaw/issues).
@@ -277,3 +323,9 @@ snowsaw is based on the awesome [Dotbot](https://github.com/anishathalye/dotbot)
+
+[scrot-readme-debugging-run-configuration]: https://raw.githubusercontent.com/arcticicestudio/snowsaw/develop/assets/scrot-readme-debugging-run-configuration.png
+[scrot-readme-debugging-run]: https://raw.githubusercontent.com/arcticicestudio/snowsaw/develop/assets/scrot-readme-debugging-run.png
+[scrot-readme-debugging-run-menu]: https://raw.githubusercontent.com/arcticicestudio/snowsaw/develop/assets/scrot-readme-debugging-run-menu.png
+[scrot-readme-debugging-variables]: https://raw.githubusercontent.com/arcticicestudio/snowsaw/develop/assets/scrot-readme-debugging-variables.png
+[scrot-readme-debugging-code]: https://raw.githubusercontent.com/arcticicestudio/snowsaw/develop/assets/scrot-readme-debugging-code.png
diff --git a/assets/scrot-readme-debugging-code.png b/assets/scrot-readme-debugging-code.png
new file mode 100755
index 0000000..52b6bbd
Binary files /dev/null and b/assets/scrot-readme-debugging-code.png differ
diff --git a/assets/scrot-readme-debugging-run-configuration.png b/assets/scrot-readme-debugging-run-configuration.png
new file mode 100755
index 0000000..7541e0a
Binary files /dev/null and b/assets/scrot-readme-debugging-run-configuration.png differ
diff --git a/assets/scrot-readme-debugging-run-menu.png b/assets/scrot-readme-debugging-run-menu.png
new file mode 100755
index 0000000..262403b
Binary files /dev/null and b/assets/scrot-readme-debugging-run-menu.png differ
diff --git a/assets/scrot-readme-debugging-run.png b/assets/scrot-readme-debugging-run.png
new file mode 100755
index 0000000..d252e7c
Binary files /dev/null and b/assets/scrot-readme-debugging-run.png differ
diff --git a/assets/scrot-readme-debugging-variables.png b/assets/scrot-readme-debugging-variables.png
new file mode 100755
index 0000000..43b90c4
Binary files /dev/null and b/assets/scrot-readme-debugging-variables.png differ
diff --git a/snowsaw/plugins/link.py b/snowsaw/plugins/link.py
index b038ce8..50f9068 100644
--- a/snowsaw/plugins/link.py
+++ b/snowsaw/plugins/link.py
@@ -34,7 +34,7 @@ def _process_links(self, links):
force = defaults.get("force", False)
relink = defaults.get("relink", False)
create = defaults.get("create", False)
- hosts = defaults.get("hosts", [])
+ hosts = defaults.get("hosts", {})
if isinstance(source, dict):
relative = source.get("relative", relative)
force = source.get("force", force)
@@ -46,9 +46,13 @@ def _process_links(self, links):
path = self._default_source(destination, source)
path = os.path.expandvars(os.path.expanduser(path))
- if not self._is_target_host(hosts, hostname):
- self._log.lowinfo("Skipped host specific link {} -> {}".format(destination, os.path.join(self._context.snowblock_dir(), path)))
- continue
+ if hosts:
+ for host in hosts.items():
+ if not host[0] == hostname:
+ self._log.lowinfo("Skipped host specific link {} -> {}".format(destination, os.path.join(self._context.snowblock_dir(), host[1])))
+ continue
+ else:
+ path = os.path.expandvars(os.path.expanduser(hosts.get(hostname)))
if not self._exists(os.path.join(self._context.snowblock_dir(), path)):
success = False
@@ -97,18 +101,6 @@ def _is_link(self, path):
"""
return os.path.islink(os.path.expanduser(path))
- def _is_target_host(self, hosts, host):
- """
- Checks if the specified host is a target.
-
- A empty default list of target hosts indicates that all hosts are allowed.
-
- :param hosts: The list of target hosts
- :param host: The name of the host to check
- :return: True if the specified host is listed or the hosts list is empty, False otherwise
- """
- return True if not hosts or host in hosts else False
-
def _link_destination(self, path):
"""
Gets the destination of the specified symbolic link.
diff --git a/snowsaw/setup.py b/snowsaw/setup.py
index 3985730..e398409 100644
--- a/snowsaw/setup.py
+++ b/snowsaw/setup.py
@@ -2,7 +2,7 @@
setup(
name='snowsaw',
- version='0.1.1',
+ version='0.2.0',
packages=['', 'util', 'logging'],
package_dir={'': 'snowsaw'},
url='https://github.com/arcticicestudio/snowsaw',