From 6c044208fa343bdef6a375faa0fbe77e3b9d8c19 Mon Sep 17 00:00:00 2001 From: Eric Engstrom Date: Thu, 27 May 2021 12:07:17 -0500 Subject: [PATCH] feat: Add `prefix: 'string'` option to linking when `glob: true`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows one to store files in a directory or git-repo without the leading `.`, as in: ``` dotconf: ├── README.md ├── bin │ ├── dotbot │ ├── look │ ├── pbfile │ └── ... ├── dot │ ├── bashrc │ ├── gitconfig │ ├── gitignore │ ├── gorc │ ├── login │ ├── ... │ ├── zshrc │ └── zshenv ``` Can take a many-line dotbot.yml listing **each** file in `dotconf/dot`, reducing it to five lines: ``` - link: ~/: path: dotconf/dot/* glob: true prefix: '.' ``` FIXES: #259 --- README.md | 24 +++++++++++++++++------- dotbot/plugins/link.py | 5 +++++ test/tests/link-prefix.bash | 23 +++++++++++++++++++++++ 3 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 test/tests/link-prefix.bash diff --git a/README.md b/README.md index cf99c3b..0ee7f7e 100644 --- a/README.md +++ b/README.md @@ -181,16 +181,22 @@ mapped to extended configuration dictionaries. | `force` | Force removes the old target, file or folder, and forces a new link (default: false) | | `relative` | Use a relative path to the source when creating the symlink (default: false, absolute links) | | `canonicalize` | Resolve any symbolic links encountered in the source to symlink to the canonical path (default: true, real paths) | -| `glob` | Treat a `*` character as a wildcard, and perform link operations on all of those matches (default: false) | | `if` | Execute this in your `$SHELL` and only link if it is successful. | | `ignore-missing` | Do not fail if the source is missing and create the link anyway (default: false) | -| `exclude` | Array of paths to remove from glob matches. Uses same syntax as `path`. Ignored if `glob` is `false`. (default: empty, keep all matches) | +| `glob` | Treat `path` as a glob pattern, expanding patterns referenced below, linking all *files** matched. (default: false) | +| `exclude` | Array of glob patterns to remove from glob matches. Uses same syntax as `path`. Ignored if `glob` is `false`. (default: empty, keep all matches) | +| `prefix` | Prepend prefix prefix to basename of each file when linked, when `glob` is `true`. (default: '') | -Dotbot uses [glob.glob](https://docs.python.org/3/library/glob.html#glob.glob) -to resolve glob paths. However, due to its design, using a glob path such as -`config/*` for example, will not match items that begin with `.`. To -specifically capture items that begin with `.`, you will need to use a path -like this: `config/.*`. +When `glob: True`, Dotbot uses [glob.glob](https://docs.python.org/3/library/glob.html#glob.glob) to resolve glob paths, expanding Unix shell-style wildcards, which are **not** the same as regular expressions; Only the following are expanded: + +| Pattern | Meaning | +|:---------|:-------------------------------------------------------| +| `*` | matches anything | +| `?` | matches any single character | +| `[seq]` | matches any character in `seq` | +| `[!seq]` | matches any character not in `seq` | + +However, due to the design of `glob.glob`, using a glob pattern such as `config/*`, will **not** match items that being with `.`. To specifically capture items that being with `.`, you will need to include the `.` in the pattern, like this: `config/.*`. #### Example @@ -209,6 +215,10 @@ like this: `config/.*`. ~/.hammerspoon: if: '[ `uname` = Darwin ]' path: hammerspoon + ~/: + glob: true + path: dotconf/* + prefix: '.' ``` If the source location is omitted or set to `null`, Dotbot will use the diff --git a/dotbot/plugins/link.py b/dotbot/plugins/link.py index 27e75dc..5614239 100644 --- a/dotbot/plugins/link.py +++ b/dotbot/plugins/link.py @@ -33,6 +33,7 @@ class Link(dotbot.Plugin): relink = defaults.get('relink', False) create = defaults.get('create', False) use_glob = defaults.get('glob', False) + base_prefix = defaults.get('prefix', '') test = defaults.get('if', None) ignore_missing = defaults.get('ignore-missing', False) exclude_paths = defaults.get('exclude', []) @@ -45,6 +46,7 @@ class Link(dotbot.Plugin): relink = source.get('relink', relink) create = source.get('create', create) use_glob = source.get('glob', use_glob) + base_prefix = source.get('prefix', base_prefix) ignore_missing = source.get('ignore-missing', ignore_missing) exclude_paths = source.get('exclude', exclude_paths) path = self._default_source(destination, source.get('path')) @@ -80,6 +82,9 @@ class Link(dotbot.Plugin): # Find common dirname between pattern and the item: glob_dirname = os.path.dirname(os.path.commonprefix([path, glob_full_item])) glob_item = (glob_full_item if len(glob_dirname) == 0 else glob_full_item[len(glob_dirname) + 1:]) + # Add prefix to basepath, if provided + if base_prefix: + glob_item = base_prefix + glob_item # where is it going glob_link_destination = os.path.join(destination, glob_item) if create: diff --git a/test/tests/link-prefix.bash b/test/tests/link-prefix.bash new file mode 100644 index 0000000..e3db102 --- /dev/null +++ b/test/tests/link-prefix.bash @@ -0,0 +1,23 @@ +test_description='link prefix' +. '../test-lib.bash' + +test_expect_success 'setup' ' +mkdir ${DOTFILES}/conf && +echo "apple" > ${DOTFILES}/conf/a && +echo "banana" > ${DOTFILES}/conf/b && +echo "cherry" > ${DOTFILES}/conf/c +' + +test_expect_success 'test glob w/ prefix' ' +run_dotbot -v <