Skip to content

Instantly share code, notes, and snippets.

@XaydBayeck
Last active November 19, 2022 07:30
Show Gist options
  • Save XaydBayeck/5648218a2f2d7372693d67f3cfd25901 to your computer and use it in GitHub Desktop.
Save XaydBayeck/5648218a2f2d7372693d67f3cfd25901 to your computer and use it in GitHub Desktop.
Run bash file and inherit environment parameters for nushell.
"""
To be used with a companion fish function like this:
function refish
set -l _x (python /tmp/bass.py source ~/.nvm/nvim.sh ';' nvm use iojs); source $_x; and rm -f $_x
end
"""
from __future__ import print_function
import json
import os
import signal
import subprocess
import sys
import traceback
BASH = 'bash'
FISH_READONLY = [
'PWD', 'SHLVL', 'history', 'pipestatus', 'status', 'version',
'FISH_VERSION', 'fish_pid', 'hostname', '_', 'fish_private_mode'
]
IGNORED = [
'PS1', 'XPC_SERVICE_NAME'
]
def ignored(name):
if name == 'PWD': # this is read only, but has special handling
return False
# ignore other read only variables
if name in FISH_READONLY:
return True
if name in IGNORED or name.startswith("BASH_FUNC"):
return True
return False
def escape(string):
# use json.dumps to reliably escape quotes and backslashes
return json.dumps(string).replace(r'$', r'\$')
def escape_identifier(word):
return escape(word.replace('?', '\\?'))
def comment(string):
return '\n'.join(['# ' + line for line in string.split('\n')])
def gen_script():
# Use the following instead of /usr/bin/env to read environment so we can
# deal with multi-line environment variables (and other odd cases).
env_reader = "%s -c 'import os,json; print(json.dumps({k:v for k,v in os.environ.items()}))'" % (sys.executable)
args = [BASH, '-c', env_reader]
output = subprocess.check_output(args, universal_newlines=True)
old_env = output.strip()
pipe_r, pipe_w = os.pipe()
if sys.version_info >= (3, 4):
os.set_inheritable(pipe_w, True)
command = 'eval $1 && ({}; alias) >&{}'.format(
env_reader,
pipe_w
)
args = [BASH, '-c', command, 'bass', ' '.join(sys.argv[1:])]
p = subprocess.Popen(args, universal_newlines=True, close_fds=False)
os.close(pipe_w)
with os.fdopen(pipe_r) as f:
new_env = f.readline()
alias_str = f.read()
if p.wait() != 0:
raise subprocess.CalledProcessError(
returncode=p.returncode,
cmd=' '.join(sys.argv[1:]),
output=new_env + alias_str
)
new_env = new_env.strip()
old_env = json.loads(old_env)
new_env = json.loads(new_env)
script_lines = []
for k, v in new_env.items():
if ignored(k):
continue
v1 = old_env.get(k)
if not v1:
# script_lines.append(comment('adding %s=%s' % (k, v)))
pass
elif v1 != v:
# script_lines.append(comment('updating %s=%s -> %s' % (k, v1, v)))
# process special variables
if k == 'PWD':
script_lines.append('cd %s' % escape(v))
continue
else:
continue
if k == 'PATH':
# value = '"($env.PATH | prepend [' + ', '.join([escape(directory)
# for directory in v.split(':')]) + '])"'
value = '['+', '.join([escape(directory) for directory in v.split(':')])+']'
else:
value = escape(v)
script_lines.append('"%s" : %s' % (k, value))
# TODO: really remove repeate env parameters
for var in set(old_env.keys()) - set(new_env.keys()):
script_lines.append(comment('removing %s' % var))
script_lines.append('hide %s' % var)
script = '"env_params" : {\n' + ',\n'.join(script_lines) + "\n}"
alias_lines = []
for line in alias_str.splitlines():
_, rest = line.split(None, 1)
k, v = rest.split("=", 1)
alias_lines.append( '{"nick" : ' + escape_identifier(k) + ' , "aliased" : ' + v.replace("'", '"') + '}')
alias = '"alias" : [\n' + ',\n'.join(alias_lines) + "\n]"
return "{\n" + script + ',\n' + alias + "\n}"
script_file = os.fdopen(1, 'w')
if not sys.argv[1:]:
# print('__bass_usage', file=script_file, end='')
print('__bass_usage', file=os.fdopen(2, 'w'), end='')
sys.exit(0)
try:
script = gen_script()
except subprocess.CalledProcessError as e:
sys.exit(e.returncode)
except Exception:
print('Bass internal error!', file=sys.stderr)
raise # traceback will output to stderr
except KeyboardInterrupt:
signal.signal(signal.SIGINT, signal.SIG_DFL)
os.kill(os.getpid(), signal.SIGINT)
else:
script_file.write(script)
export def-env bass [
--debug (-d)
...commands:string
] {
let bash_args = $commands
# let script_file = (mktemp)
touch bass_temp.nu
do -c {
# TODO: Add a cheker check if python3 is in PATH
python3 -sS ./__bass.py $bash_args | save -r bass_temp.json
}
if $debug { bat bass_temp.json }
let temp = (open bass_temp.json)
let new_path = ($env.PATH | prepend $temp.env_params.PATH)
let temp = ($temp.env_params | update PATH new_path)
load-env $temp.env_params
rm bass_temp.json
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment