diff options
| author | Douglas B. Rumbaugh <doug@douglasrumbaugh.com> | 2026-06-06 12:27:03 -0400 |
|---|---|---|
| committer | Douglas B. Rumbaugh <doug@douglasrumbaugh.com> | 2026-06-06 12:27:03 -0400 |
| commit | 0ecfe53b2d271133fac36de11ecfc0f7e47840f0 (patch) | |
| tree | 3ee8b5188936e350e15ff851b07a33031d366389 /ves.fish | |
| parent | 04b385284a8559bde3df51bab950784a0fd28cfd (diff) | |
| download | sh-ves-0ecfe53b2d271133fac36de11ecfc0f7e47840f0.tar.gz | |
Initial version complete
I dusted this off after years and had Claude finish
it for me.
caveat emptor: this is largely (though not entirely)
LLM generated as of this commit
Diffstat (limited to 'ves.fish')
| -rw-r--r-- | ves.fish | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/ves.fish b/ves.fish new file mode 100644 index 0000000..3315f61 --- /dev/null +++ b/ves.fish @@ -0,0 +1,104 @@ +# ves.fish +# +# fish shell shim for sh-ves. Runs each ves command in a POSIX sh subshell +# against the real implementation, then diffs the subshell's resulting +# environment against fish's and replays the changes with set -gx / set -e. +# The activation save-state is carried in exported SHVES_SAVED_* variables, +# so it survives between subshell invocations. +# +# Install this file to ~/.config/fish/functions/ves.fish (make install does +# this automatically when fish is present). + +function ves --description 'sh-ves: Bourne Shell Virtual Environment System' + if not set -q SHVES_SCRIPTS_DIR + set -gx SHVES_SCRIPTS_DIR $HOME/.local/bin/ves_scripts + end + + if not test -f "$SHVES_SCRIPTS_DIR/ves-init.sh" + printf "ERROR: sh-ves scripts not found in [%s]. Is sh-ves installed?\n" "$SHVES_SCRIPTS_DIR" >&2 + return 1 + end + + set -l dump (mktemp) + + # Resolve env(1) to an absolute path up front: the command may rewrite + # PATH inside the subshell, which would otherwise make the dump fail. + set -l envbin (command -v env) + if test -z "$envbin" + set envbin /usr/bin/env + end + + # Run the real command, then dump the subshell's final environment + # NUL-delimited (values may contain newlines) for replay into fish. + VES_ENV_DUMP=$dump VES_ENV_BIN=$envbin sh -c ' + . "$SHVES_SCRIPTS_DIR"/ves-init.sh + ves "$@" + _shves_status=$? + "$VES_ENV_BIN" -0 > "$VES_ENV_DUMP" + exit $_shves_status + ' ves $argv + set -l ret $status + + # An empty dump means the subshell could not write its environment at + # all; replaying it would wrongly erase every exported variable. + if not test -s $dump + printf "ERROR: failed to capture subshell environment, shell state unchanged.\n" >&2 + rm -f $dump + return 1 + end + + # Variables managed by the wrapper machinery itself, plus per-process + # bookkeeping that should never be replayed into the parent shell. + set -l skip VES_ENV_DUMP VES_ENV_BIN PWD OLDPWD SHLVL _ + + # fish list-style path variables: replay these split on ':' so fish + # keeps treating them as lists. + set -l pathvars PATH CDPATH MANPATH + + set -l seen + for entry in (string split0 < $dump) + set -l kv (string split -m1 = -- $entry) + set -l name $kv[1] + set -l value $kv[2] + + # entries that are not well-formed variable names (e.g. exported + # multiline values' continuation, or odd env entries) are skipped + if not string match -qr '^[a-zA-Z_][a-zA-Z0-9_]*$' -- $name + continue + end + if contains -- $name $skip + continue + end + + set -a seen $name + + if contains -- $name $pathvars + set -l new (string split : -- $value) + if not set -q $name + set -gx $name $new + else if test "$new" != "$$name" + set -gx $name $new + end + else + if not set -q $name + set -gx $name $value + else if test "$value" != "$$name" + set -gx $name $value + end + end + end + + # Anything fish exports that vanished from the subshell environment + # was unset by the command (e.g. by ves deactivate). + for name in (set -nx) + if contains -- $name $skip + continue + end + if not contains -- $name $seen + set -e $name + end + end + + rm -f $dump + return $ret +end |