-
Notifications
You must be signed in to change notification settings - Fork 75
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
readlink has no -e option on busybox/alpine, realpath has no options at all #49
Comments
Maybe just: resolve_link() {
if type -p realpath >/dev/null; then
realpath "$1"
else
readlink -f "$1"
fi
} I didn't update resolve link in the binary file because it's copied over from rbenv and if it matches their use case, it should match ours. |
Yep I saw that while experimenting, it shouldn't be changed indeed 🙂 I like defaulting to readlink. I can open a PR if you want! We only need to update the function in basher-link and basher-link.bats |
FYI, bashup/realpaths has a well-tested ultraportable microlibrary for handling symlink resolution that requires only |
It's up to @juanibiapina. Using your library can be a good idea for portability reasons. Though we would have to decide how we use it (copy/paste in two files? share code in one file and source it from elsewhere?) and I'd leave that design decision to @juanibiapina too 🙂 Also, could be nice to update the Travis config to test on different distributions! |
For now I'd say to go with the simpler solution. If we ever need even more portability, we can revisit this decision. The major difficulty I see is how to include dependent code in basher itself without complicating the installation process. I've tried this before and couldn't find a good way. We could always copy the code, but then this create other kinds of problems. |
I've got a solution for that, too. 😉 The way my projects do dependencies like that, is that during development, dependencies are checked out in a project-local, git-ignored basher directory ( The bashup/.devkit toolset makes this whole process easy to do; in fact it's a big part of what it was made for. A project using .devkit that needed to add realpaths would just add this to # BUILD_DEPS is a :-separated list, can use '@ref' to pin a branch or tag
# These projects' files can then be accessed under .deps/, and their BINS under .deps/bin
BUILD_DEPS=bashup/realpaths And this to the end of # Copy updated dependency to libexec
on build cp .deps/bin/bashup.realpaths libexec/ And then anybody who checked out the package could update the dependency by running Of course, you don't need .devkit in order to do all that; you can just add stuff to your makefile to do the same things! The real point is just that this overall approach lets you bundle dependencies in git, without needing to copy-and-paste the updates, and without needing |
I like some of the ideas there. I'll keep that in mind! |
@juanibiapina What about falling back to perl for at least the osx (or any system with perl) case... resolve_link() {
if type -p realpath >/dev/null; then
realpath "$1"
else
directory="$(readlink -f "$1" 2>&1)"
if [ "$?" -ne "0" ] && [ "${directory#*readlink: illegal option -- f*}" != "$directory" ]; then
if type -p perl >/dev/null; then
perl -MCwd -le 'print Cwd::abs_path shift' "$1";
else
echo "$directory"
fi
else
echo "$directory"
fi
fi
} Might even be able to just try perl if the |
Well, |
@pawamoy but in that case, |
True, but if you're writing that much code you might as well use something like: resolve_link() {
local target srcdir resolved
while [[ -L "$1" ]] && target="$(readlink -- "$1")"; do
srcdir="$(dirname "$1")"
# Resolve relative to symlink's directory
[[ $srcdir != . && $target != /* ]] && resolved=$srcdir/$target || resolved=$target
# Break out if we found a symlink loop
for target; do [[ $resolved != "$target" ]] || break 2; done
# Add to the loop-detect list and tail-recurse
set -- "$resolved" "$@"
done
cd "$(dirname "$1")" >/dev/null
echo "$PWD/$(basename "$1")"
} which isn't much bigger and only needs readlink. |
Nice code. In this case you would need a lot more tests. An alternative to add OS X support easily is to check for |
The code I posted is a rip-off of Conversely, checking for a bunch of different alternatives to readlink means tests need to be run on those OSes, but plain |
True. That said, the badger tests should be run on OS X, alpine, etc. in Travis anyway.
|
BTW, the resolve_link() {
if type -p realpath >/dev/null; then
realpath "$1"
elif type -p greadlink >/dev/null; then
greadlink -f "$1"
else
readlink -f "$1"
fi
} |
Technically, that's homebrew support, not actually OS X support. When used on OS X without greadlink, the above code fails and then the code calling resolve_link silently breaks in some fashion. The slightly-longer code to emulate Currently, basher has three separate partial implementations of functionality from realpaths, and none of them are directly tested. It would probably make more sense to embed realpaths in the main executable and then use the functions elsewhere, e.g. Come to think of it, looking more closely at where symlink resolution is used in For example, one hosting provider I had would sometimes move my shell account to a different box, with different physical hard drive names, and then every tool I used that blindly unpacked symlinks quit working (I'm looking at you, Resilio Sync!) because it was pointing to physical directories that no longer existed. If the tool just took what I gave it in the first place (a virtual path), that problem wouldn't have happened. That would basically leave the following two uses for symlink resolution:
For the former usecase, the relevant bits of realpath can be embedded, and for the latter, they can be sourced. But with it embedded in the main basher executable, the functions could be made available to child processes via At this point, though, we're just going back and forth so I'm going to stop here. It's up to the project devs how to resolve this (no pun intended). 😉 |
Thanks for all comments and discussions. This is really great. @pjeby great summary! I'll continue advocating for less code, and probably no copy and paste, as long as the use cases are very simple. It hasn't so far been a requirement, so I'm unwilling to introduce complexity. If rbenv can make do, so should we. |
@juanibiapina @pawamoy Everything you guys are saying makes sense, but at the moment an entire platform is unusable without forking basher, even though there is a simple change that can make it possible to support that platform. I'd advocate that adding @juanibiapina Given the reference to rbenv, it (and all derivatives) work on OS X because of code like READLINK=$(type -p greadlink readlink | head -1). If basher does something similar, OS X is capable of working today. @juanibiapina @pawamoy At the moment, alpine support for basher can be added by installing coreutils. So, just like OS X, support for basher is possible right now. Even better than OS X, this can be done with zero code changes to basher. I would argue that the overhead of adding coreutils in minimal enough that until a better solution is agreed upon, alpine has the capability of running basher Before installing coreutils$ docker ps -s
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
7c8ae7a23230 alpine:latest "/bin/sh" 18 seconds ago Up 17 seconds alpine 0B (virtual 5.53MB) After installing coreutils
|
@kadaan I think we started wrong. It wasn't clear to me that Basher is not working (or partially not working) on OSX. Maybe you could open a new issue describing the problem, so we can start fresh again? If I understood correctly, the current Also @pjeby made a really interesting point, asking if it is even needed to resolve a symlink? At first when I opened a PR to change Let me know if I got things wrong of course! (also @kadaan thanks for the note about coreutils, I didn't think of that and it's actually very handy, I would totally be OK to install coreutils if needed in my Docker images. I would prefer that it works by default everywhere of course, including OSX 🙂 ) |
It doesn't need a "canonical" path even in those cases, just an absolute path, which can be easily had via As for copy-paste, basher already has copy-pasted this very function into three different files. So it would probably be be a good idea to have less copy-pasting. An easy way to do that is to put the function in a file, say #!/usr/bin/env bash
follow_link() {
# ...
}
abs_dirname() {
# ...
}
if [[ $0 == "$BASH_SOURCE" ]]; then
bin_path="$(abs_dirname "$0")"
export PATH="${bin_path}:${PATH}"
source "${bin_path}/basher" "$@"
fi This file can now be sourced by the tests and basher-link. And by making it the symlink target of Voila, no more copy-paste, regardless of which of the many proposed versions of |
I've now created a proof-of-concept PR for this approach (#60), which eliminates the existing code duplication, uses absolute paths for the base dir of The PR actually reduces the total lines of non-vendor code in basher, as well as eliminating any dependency on platform-specific tools like realpath and greadlink for symlink resolution. Hopefully, having a concrete PR will aid in a prompt resolution (no pun intended 😉) to the issue of cross-platform symlink resolution. If anyone would like to try it out for themselves, you can get a copy via: git clone -b realpaths https://github.com/bashup/basher Please let me know if you find any issues not covered by the automated tests. |
@pjeby Works great when I run it on OS X. I do get this logging on the first run of $ make
echo "#!/usr/bin/env bash" >libexec/basher.realpath
echo "# This file is automatically generated by makefile; do not edit!" >>libexec/basher.realpath
echo >>libexec/basher.realpath
cat vendor/bashup/realpaths/realpaths libexec/basher.stub >>libexec/basher.realpath
chmod +x libexec/basher.realpath
bats tests
✓ without arguments prints usage
... Seems like this is new. What is the purpose? |
It's rebuilding the bootstrap script that's used to find the real location of the |
@kadaan I use basher o OSX, why do you say it is not supported? |
@juanibiapina Because without installing coreutils and overwriting the default install of readlink the basher-link commands don't work. |
got it. So coreutils has always been an implicit requirement it seems. It's probably my fault because I never have a mac without it. |
Cool. The issues might be that coreutils is required, but not documented, and that it doesn't look for greadlink, expecting that coreutils was installed over the existing tools. |
It's only been an implicit requirement for If the coreutils requirement is documented, then the stuff in the main basher script can go away too, since it's emulating `dirname "$(realpath "$1")" and could just be replaced with using that directly. However, since there's already emulation of part of coreutils in basher, I don't see a point to adding the requirement; to me it makes more sense to just use a better emulation, and to do it without copying the code into each subcommand. |
The
resolve_link
functions do not work on Alpine / Busybox (Alpine uses Busybox), becauseBusybox's
realpath
has no option at all and itsreadlink
just has the following (not-e
):So I guess we'll have complexify the function a bit. Also I noticed you did not update the
resolve_link
function inbasher
, it still usesgreadlink
.Here is a first draft:
The text was updated successfully, but these errors were encountered: