Compare commits
5 Commits
53b3781fbb
...
b04a3f1844
Author | SHA1 | Date |
---|---|---|
Anish Athalye | b04a3f1844 | |
Anish Athalye | 416f32f5fe | |
Anish Athalye | 9f8fd76f32 | |
Anish Athalye | 4daa065dc9 | |
Anish Athalye | ed60c62432 |
|
@ -201,6 +201,8 @@ When `glob: True`, Dotbot uses [glob.glob](https://docs.python.org/3/library/glo
|
|||
|
||||
However, due to the design of `glob.glob`, using a glob pattern such as `config/*`, will **not** match items that begin with `.`. To specifically capture items that being with `.`, you will need to include the `.` in the pattern, like this: `config/.*`.
|
||||
|
||||
When using glob with the `exclude:` option, the paths in the exclude paths should be relative to the base directory, same as the glob pattern itself. For example, if a glob pattern `vim/*` matches directories `vim/autoload`, `vim/ftdetect`, `vim/ftplugin`, and `vim/spell`, and you want to ignore the spell directory, then you should use `exclude: ["vim/spell"]` (not just `"spell"`).
|
||||
|
||||
#### Example
|
||||
|
||||
```yaml
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from .cli import main
|
||||
from .plugin import Plugin
|
||||
|
||||
__version__ = "1.19.1"
|
||||
__version__ = "1.19.2"
|
||||
|
|
|
@ -95,7 +95,7 @@ def main():
|
|||
["git", "rev-parse", "HEAD"],
|
||||
cwd=os.path.dirname(os.path.abspath(__file__)),
|
||||
stderr=devnull,
|
||||
)
|
||||
).decode("ascii")
|
||||
hash_msg = " (git %s)" % git_hash[:10]
|
||||
except (OSError, subprocess.CalledProcessError):
|
||||
hash_msg = ""
|
||||
|
|
|
@ -59,64 +59,39 @@ class Link(Plugin):
|
|||
self._log.lowinfo("Skipping %s" % destination)
|
||||
continue
|
||||
path = os.path.normpath(os.path.expandvars(os.path.expanduser(path)))
|
||||
if use_glob:
|
||||
if use_glob and self._has_glob_chars(path):
|
||||
glob_results = self._create_glob_results(path, exclude_paths)
|
||||
if len(glob_results) == 0:
|
||||
self._log.warning("Globbing couldn't find anything matching " + str(path))
|
||||
success = False
|
||||
continue
|
||||
if len(glob_results) == 1 and destination[-1] == "/":
|
||||
self._log.error("Ambiguous action requested.")
|
||||
self._log.error(
|
||||
"No wildcard in glob, directory use undefined: "
|
||||
+ destination
|
||||
+ " -> "
|
||||
+ str(glob_results)
|
||||
self._log.lowinfo("Globs from '" + path + "': " + str(glob_results))
|
||||
for glob_full_item in glob_results:
|
||||
# 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 :]
|
||||
)
|
||||
self._log.warning("Did you want to link the directory or into it?")
|
||||
success = False
|
||||
continue
|
||||
elif len(glob_results) == 1 and destination[-1] != "/":
|
||||
# perform a normal link operation
|
||||
# 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:
|
||||
success &= self._create(destination)
|
||||
success &= self._create(glob_link_destination)
|
||||
if force or relink:
|
||||
success &= self._delete(path, destination, relative, canonical_path, force)
|
||||
success &= self._link(
|
||||
path, destination, relative, canonical_path, ignore_missing
|
||||
)
|
||||
else:
|
||||
self._log.lowinfo("Globs from '" + path + "': " + str(glob_results))
|
||||
for glob_full_item in glob_results:
|
||||
# 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:
|
||||
success &= self._create(glob_link_destination)
|
||||
if force or relink:
|
||||
success &= self._delete(
|
||||
glob_full_item,
|
||||
glob_link_destination,
|
||||
relative,
|
||||
canonical_path,
|
||||
force,
|
||||
)
|
||||
success &= self._link(
|
||||
success &= self._delete(
|
||||
glob_full_item,
|
||||
glob_link_destination,
|
||||
relative,
|
||||
canonical_path,
|
||||
ignore_missing,
|
||||
force,
|
||||
)
|
||||
success &= self._link(
|
||||
glob_full_item,
|
||||
glob_link_destination,
|
||||
relative,
|
||||
canonical_path,
|
||||
ignore_missing,
|
||||
)
|
||||
else:
|
||||
if create:
|
||||
success &= self._create(destination)
|
||||
|
@ -155,6 +130,9 @@ class Link(Plugin):
|
|||
else:
|
||||
return source
|
||||
|
||||
def _has_glob_chars(self, path):
|
||||
return any(i in path for i in "?*[")
|
||||
|
||||
def _glob(self, path):
|
||||
"""
|
||||
Wrap `glob.glob` in a python agnostic way, catching errors in usage.
|
||||
|
|
|
@ -270,7 +270,7 @@ def test_link_glob_4(home, dotfiles, run_dotbot):
|
|||
|
||||
|
||||
@pytest.mark.parametrize("path", ("foo", "foo/"))
|
||||
def test_link_glob_ambiguous_failure(path, home, dotfiles, run_dotbot):
|
||||
def test_link_glob_ignore_no_glob_chars(path, home, dotfiles, run_dotbot):
|
||||
"""Verify ambiguous link globbing fails."""
|
||||
|
||||
dotfiles.makedirs("foo")
|
||||
|
@ -286,28 +286,8 @@ def test_link_glob_ambiguous_failure(path, home, dotfiles, run_dotbot):
|
|||
}
|
||||
]
|
||||
)
|
||||
with pytest.raises(SystemExit):
|
||||
run_dotbot()
|
||||
assert not os.path.exists(os.path.join(home, "foo"))
|
||||
|
||||
|
||||
def test_link_glob_ambiguous_success(home, dotfiles, run_dotbot):
|
||||
"""Verify the case where ambiguous link globbing succeeds."""
|
||||
|
||||
dotfiles.makedirs("foo")
|
||||
dotfiles.write_config(
|
||||
[
|
||||
{
|
||||
"link": {
|
||||
"~/foo": {
|
||||
"path": "foo",
|
||||
"glob": True,
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
)
|
||||
run_dotbot()
|
||||
assert os.path.islink(os.path.join(home, "foo"))
|
||||
assert os.path.exists(os.path.join(home, "foo"))
|
||||
|
||||
|
||||
|
@ -587,6 +567,39 @@ def test_link_glob_recursive(home, dotfiles, run_dotbot):
|
|||
assert file.read() == "cherry"
|
||||
|
||||
|
||||
def test_link_glob_no_match(home, dotfiles, run_dotbot):
|
||||
"""Verify that a glob with no match doesn't raise an error."""
|
||||
|
||||
dotfiles.makedirs("foo")
|
||||
dotfiles.write_config(
|
||||
[
|
||||
{"defaults": {"link": {"glob": True, "create": True}}},
|
||||
{"link": {"~/.config/foo": "foo/*"}},
|
||||
]
|
||||
)
|
||||
run_dotbot()
|
||||
|
||||
|
||||
def test_link_glob_single_match(home, dotfiles, run_dotbot):
|
||||
"""Verify linking works even when glob matches exactly one file."""
|
||||
# regression test for https://github.com/anishathalye/dotbot/issues/282
|
||||
|
||||
dotfiles.write("foo/a", "apple")
|
||||
dotfiles.write_config(
|
||||
[
|
||||
{"defaults": {"link": {"glob": True, "create": True}}},
|
||||
{"link": {"~/.config/foo": "foo/*"}},
|
||||
]
|
||||
)
|
||||
run_dotbot()
|
||||
|
||||
assert not os.path.islink(os.path.join(home, ".config"))
|
||||
assert not os.path.islink(os.path.join(home, ".config", "foo"))
|
||||
assert os.path.islink(os.path.join(home, ".config", "foo", "a"))
|
||||
with open(os.path.join(home, ".config", "foo", "a")) as file:
|
||||
assert file.read() == "apple"
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
"sys.platform[:5] == 'win32'",
|
||||
reason="These if commands won't run on Windows",
|
||||
|
|
Loading…
Reference in New Issue