Skip to content

Preserve caller cwd in macOS shebang launcher (#293)#311

Open
dwoz wants to merge 1 commit into
mainfrom
fix-issue-293-macos-shebang-cwd
Open

Preserve caller cwd in macOS shebang launcher (#293)#311
dwoz wants to merge 1 commit into
mainfrom
fix-issue-293-macos-shebang-cwd

Conversation

@dwoz

@dwoz dwoz commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

The macOS polyglot launcher template (SHEBANG_TPL_MACOS) emulated readlink -f with a cd/readlink loop because macOS's /usr/bin/readlink predates the -f flag. All those cd's happened in the launcher's own shell, so by the time it exec'd Python the caller's working directory had been replaced with the install dir. Every entry point relenv produces for macOS (salt, salt-ssh, salt-call, salt-run, ...) inherited this cwd, which broke anything resolving relative paths against os.getcwd(): Saltfile auto-discovery from the current directory, config_dir: ./..., --config-dir=./..., root_dir: ./, ssh_pre_flight: ./..., and so on.

Confine the symlink-resolution loop to a subshell — its cd's no longer leak into the parent — and emit the resolved physical path on its stdout so the parent can capture it. Python is exec'd from the launcher's untouched cwd.

Verified against the original reproducer in the issue on a real macOS (Sonoma) box: before, the launcher's Python sees the install dir as cwd; after, it sees the directory the user invoked the command from.

Linux-side template (SHEBANG_TPL_LINUX) is unaffected — it uses readlink -f directly and never cd's.

Adds tests/test_common.py::test_macos_shebang_preserves_caller_cwd, a sh-driven integration test that reproduces the symlinked Salt-on-macOS deployment shape (/usr/local/bin/ -> /opt//bin/) and asserts the launcher's Python sees the caller's cwd. The test runs on any POSIX runner — the launcher polyglot is portable, so a Linux runner catches the regression too. Confirmed the test fails on the pre-fix template and passes on the new one.

Fixes #293

The macOS polyglot launcher template (SHEBANG_TPL_MACOS) emulated
`readlink -f` with a cd/readlink loop because macOS's /usr/bin/readlink
predates the -f flag.  All those cd's happened in the launcher's own
shell, so by the time it `exec`'d Python the caller's working directory
had been replaced with the install dir.  Every entry point relenv
produces for macOS (salt, salt-ssh, salt-call, salt-run, ...) inherited
this cwd, which broke anything resolving relative paths against
os.getcwd(): Saltfile auto-discovery from the current directory,
config_dir: ./..., --config-dir=./..., root_dir: ./, ssh_pre_flight:
./..., and so on.

Confine the symlink-resolution loop to a subshell — its cd's no longer
leak into the parent — and emit the resolved physical path on its
stdout so the parent can capture it.  Python is exec'd from the
launcher's untouched cwd.

Verified against the original reproducer in the issue on a real macOS
(Sonoma) box: before, the launcher's Python sees the install dir as
cwd; after, it sees the directory the user invoked the command from.

Linux-side template (SHEBANG_TPL_LINUX) is unaffected — it uses
`readlink -f` directly and never cd's.

Adds tests/test_common.py::test_macos_shebang_preserves_caller_cwd, a
sh-driven integration test that reproduces the symlinked Salt-on-macOS
deployment shape (/usr/local/bin/<cmd> -> /opt/<app>/bin/<cmd>) and
asserts the launcher's Python sees the caller's cwd.  The test runs on
any POSIX runner — the launcher polyglot is portable, so a Linux
runner catches the regression too.  Confirmed the test fails on the
pre-fix template and passes on the new one.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

macOS shebang launcher (SHEBANG_TPL_MACOS) cd's away from caller cwd before exec'ing python, breaking relative paths

1 participant