Skip to content

engine: honor userEnvProbe in Engine.Exec#19

Merged
bilby91 merged 3 commits intomainfrom
honor-user-env-probe
May 9, 2026
Merged

engine: honor userEnvProbe in Engine.Exec#19
bilby91 merged 3 commits intomainfrom
honor-user-env-probe

Conversation

@bilby91
Copy link
Copy Markdown
Member

@bilby91 bilby91 commented May 9, 2026

Summary

  • ws.Config.UserEnvProbe was parsed but never applied — Engine.Exec passed the raw cmd to the runtime, so PATH additions from the user's rc files (nvm, asdf, feature-installed ~/.local/bin) were invisible to library callers. Discovered downstream in crunchloop/dap.
  • Adopt devpod's probe-once-and-inject model rather than @devcontainers/cli's per-exec shell wrapping: probe with bash -lic (or the configured variant) once after Up's lifecycle and on Attach, then merge the captured env under every Engine.Exec.
  • Layering: probedEnv < cfg.RemoteEnv < caller's opts.Env, with devpod's PATH-aware merge (/sbin stripped for non-root).
  • Adds ExecOptions.SkipUserEnvProbe for callers that want a clean env (internal probes, raw-env helpers).

Why probe-once instead of per-exec shell wrapping

  • No per-exec bash startup or .bashrc parse.
  • Clean signal/exit-code semantics — runtime exec stays direct (no exec "$@" trick).
  • Lifecycle phases (which set up the very rc state we're probing) don't risk double-wrapping.
  • Tradeoff: cache-staleness if .bashrc is edited at runtime — re-Up/re-Attach re-probes.

Test plan

  • go test -short ./... — existing unit tests pass (one assertion adjusted: Up now performs an extra probe exec).
  • go test -tags=integration ./test/integration/... — 5 new integration tests, all green:
    • TestUserEnvProbe_PathFromBashrcpostCreateCommand writes /etc/profile.d/*.sh; default-probe Exec sees EXTRA_PATH.
    • TestUserEnvProbe_NoneuserEnvProbe: \"none\" skips injection.
    • TestUserEnvProbe_SkipOption — per-Exec opt-out works even with default probe.
    • TestUserEnvProbe_NoBashFallback — alpine (no bash) doesn't break Up; Exec still works (graceful fallback).
    • TestUserEnvProbe_RemoteEnvMergedremoteEnv reaches Exec.
  • Existing image-source integration tests still pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Capture user shell environment from containers after startup/attach and make it available to later execs.
    • Per-exec opt-out to skip user-environment probing.
  • Improvements

    • User rc-file additions (e.g., PATH) are visible to subsequent execs; PATH merging improved and skips certain system paths for non-root users.
    • Probing falls back gracefully when bash is unavailable; failures are non-fatal.
  • Tests

    • Added integration tests covering probing, opt-out, fallback, and environment layering.

ws.Config.UserEnvProbe was parsed but never applied: Exec passed the
raw cmd to the runtime, so PATH additions injected by the user's rc
files (nvm, asdf, feature-installed binaries in ~/.local/bin) were
invisible to library callers. Discovered downstream in crunchloop/dap.

Adopt devpod's probe-once-and-inject model rather than @devcontainers/cli's
per-exec shell wrapping: probe with bash -lic (or the configured variant)
once after Up's lifecycle and on Attach, parse the resulting env, and
merge it under every Engine.Exec — layered as probed < cfg.RemoteEnv <
caller's opts.Env, with devpod's PATH-aware merge (/sbin stripped for
non-root). Adds ExecOptions.SkipUserEnvProbe for callers that want a
clean env (internal probes, raw-env helpers).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 670cfe7e-0b49-4107-874a-eaf75f9530e0

📥 Commits

Reviewing files that changed from the base of the PR and between 8beaadf and bfcafbb.

📒 Files selected for processing (1)
  • userenvprobe.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • userenvprobe.go

📝 Walkthrough

Walkthrough

Adds user environment probing during Up/Attach to capture shell-modified env into Workspace.probedEnv and conditionally merges that plus remoteEnv into Exec calls; per-call opt-out via ExecOptions.SkipUserEnvProbe.

Changes

User Environment Probing Feature

Layer / File(s) Summary
Data Shape
workspace.go
Workspace adds internal probedEnv map[string]string.
Probing Engine
userenvprobe.go
Adds probeUserEnv (10s timeout, /proc/self/environ then printenv fallback), probeShellFlags, parseEnvironBytes, mergeProbedEnv, and mergePath with /sbin filtering.
Lifecycle Integration
up.go, attach.go
Engine.Up probes post-lifecycle and sets ws.probedEnv; Engine.AttachWith re-probes on attach and assigns ws.probedEnv on success.
Exec Control
exec.go
ExecOptions gains SkipUserEnvProbe bool; Engine.Exec skips merging probed/remote env when this flag is true.
Tests / Integration
engine_test.go, test/integration/userenvprobe_test.go
Unit test fixes to ignore probe execs; integration tests (build-tagged) cover PATH injection from bashrc, userEnvProbe="none", per-call SkipUserEnvProbe, bash-less fallback, and remoteEnv merging.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Engine
  participant Container
  participant Workspace
  Client->>Engine: Up / AttachWith
  Engine->>Container: perform lifecycle
  Engine->>Container: spawn probe shell
  Container-->>Engine: env bytes
  Engine->>Workspace: set ws.probedEnv
  Client->>Engine: Exec (may SkipUserEnvProbe)
  Engine->>Container: exec with merged or caller env
  Container-->>Engine: command output
  Engine-->>Client: result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 A rabbit sniffed the shells at night,
Finding PATHs that hid from light,
It merges, skips, and softly treads,
Leaving remote envs and bash-made threads,
Hopping home with tidy threads.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding userEnvProbe support to Engine.Exec by probing and merging the user environment into exec calls.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch honor-user-env-probe

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@exec.go`:
- Around line 31-36: The comment for SkipUserEnvProbe is inaccurate: when Exec
uses SkipUserEnvProbe it actually skips the entire merge path (so RemoteEnv is
not merged) rather than only leaving RemoteEnv merging; update the comment near
the Exec implementation and the SkipUserEnvProbe field to reflect that behavior
by stating that setting SkipUserEnvProbe causes Exec to bypass probing and skip
merging probedEnv and RemoteEnv into the process environment, and mention
probedEnv remains empty for lifecycle scripts — reference the SkipUserEnvProbe
field, Exec function, RemoteEnv and probedEnv symbols when editing the doc to
ensure correct, unambiguous behavior is documented.

In `@userenvprobe.go`:
- Around line 89-90: The parsing currently mutates env values by calling
strings.TrimSpace on each fragment (line := strings.TrimSpace(s[start:i]));
remove the TrimSpace call and use the raw slice string(s[start:i]) so
leading/trailing spaces in values are preserved, optionally skipping truly empty
fragments (e.g., if len(s[start:i]) == 0) before processing; keep the same start
= i + 1 update and downstream logic that consumes variable name/value from the
preserved line.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3440d639-c677-403a-91a5-e30b6e9de63c

📥 Commits

Reviewing files that changed from the base of the PR and between 8d9a153 and 02a933e.

📒 Files selected for processing (7)
  • attach.go
  • engine_test.go
  • exec.go
  • test/integration/userenvprobe_test.go
  • up.go
  • userenvprobe.go
  • workspace.go

Comment thread exec.go Outdated
Comment thread userenvprobe.go Outdated
- SkipUserEnvProbe also bypasses RemoteEnv merging (not just probedEnv);
  doc previously implied otherwise.
- parseEnvironBytes no longer TrimSpaces entries — env values may
  legitimately carry leading/trailing whitespace.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@userenvprobe.go`:
- Around line 166-167: The root-detection is too narrow: update the if condition
that currently checks remoteUser == "root" (used when deciding whether to keep
/sbin segments before inserting into probedTokens) to treat all root
representations as root by including remoteUser == "0", remoteUser == ""
(container default), and the ContainerUser constant; i.e., change the check in
the block that references remoteUser, sbinSegment, probedTokens, insertAt and
slices.Insert to consider remoteUser == "root" || remoteUser == "0" ||
remoteUser == "" || remoteUser == ContainerUser so /sbin is preserved for all
root cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1ce5a5e2-cd80-484f-97ce-bd90b41d5b42

📥 Commits

Reviewing files that changed from the base of the PR and between 02a933e and 8beaadf.

📒 Files selected for processing (2)
  • exec.go
  • userenvprobe.go

Comment thread userenvprobe.go Outdated
Treat "0" and "" as root in addition to "root". Many minimal images
(alpine, bash:*-alpine) run as root by default but report empty
remoteUser via effectiveUser; the narrow check stripped /sbin
entries that the user actually has access to.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bilby91 bilby91 merged commit b143452 into main May 9, 2026
5 checks passed
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.

1 participant