If the target directory as a file named X and a package has a
directory named X, or vice-versa, then it is impossible for Stow
to stow that entry X from the package, even if --adopt is supplied.
However we were previously only handling the former case, and not the
latter, and the test for the former was actually broken. So fix
stow_contents() to handle both cases correctly, fix the broken test,
and add a new test for the latter case.
Stow walks the package and target tree hierarchies by using mutually
recursive pairs of functions:
- `stow_contents()` and `stow_node()`
- `unstow_contents()` and `unstow_node()`
As Stow runs its planning from the target directory (`plan_*()` both
call `within_target_do()`), previously the parameters for these
included:
- `$target_subpath` (or `$target_subdir` in the `*_node()` functions):
the relative path from the target top-level directory to the target
subdirectory (initially `.` at the beginning of recursion). For
example, this could be `dir1/subdir1/file1`.
- `$source`: the relative path from the target _subdirectory_ (N.B. _not_
top-level directory) to the package subdirectory. For example, if
the relative path to the Stow directory is `../stow`, this could be
`../../../stow/pkg1/dir1/subdir1/file1`. This is used when stowing
to construct a new link, or when unstowing to detect whether the
link can be unstowed.
Each time it descends into a further subdirectory of the target and
package, it appends the new path segment onto both of these, and also
prefixes `$source` with another `..`. When the `--dotfiles` parameter
is enabled, it adjusts `$target_subdir`, performing the `dot-foo` =>
`.foo` adjustment on all segments of the path in one go. In this
case, `$target_subpath` could be something like `.dir1/subdir1/file1`,
and the corresponding `$source` could be something like
`../../../stow/pkg1/dot-dir1/subdir1/file1`.
However this doesn't leave an easy way to obtain the relative path
from the target _top-level_ directory to the package subdirectory
(i.e. `../stow/pkg1/dot-dir1/subdir1/file1`), which is needed for
checking its existence and if necessary iterating over its contents.
The current implementation solves this by including an extra `$level`
parameter which tracks the recursion depth, and uses that to strip the
right number of leading path segments off the front of `$source`.
(In the above example, it would remove `../..`.)
This implementation isn't the most elegant because:
- It involves adding things to `$source` and then removing them again.
- It performs the `dot-` => `.` adjustment on every path segment
at each level, which is overkill, since when recursing down a level,
only adjustment on the final subdirectory is required since the higher
segments have already had any required adjustment.
This in turn requires `adjust_dotfile` to be more complex than it
needs to be.
It also prevents a potential future where we might want Stow to
optionally start iterating from within a subdirectory of the whole
package install image / target tree, avoiding adjustment at higher
levels and only doing it at the levels below the starting point.
- It requires passing an extra `$level` parameter which can be
automatically calculated simply by counting the number of slashes
in `$target_subpath`.
So change the `$source` recursion parameter to instead track the
relative path from the top-level package directory to the package
subdirectory or file being considered for (un)stowing, and rename it
to avoid the ambiguity caused by the word "source".
Also automatically calculate the depth simply by counting the number
of slashes, and reconstruct `$source` when needed by combining the
relative path to the Stow directory with the package name and
`$target_subpath`.
Closes#33.
There was a ton of duplication which is not maintainable, so refactor
everything into a single test which still covers the differences.
This in turn revealed some issues in the unstowing logic:
- We shouldn't conflict if we find a file which isn't a link or a
directory; we can just skip over it.
- Unstowing with `--dotfiles` was using the wrong variable to obtain
the package path, and as a result having to perform an unnecessary
call to `adjust_dotfile()`.
So fix those at the same time.
$target was the source of the link, and $source was the
target (destination) of the link. Obviously this was hopelessly
confusing, so rename to avoid this.
When unstowing a package, cleanup_invalid_links() is invoked to remove
any invalid links owned by Stow. It was invoking link_owned_by_package()
to check whether each existing link is owned by Stow. This in turn
called find_stowed_path() which since 40a0807185 was not allowing for
the possibility that it could be passed a symlink *not* owned by Stow
with an absolute target and consequently emitting an erroneous warning.
So remove this erroneous warning, and refactor find_stowed_path()
to use two new helper functions for detecting stow directories:
link_dest_within_stow_dir() and find_containing_marked_stow_dir().
Also refactor the logic within each to be simpler and more accurate,
and add more test cases to the corresponding parts of the test suite.
Fixes#65.
Closes#103.
https://github.com/aspiers/stow/issues/65
Placing a .stow file in a directory tells Stow that this directory
should be considered a Stow directory. This is already
well-documented.
There was an undocumented and slightly broken feature where placing a
.nonstow file in a directory was treated in exactly the same way. The
intention was for .nonstow to cause Stow to skip stowing into and
unstowing from that directory and any of its descendants. However, it
also caused Stow to consider symlinks into any of those directories as
owned by Stow, even though that was clearly not the intention. So
separate treatment of .stow and .nonstow markers, so that while both
provide protection against Stow stowing and unstowing, only .stow
affects the symlink ownership logic in find_stowed_path() and
marked_stow_dir().
Probably no one uses the undocumented .nonstow feature, so it may make
sense to remove this in future.
Previously join_paths() was incorrectly handling absolute paths, for
example join_paths('a/b', '/c/d') would return 'a/b/c/d' rather than
'/c/d'. This was a problem when following a symlink in
find_stowed_path(), because if the symlink was not owned by Stow and
pointed to an absolute path, find_stowed_path() might accidentally
deem the link owned by Stow, if c/d was a valid path relative to the
current directory.
t/cli.t calls scripts which run with the first perl found in the
user's PATH (usually the system perl), not with the perl used for the
build, as reported here:
https://rt.cpan.org/Ticket/Display.html?id=129944
Thanks to Slaven Rezic for spotting this and reporting it!
stow 2.3.0 added external runtime dependencies on Hash::Merge and
Clone::Choose. Historically stow hasn't had runtime dependencies other
than Perl itself, which is a useful property if you're managing the
installation of Perl using stow; the bootstrapping instructions in
stow's manual would need updating to describe how to install these two
modules (and any dependencies they have now or in the future) as well.
However, Hash::Merge is much more general than stow actually needs, so
replace the merge() call with a few lines of equivalent code -- this
avoids the external dependencies, and is clearer than the merge()
call.
Many thanks to Adam Sampson for this patch:
https://lists.gnu.org/archive/html/bug-stow/2019-06/msg00001.html
.stowrc can be obtained from $HOME and/or the current working
directory; however only the $HOME case was tested before, because
during tests Stow was being run from $HOME.
So switch $TEST_DIR to an absolute path, create a new run_from/
subdirectory, and chdir to that before invoking any Stow code. This
allows us to test the behaviour of .stowrc in $HOME and run_from/
separately.
Add a new expand_tilde() function that performs tilde expansion of
strings, and corresponding unit tests:
* A ~ at the beginning of a path is expanded to the user's home
directory.
* Literal '~' can be provided with '\~'
Combine this with expand_environment() in a new expand_filepath()
function which applies all (both) required expansion functions to a
string, and use that in get_config_file_options() to expand .stowrc
options.
Add more tests to check that tilde expanded in correct places, i.e.:
* expanded for --target and --dir
* not expanded for --ignore, --defer, or --override
Update documentation on stowrc files according to this functionality
change.
Fixes#14: https://github.com/aspiers/stow/issues/14
Expand environment variables used in stowrc, as requested in
https://savannah.gnu.org/bugs/?41826
This is achieved by creating a new function expand_environment() that
replaces any substring of the form '$VAR' or '${VAR}' with contents of
environment variable $VAR. Literal '$' can be given by '\$'.
N.B. The function is only applied to the --target and --dir options,
and only for options specified in .stowrc; cli options are left
untouched.
Undefined variables are expanded to the empty string, as they would be
in normal shell parameter expansion.
Unit tests added accordingly:
- Test expand_environment():
* Expand $HOME
* Expand ${HOME}
* Expand ${WITH SPACE}
* Expand '\$HOME'. Expected is '$HOME'
* Expand ${UNDEFINED}. Expected is ''.
- Test that it's applied to the correct options.
- Test that CLI options are not expanded.
Why:
* Want to add feature to stowrc parsing.
* Missing regression test for conflicting cli and stowrc options.
This change addresses the need by:
* Add missing regression tests to rc_options.t
* Two types of tests for the two types of options possible.
* For scalar options such as --target, cli arguments should overwrite
stowrc arguments.
* For options that result in a list, such as --ignore, the arguments
from cli and stowrc files should be merged.
Why:
* Planning on developing a new feature for parsing of stowrc files.
* Need a test harness that performs initialization and clean up
* Initialization: Create directory structure that allows creation of
stowrc files without worrying about squashing existing files.
* Clean up: Remove all files created during testing.
This change addresses the need by:
* Add intialization and cleanup harness in t/rc_options.t
* Define the location to write stowrc files to in $RC_FILE
* Ensures that location $RC_FILE does not already exist.
* Calls the init_test_dirs to bootstrap directory tree.
* After all tests are run, removes $RC_FILE and the testing
directory tree.
* Add basic test of stowrc parsing to t/rc_options.t
* Provides a template of how to create and test a stowrc file.
* Newly created t/rc_options.t file added to MANIFEST
Why:
* Want to add a new feature to parsing of stowrc files.
* Need ability to write .stowrc files for testing without risk of
squashing existing files.
This change addresses the need by:
* Reusing logic in init_test_dirs
* init_test_dirs already creates new directory structure and overwrites
$HOME to point into /tmp.
* This commit changes init_test_dirs to point $HOME at the newly created
directory structure ($OUT_DIR) instead of /tmp.
* Grants ability to write .stowrc to $HOME without fear.
* Pointing $HOME at $OUT_DIR instead of /tmp also makes cleanup easier.
* Remove $OUT_DIR vs remove specific files in /tmp.
Unlike the other tests, this actually treats stow(1) as a black box
script, running it directly rather than require-ing it as a library.
This allows us to check things like the exit codes returned.