fix(shellenv): only export env vars Devbox adds or changes - #2894
Conversation
`devbox shellenv` and `devbox global shellenv` exported the entire
environment, including unrelated variables such as HOSTNAME and LANG.
On distributions that mark some variables read-only (e.g. PROFILEREAD on
openSUSE), `eval "$(devbox global shellenv)"` then fails with errors like:
(eval):55: read-only variable: PROFILEREAD
Restrict the exported variables to those Devbox actually adds or changes
relative to the current shell. Unchanged variables are already present in
the shell, so re-exporting them is redundant and only risks these errors.
The filtering is applied to the `devbox shellenv` command path and skipped
in pure mode (where a complete, self-contained environment is intended).
Fixes #2826
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E6tBNHN6DFv5VabVTAosNq
|
@claude please review this PR for correctness, and flag anything that would block merging. |
|
Claude finished @mikeland73's task in 2m 10s —— View job Reviewing PR #2894
ReviewI traced the full data flow ( Why it works: Scoping checked and looks right:
Minor / non-blocking observations:
Verification: I wasn't able to run Verdict: I don't see anything that should block merging. The change is well-scoped, the filtering logic is sound, and the "opt-in only for |
Summary
Fixes #2826.
devbox shellenvanddevbox global shellenvexport the entire environment, including variables that Devbox never touches such asHOSTNAMEandLANG. On distributions that mark some variables read-only (e.g. openSUSE marksPROFILEREADread-only),eval "$(devbox global shellenv)"then fails with errors like:Re-exporting a variable to the value it already has is a no-op at best, and at worst breaks the user's prompt/shell on every command.
Fix
EnvExportsnow restricts the exported variables to those Devbox actually adds or changes relative to the current shell environment. Variables whose value already matches the ambient environment are dropped, since they are already present in the shell — so there is no reason to re-emit them (and no reason to trip over read-only ones).internal/devbox/envvars.go: addonlyModifiedEnvVars(env, ambient)helper that returns only new/changed variables.internal/devbox/devbox.go: inEnvExports, filter the computed env againstos.Environ()when the new option is set. Skipped in pure mode, where the intent is to emit a complete, self-contained environment rather than a diff against the current shell.internal/devbox/devopt/devboxopts.go: addOnlyModifiedEnvtoEnvExportsOpts.internal/boxcli/shellenv.go: enableOnlyModifiedEnvfor theshellenvcommand (the reported path). Thedevbox shell --print-env/ direnv path is intentionally left unchanged.Variables Devbox relies on (
PATH, theDEVBOX_*helpers, the shellenv hash, plugin/config env, etc.) are always new or changed, so they continue to be exported. Re-runningshellenvinside an already-active environment now emits only what differs, which is idempotent.Testing
TestOnlyModifiedEnvVarsininternal/devbox/envvars_test.gocovering unchanged (dropped), changed (kept), and new (kept) variables, including a read-only-stylePROFILEREADcase.go test ./internal/devbox/ -run 'TestOnlyModifiedEnvVars|TestExportify'passes.go build ./internal/...,go vet, andgofmtare clean.(The remaining failures in
./internal/devbox/unit tests are pre-existing and environmental — they require thenixbinary, which isn't available in CI sandbox; they are unrelated to this change.)cc @gasuketsu (issue reporter)
🤖 Generated with Claude Code
https://claude.ai/code/session_01E6tBNHN6DFv5VabVTAosNq
Generated by Claude Code