Last active
May 31, 2021 02:52
-
-
Save sebastien/18a7eb71fdd34f6a0f825e69f9461d01 to your computer and use it in GitHub Desktop.
Python script that converts Nix's profile bash script to Fish
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# Updated: 2018-10-17 | |
import re, sys, os | |
""" | |
Converts the Nix profile SH script to a Fish-compatible profile using a | |
simple line-by-line replace algorithm. | |
""" | |
# Regular expressions for things we need to rewrite | |
RE_TEST = re.compile("\[([^\]]+)\]") | |
RE_EXPORT = re.compile("^(\s*)export ([A-Z_]+)\s*=(.+)$") | |
RE_SET = re.compile("^(\s*)([A-Za-z_]+)\s*=(.+)$") | |
RE_IF_NOT = re.compile("(\s*)if\s+!\s+(.*)$") | |
RE_FI = re.compile("(^\s*|\s+)fi(\s+|\s*$)") | |
RE_UNSET = re.compile("^(\s*)unset(\s*)(.+)$") | |
RE_BRACKET = re.compile("\${([A-Z_]+)(:-)?\}") | |
RE_WHITESPACE = re.compile("^[\s\n]+") | |
# Simple word-for-word rewrites | |
REWRITE = { | |
"elif " : "else if ", | |
"! test" : "test !", | |
"; then" : ";", | |
" && " : "; and ", | |
"$(" : "(", | |
} | |
def unwrap( text ): | |
"""Takes a line like `"abc:def"` and returns `abc def`. This is | |
useful for converting sh-like path lists to fish path lists.""" | |
text = text.strip() | |
if len(text) > 2 and text[0] == text[-1] and text[0] == '"': | |
return " ".join(text[1:-1].split(":")) | |
else: | |
return text | |
def re_replace( regexp, text, functor ): | |
"""Replaces all matching instances of `regexp` in `text` by passing the | |
match to the given `functor`.""" | |
r = [] | |
o = 0 | |
for m in regexp.finditer(text): | |
r.append(text[o:m.start()]) | |
r.append(functor(m)) | |
o = m.end() | |
r.append(text[o:]) | |
return "".join(r) | |
def process_line( line ): | |
"""Processes the given line of Shell code, applying both the regexes | |
and word substitutions.""" | |
nix_path = "${NIX_PATH:+$NIX_PATH:}" | |
if nix_path in line: | |
# Here we have a special case which is quite annoying, where a Bash | |
# substitution is used with a default value. | |
line = "\n".join(( | |
'if test -z "$NIX_PATH";', | |
' ' + process_line(line.replace(nix_path, "")), | |
'else', | |
' ' + process_line(line.replace(nix_path, "$NIX_PATH:")), | |
'end', | |
)) | |
return line | |
# Fish doesn't allow using return outside of a function | |
if '"${__ETC_PROFILE_NIX_SOURCED:-}"' in line: | |
line = line.replace('return', 'exit 0') | |
# Fish is a little bit annoying with shell expressions in strings. | |
# "(id -u)" is not like "$(id -u)" in bash, but should be ""(id -u)"" | |
l = RE_WHITESPACE.sub("", line) | |
if l.startswith("echo"): | |
line = line.replace("$(", '"(').replace(")", ')"').replace('""','') | |
elif l.startswith("if"): | |
line = line.replace('"$(', "(").replace(')"', ")") | |
elif l.startswith("__savedpath="): | |
# This is a special handling of the initial path saving. Fish complains | |
# about non-existing entries in PATH, so we need to filter them first. | |
return " for d in $PATH; if test -e $d; set __savedpath $__savedpath $d; end; end;" | |
m = RE_IF_NOT.match(line) | |
if m: | |
pre = m.group(1) | |
cmd = m.group(2) | |
return "\n".join(( | |
"{0}{1}".format(pre, process_line(cmd)), | |
"{0}if test ! $status -eq 0;".format(pre) | |
)) | |
line = re_replace(RE_TEST, line, lambda m:"test{0}".format(m.group(1))) | |
line = re_replace(RE_EXPORT, line, lambda m:"{0}set -xg {1} {2}".format(m.group(1), m.group(2), unwrap(m.group(3)))) | |
line = re_replace(RE_SET, line, lambda m:"{0}set -g {1} {2}".format(m.group(1), m.group(2), unwrap(m.group(3)))) | |
line = re_replace(RE_UNSET, line, lambda m:"{0}{1}".format(m.group(1), "; ".join("set -e " + _ for _ in m.group(3).split()))) | |
line = re_replace(RE_FI, line, lambda m:"{0}end{1}".format(m.group(1),m.group(2))) | |
line = re_replace(RE_BRACKET,line, lambda m:"${0}".format(m.group(1))) | |
for s,d in REWRITE.items(): line = line.replace(s,d) | |
return line | |
def process( text, origin ): | |
"""Processes the given text.""" | |
res = [ | |
"#!/usr/bin/env fish", | |
"# --8<-- [This file is automatically generated from {0}, do not edit] ---".format(origin), | |
] | |
for line in text.split("\n"): | |
res.append(process_line(line)) | |
res.append("# --8<-- EOF --- vim: readonly") | |
return "\n".join(res) | |
if __name__ == "__main__": | |
to_convert = { | |
"~/.nix-profile/etc/profile.d/nix.sh" : "~/.config/fish/conf.d/nix.fish", | |
# NOTE: We don't start nix-daemon automatically | |
"~/.nix-profile/etc/profile.d/nix-daemon.sh":"~/.config/fish/nix-daemon.fish", | |
} | |
installed = 0 | |
for src,dst in to_convert.items(): | |
src,dst = (os.path.expanduser(_) for _ in (src,dst)) | |
if not os.path.exists(src): | |
continue | |
installed += 1 | |
with open(os.path.expanduser(src), "rt") as f: | |
t = process(f.read(), src) | |
if not os.path.exists(os.path.dirname(dst)): | |
os.makedirs(os.path.dirname(dst)) | |
with open(dst, "wt") as g: | |
g.write(t) | |
sys.stderr.write(" To use Nix from Fish: source {0}\n".format(dst)) | |
if not installed: | |
sys.stderr.write("[!] Nix does not seem to be installed, expecting {0}\n".format(", ".join(to_convert.keys()))) | |
sys.exit(-1) | |
# EOF - vim: ts=4 sw=4 noet |
There is a small bug in this script. It reverses the order of PATH variable. Change line 74 to
return " for d in $PATH; if test -e $d; set __savedpath $__savedpath $d; end; end;"
@sebastien After version 2.3.0 of Fish anything located in ~/.config/fish/conf.d/
will auto load. Copying the generated nix.fish
file in that directory loads it right up.
I updated the script with all of the above changes. Thanks!
I used this, and executing the generated script fails:
~/.config/fish/nix-daemon.fish (line 59): Missing end to balance this if statement
if test ! -z "$NIX_SSL_CERT_FILE" ;
^
from sourcing file ~/.config/fish/nix-daemon.fish
I used this script to generate fish script and I got this error:
~/.config/fish/conf.d/nix.fish (line 39): ${ is not a valid variable in fish.
if test -n "${MANPATH-}" ;
I fixed it by changing ${MANPATH-}
to $MANPATH
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Version that supports
nix-daemon.s
: https://gist.github.com/princed/8ed52eeb8d665b00f6f8330132863157