Skip to content

macOS: make docker bind mounts asymmetric so macOS hosts inject correctly - #3947

Open
moflwi wants to merge 4 commits into
claude/add-macos-supportfrom
fix/macos-docker-mount-paths
Open

macOS: make docker bind mounts asymmetric so macOS hosts inject correctly#3947
moflwi wants to merge 4 commits into
claude/add-macos-supportfrom
fix/macos-docker-mount-paths

Conversation

@moflwi

@moflwi moflwi commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

`agent.instrumentation.docker.sh` and `agent.instrumentation.github.aw.docker.sh` bind-mount the host's otel install into containers with identical source and target paths (`source=/usr/share/opentelemetry_shell, target=/usr/share/opentelemetry_shell`). Containers are always Linux (even under Docker Desktop on a Mac), so the target must stay the Linux convention path regardless of host OS — but on a Darwin host the source is now `/usr/local/share/opentelemetry_shell` (per #3942). These need to be asymmetric: source resolved via `$_otel_shell_home`/`$_otel_shell_bin_home` (host-OS-aware), target always the literal Linux path.

Depends on #3942 (base branch).

Test plan

  • `bash -n` on both touched files (done locally)
  • Confirmed via security review: the new source paths are derived solely from `uname -s`, not from any attacker-influenceable input — not a new way to control what gets bind-mounted into a container

🤖 Generated with Claude Code

Moritz Witti and others added 4 commits August 8, 2026 10:31
/usr is on the SIP-sealed system volume on modern macOS, but the codebase
hardcoded /usr/share/opentelemetry_shell and /usr/bin/... as absolute paths
for cross-file sourcing. Make the bootstrap (agent.sh, api.sh), curl/wget/
netcat instrumentation, the CI/GH-Action installers, and the test suite's
package entrypoint resolve to /usr/local/... on Darwin instead, leaving
Linux behavior untouched.

Also: drop the dead macos-13 runner, use `ps` instead of the unreliable
$SHELL fallback for shell detection, fix the Homebrew formula's post_install
chicken-and-egg bug and deprecated python@3.9 dependency, template the
formula's version/sha256 in build-brew, and restore the perl fallback that
was dropped when the timeout fallback was added.
Extracting the whole tarball with --strip-components=2 into /usr/local also
stripped the opt/ prefix, landing opt/opentelemetry_shell/requirements.txt at
/usr/local/opentelemetry_shell/requirements.txt. The subsequent
`pip3 install -r /opt/opentelemetry_shell/requirements.txt` would then fail,
and api.sh looks for the SDK venv at /opt/opentelemetry_shell/venv too.

/opt is not SIP-sealed (Homebrew itself lives at /opt/homebrew), so only the
usr subtree needs relocating. Extract the two subtrees separately and create
the venv, matching what the debian postinst does.
find -executable is a GNU extension. BSD find on macOS rejects it with
"unknown primary or operator", and _otel_list_path_executables redirects
stderr to /dev/null and swallows the exit code with `|| true`, so the
failure was silent: the function returned nothing, _otel_list_path_commands
returned nothing, and the auto-instrumentation loop had no commands to
instrument. macOS would have appeared to run while instrumenting nothing.

Select the predicate once at load time, using -perm +111 on Darwin, the
same conditional-definition pattern already used elsewhere in the codebase.
Linux keeps -executable and is unaffected.
The docker instrumentation mounts the host's opentelemetry-shell install into
the container using identical source and target paths. That assumption breaks
on macOS: /usr is SIP-sealed, so the package installs to /usr/local/share/
opentelemetry_shell and /usr/local/bin/otel*.sh instead. Mirroring the host
path into the container would both mount from a non-existent source and place
the files where the container does not look for them.

Containers are always Linux, even when the docker host is a Mac, so source and
target have to be decoupled:
- source (host side) is now OS aware: $_otel_shell_home for the share
  directory, and the new $_otel_shell_bin_home (/usr/local/bin on Darwin,
  /usr/bin elsewhere) for the launcher scripts.
- target (container side) always stays on the Linux convention:
  /usr/share/opentelemetry_shell and /usr/bin/<basename>.

/opt/opentelemetry_shell is unchanged, it lives at the same path on both
platforms.

Also add _otel_find_executables to api.sh, because find -executable is a GNU
extension that BSD find (macOS) does not have; there the equivalent is
-perm +111. It follows the same conditional definition pattern already used
for _otel_escape_arg_format.

On Linux $_otel_shell_home and $_otel_shell_bin_home resolve to the previous
hardcoded paths and the find invocation is unchanged, so the emitted docker
arguments and compose volumes are identical to before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@plengauer

Copy link
Copy Markdown
Owner

Hello, thanks for contributing for the first time!

@moflwi moflwi linked an issue Aug 8, 2026 that may be closed by this pull request
@plengauer
plengauer requested a lite review from Copilot August 9, 2026 19:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Aligns host vs container path expectations so macOS installs (which use /usr/local/...) can still inject into Linux containers (which expect /usr/share/... + /usr/bin/...), and updates tests/CI to support the new layout.

Changes:

  • Introduces host-OS-aware install roots (_otel_shell_home, _otel_shell_bin_home) and uses them throughout runtime/agent scripts.
  • Makes Docker/Compose bind mounts asymmetric (host source via resolved paths, Linux target paths fixed).
  • Updates tests and GitHub Actions/Homebrew packaging paths for macOS compatibility.

Reviewed changes

Copilot reviewed 72 out of 72 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/unit/test_unit_escape_arg.sh Allow overriding API entrypoint path in tests via env var
tests/unit/test_unit_busybox_parses_otelapi.sh Allow overriding API entrypoint path for busybox syntax check
tests/sdk/test_sdk_traceparent.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_span_event.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_span_error.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_span_attribute.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_span.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_resource_attributes.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_observe_whitespaces.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_observe_stdout.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_observe_stderr.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_observe_pipes_empty.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_observe_pipes_detached_stdout.bash Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_observe_pipes.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_observe_log.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_observe_failed.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_observe_exit.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_observe.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_metric.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_sdk_init_shutdown.sh Make API entrypoint path configurable in SDK tests
tests/sdk/test_log_sideeffects.sh Make API entrypoint path configurable in SDK tests
tests/sdk/DISABLED_test_sdk_observe_pipes_detached_stdin.sh Make API entrypoint path configurable in disabled SDK test
tests/run_tests.sh Set macOS-specific test entrypoint env vars; add local fallbacks for missing tools
tests/performance/test_performance_script.sh Use configurable entrypoint for performance tests
tests/performance/test_performance_full.sh Use configurable entrypoint for performance tests
tests/integration/test_integration_debconf_second.sh Use configurable entrypoint for integration test
tests/integration/test_integration_debconf_first.sh Use configurable entrypoint for integration test
tests/integration/test_integration_debconf_arguments.bash Use configurable entrypoint for integration test
tests/integration/test_integration__which__IFS.sh Use configurable entrypoint for integration test
tests/integration/test_integration__renovate.sh Use configurable entrypoint for integration test
tests/integration/test_integration__philbot_stop__multi_tier_injection.sh Use configurable entrypoint for integration test
tests/integration/test_integration__bash_ai__shebang_vs_alias_injection.sh Use configurable entrypoint for integration test
tests/auto/wget2.sh Use configurable entrypoint for auto wrapper
tests/auto/wget.sh Use configurable entrypoint for auto wrapper
tests/auto/test_auto_traceparent.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_minimal_instrumentation.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_lifecycle.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_instrument.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection_xargs.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection_whitespaces.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection_timeout.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection_time.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection_sudo_whitespaces.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection_sudo.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection_stdin.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection_shebang.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection_parallel.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection_flock.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection_find.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection_docker.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection_c.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_injection.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_init_in_if.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_http_injection_cleanup.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_alias.sh Use configurable entrypoint for auto tests
tests/auto/test_auto_[.sh Use configurable entrypoint for auto tests
tests/auto/exec.sh Use configurable entrypoint for auto tests
tests/auto/curl_subshell.sh Use configurable entrypoint for auto wrapper
tests/auto/curl.sh Use configurable entrypoint for auto wrapper
tests/auto/DISABLED_test_auto_injection_with_script.bash Use configurable entrypoint for disabled auto test
tests/auto/DISABLED_test_auto_injection_with_path.bash Use configurable entrypoint for disabled auto test
src/usr/share/opentelemetry_shell/api.sh Add Darwin-aware install roots; fix no-/proc shell detection; make find portable; use resolved home for includes/SDK
src/usr/share/opentelemetry_shell/agent.sh Source correct api.sh on Darwin; use resolved home for instrumentation files; make find portable; adjust self-detection
src/usr/share/opentelemetry_shell/agent.instrumentation.wget.sh Use resolved home for lib paths; add otool dependency check on Darwin
src/usr/share/opentelemetry_shell/agent.instrumentation.github.aw.docker.sh Make docker-compose mounts asymmetric for macOS hosts; use resolved bin/home and fixed Linux targets
src/usr/share/opentelemetry_shell/agent.instrumentation.docker.sh Make docker --mount sources host-aware while targets remain Linux paths
src/usr/share/opentelemetry_shell/agent.instrumentation.curl.sh Use resolved home for lib paths; add otool dependency check on Darwin
src/usr/share/opentelemetry_shell/agent.injection.netcat.sh Use resolved home for injected instrumentation code path
meta/homebrew/opentelemetry-shell.rb Update Python dependency and venv creation; document macOS path caveat
actions/instrument/shared/install.sh Install tarball on macOS into /usr/local + /opt split (SIP considerations)
.github/workflows/test_shell.yml Update macOS extraction layout for SIP; remove macos-13 from matrix
.github/workflows/build.yml Generate a ready-to-use Homebrew formula artifact (version + sha256 substitution)
Suppressed comments (1)

tests/run_tests.sh:1

  • function name() is not POSIX and will fail on some shells (notably dash). Since run_tests.sh appears intended to run across different shells/distros, use POSIX function syntax (perl() { ...; }) to avoid syntax errors.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +28 to +31
sudo tar --strip-components=2 -xzf "$tarball_file" -C /usr/local usr
sudo tar --strip-components=1 -xzf "$tarball_file" -C / opt
sudo python3 -m venv /opt/opentelemetry_shell/venv
sudo /opt/opentelemetry_shell/venv/bin/pip3 install -r /opt/opentelemetry_shell/requirements.txt
Comment on lines +41 to +43
sudo mkdir -p /usr/local/bin /usr/local/share
sudo tar --strip-components=2 -xzf opentelemetry-shell_*.tar.gz -C /usr/local usr
sudo tar --strip-components=1 -xzf opentelemetry-shell_*.tar.gz -C / opt
Comment on lines +181 to +182
if \[ -d /proc ] && \[ "$(\readlink -f "$hint")" = "$(\readlink -f "/proc/$$/exe")" ]; then local _otel_hint_is_self=TRUE; else local _otel_hint_is_self=FALSE; fi
{ \[ -f "$hint" ] && \[ "$_otel_hint_is_self" = FALSE ] && \[ "$(\readlink -f "$hint")" != "$_otel_shell_home"/agent.sh ] && \cat "$hint" || \echo "$hint"; } | \tr -s ' $=";(){}/\\!#~^'\' '\n' | _otel_filter_by_validity | \sort -u
Comment on lines +542 to +546
if \[ "$(\uname -s)" = Darwin ]; then
_otel_find_executables() {
\find "$1" -perm +111 -iname "$2"
}
else
Comment on lines +131 to +135
if \[ "$(\uname -s)" = Darwin ]; then
_otel_find_type_executable() { \find "$1" -maxdepth 1 -type "$2" -perm +111 2> /dev/null || \true; }
else
_otel_find_type_executable() { \find "$1" -maxdepth 1 -type "$2" -executable 2> /dev/null || \true; }
fi
\echo -n ' '; _otel_escape_args --mount type=bind,source=/usr/share/opentelemetry_shell,target=/usr/share/opentelemetry_shell
# the container is always linux, so sources follow the host layout while targets stay on the linux paths the container expects
{ _otel_find_executables "$_otel_shell_bin_home" 'otel*.sh'; _otel_find_executables "$_otel_shell_bin_home" 'opentelemetry_shell*.sh'; } | while read -r file; do \echo -n ' '; _otel_escape_args --mount type=bind,source="$file",target=/usr/bin/"${file##*/}",readonly; done
\echo -n ' '; _otel_escape_args --mount type=bind,source="$_otel_shell_home",target=/usr/share/opentelemetry_shell
@moflwi
moflwi marked this pull request as ready for review August 11, 2026 20:24
@moflwi
moflwi requested a review from plengauer as a code owner August 11, 2026 20:24

@plengauer plengauer left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as discussed:
*) lets rebase
*) lets not make sure they are not overlapping
*) i suggest finding a new solve for the dynamic path resolution everywhere - like rewriting paths when buillding the tarball

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 support

3 participants