Skip to content

Fix #1011 nixos doesnt have /bin/bash #1037

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion invoke/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ def global_defaults() -> Dict[str, Any]:
# TODO: consider an automatic fallback to /bin/sh for systems lacking
# /bin/bash; however users may configure run.shell quite easily, so...
else:
shell = "/bin/bash"
shell = "bash"

return {
# TODO: we document 'debug' but it's not truly implemented outside
Expand Down
7 changes: 3 additions & 4 deletions invoke/runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -1327,12 +1327,11 @@ def start(self, command: str, shell: str, env: Dict[str, Any]) -> None:
# TODO: make subroutine?
winsize = struct.pack("HHHH", rows, cols, 0, 0)
fcntl.ioctl(sys.stdout.fileno(), termios.TIOCSWINSZ, winsize)
# Use execve for bare-minimum "exec w/ variable # args + env"
# behavior. No need for the 'p' (use PATH to find executable)
# for now.
# Use execvpe for bare-minimum "exec w/ variable # args + env"
# behavior.
# NOTE: stdlib subprocess (actually its posix flavor, which is
# written in C) uses either execve or execv, depending.
os.execve(shell, [shell, "-c", command], env)
os.execvpe(shell, [shell, "-c", command], env)
else:
self.process = Popen(
command,
Expand Down
5 changes: 5 additions & 0 deletions sites/www/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
Changelog
=========

- :bug:`1011`: On some systems, the default configuration for the shell
to use provoked a crash, due to the shell binary being in a different
path. A fix has been introduced to switch to execution with `$PATH`
binary search (execvpe). Previous custom shell configurations are unaffected.
Thanks to Lou Lecrivain for the patch.
- :release:`2.2.0 <2023-07-12>`
- :feature:`-` Remove the somewhat inaccurate subclass requirement around
`~invoke.config.Config`'s ``.clone(into=...)`` constructor call. It was
Expand Down
2 changes: 1 addition & 1 deletion tests/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ def fakeread(fileno, count):
assert ioctl.call_args_list[0][0][1] == termios.TIOCGWINSZ
assert ioctl.call_args_list[1][0][1] == termios.TIOCSWINSZ
if not skip_asserts:
for name in ("execve", "waitpid"):
for name in ("execvpe", "waitpid"):
assert getattr(os, name).called
# Ensure at least one of the exit status getters was called
assert os.WEXITSTATUS.called or os.WTERMSIG.called
Expand Down
2 changes: 1 addition & 1 deletion tests/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def basic_settings(self):
"out_stream": None,
"pty": False,
"replace_env": False,
"shell": "/bin/bash",
"shell": "bash",
"warn": False,
"watchers": [],
},
Expand Down
8 changes: 4 additions & 4 deletions tests/runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def _expect_platform_shell(shell):
if WINDOWS:
assert shell.endswith("cmd.exe")
else:
assert shell == "/bin/bash"
assert shell == "bash"


def _make_tcattrs(cc_is_ints=True, echo=False):
Expand Down Expand Up @@ -1655,7 +1655,7 @@ def defaults_to_bash_or_cmdexe_when_pty_True(self, mock_os):
# NOTE: yea, windows can't run pty is true, but this is really
# testing config behavior, so...meh
self._run(_, pty=True)
_expect_platform_shell(mock_os.execve.call_args_list[0][0][0])
_expect_platform_shell(mock_os.execvpe.call_args_list[0][0][0])

@mock_subprocess(insert_Popen=True)
def defaults_to_bash_or_cmdexe_when_pty_False(self, mock_Popen):
Expand All @@ -1667,7 +1667,7 @@ def defaults_to_bash_or_cmdexe_when_pty_False(self, mock_Popen):
@mock_pty(insert_os=True)
def may_be_overridden_when_pty_True(self, mock_os):
self._run(_, pty=True, shell="/bin/zsh")
assert mock_os.execve.call_args_list[0][0][0] == "/bin/zsh"
assert mock_os.execvpe.call_args_list[0][0][0] == "/bin/zsh"

@mock_subprocess(insert_Popen=True)
def may_be_overridden_when_pty_False(self, mock_Popen):
Expand All @@ -1690,7 +1690,7 @@ def uses_execve_for_pty_True(self, mock_os):
type(mock_os).environ = {"OTHERVAR": "OTHERVAL"}
self._run(_, pty=True, env={"FOO": "BAR"})
expected = {"OTHERVAR": "OTHERVAL", "FOO": "BAR"}
env = mock_os.execve.call_args_list[0][0][2]
env = mock_os.execvpe.call_args_list[0][0][2]
assert env == expected

class close_proc_stdin:
Expand Down