feat(integrate): add --run-init-hook flag to run init_hook when reopening an editor - #2874
Conversation
`devbox integrate vscode` (the VSCode/Cursor "Reopen in Devbox" action) launched the editor with the computed Devbox environment but never ran the project's init hook. It used Devbox.EnvVars, which deliberately excludes hooks. As a result, environment variables exported by init_hook (and any other side effects) were missing from the reopened environment, even though they are present in a normal `devbox shell`. Add Devbox.EnvVarsWithInitHook, which sources the init hook in a subshell and captures the resulting environment (NUL-separated so multiline values survive, with the hook's stdout redirected to stderr so it can't corrupt the dump). If the hook errors, it falls back to the hook-less environment so the integration keeps working. The integrate command now uses this method. Fixes #2703.
- Use fileutil.Exists instead of os.Stat to detect a missing hooks file, avoiding the nilerr lint error from returning nil after a non-nil error. - Use t.Context() instead of context.Background() in tests (usetesting).
There was a problem hiding this comment.
Pull request overview
This PR fixes devbox integrate vscode so the VSCode/Cursor “Reopen in Devbox” flow captures environment variables and side effects from a project’s init_hook, matching what users get in a normal devbox shell.
Changes:
- Added
Devbox.EnvVarsWithInitHookto compute env vars by sourcing the generated hooks file in a subshell and capturing the resulting environment. - Added unit tests covering NUL-separated env parsing and init hook capture behavior (including preventing hook stdout from corrupting the captured env).
- Updated
integrate vscodeto useEnvVarsWithInitHookinstead ofEnvVars.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| internal/devbox/inithookenv.go | Implements env capture that includes init hook effects by sourcing hooks in a subshell and parsing captured env output. |
| internal/devbox/inithookenv_test.go | Adds unit tests for NUL-env parsing and init-hook-based env capture behavior. |
| internal/boxcli/integrate.go | Switches VSCode integration to use the new env computation that includes init hook effects. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Address review feedback on captureEnvWithInitHook: - Pass the hooks path as a positional parameter ($1) to `sh -c` instead of interpolating it into the script, so paths containing spaces, quotes, $() or backticks can't alter shell parsing. - Dump the environment with awk's POSIX ENVIRON instead of `env -0`. macOS' default /usr/bin/env does not support `-0`, which would have made the hook capture silently fall back to the hook-less env on macOS. awk is portable across Linux and macOS.
|
Heads up on CI: the one red check — Evidence it's a flake, not a regression:
I don't have permission to re-run failed jobs (API returns 403). Could a maintainer re-run that job? It should go green on retry. Happy to push a no-op commit to re-trigger CI instead if you'd prefer. Generated by Claude Code |
|
@claude please review this PR for correctness, and flag anything that would block merging. |
|
Claude finished @mikeland73's task in 3m 5s —— View job Reviewing PR #2874
ReviewI reviewed the full diff (base Copilot's earlier concerns are resolved. I verified in the current code ( cmd := exec.CommandContext(ctx, "sh", "-c", script, "sh", hooksPath)The hooks path is passed as a positional parameter ( Things I specifically checked and confirmed are correct:
Minor, non-blocking observations:
Nice fix — good test coverage for the tricky parts (NUL parsing, stdout isolation, missing-hooks-file fallback), and the iteration in response to the Copilot review meaningfully improved portability/robustness. |
Running the init hook when reopening an editor is now behind an explicit --run-init-hook flag (default off), since init hooks can be slow or have side effects. The VSCode extension will expose this as a setting in a follow-up. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Stacked on #2874 (adds the `--run-init-hook` flag to `devbox integrate vscode`). Merge that first. ## Summary - New extension setting **`devbox.runInitHookOnReopen`** (boolean, default `false`). When enabled, "Devbox: Reopen in Devbox shell environment" passes `--run-init-hook` to `devbox integrate vscode`, so the project's `init_hook` runs and any environment variables it exports are present in the reopened editor. See #2703. - The flag is only appended when the setting is on, so users on an older CLI that doesn't know the flag keep working with default settings. - Bumps the extension to **0.1.9** so `vsce publish --skip-duplicate` in the release workflow actually publishes the change. - README and CHANGELOG updated. ## How was it tested? - `yarn compile` and `yarn lint` pass in `vscode-extension/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
Addresses #2703. The VSCode extension side is in the stacked PR #2975.
devbox integrate vscodepowers the VSCode/Cursor "Reopen in Devbox" action. It computes the Devbox environment and relaunches the editor with it — but it usesDevbox.EnvVars, which deliberately excludes the init hook:So any environment variables exported by a project's
init_hookare missing from the reopened editor, even though they are present in a normaldevbox shell. Editors like Cursor that open their own terminals — and don't get thedevbox shellinjection — are left without those variables.Fix
Devbox.EnvVarsWithInitHook, which sources the init hook in a subshell and captures the resulting environment.--run-init-hookflag todevbox integrate vscode(default off). When set, the command usesEnvVarsWithInitHookinstead ofEnvVars. Default behavior is unchanged, since init hooks can be slow or have side effects.Implementation details:
. "$1" 1>&2) so the hook's own output can never corrupt the captured environment (the integrate command speaks an IPC protocol to the editor over a separate fd, so this is important).awk'sENVIRON(portable to macOS, unlikeenv -0) so values containing newlines survive intact.shellgen.WriteScriptsToFiles, and sourced the same wayEnvExports/direnv already source it — so this matches existing behavior of running hooks outside an interactive shell.How was it tested?
go build ./...andgo vetpass.internal/devbox/inithookenv_test.go:TestParseNulEnv— parsing of NUL-separated output, including a value with an embedded newline and an empty value.TestCaptureEnvWithInitHook— a hook that sets a new var, overrides an existing one, and prints to stdout; asserts the new/overridden vars are captured and the hook's stdout does not leak into the env.TestCaptureEnvWithInitHook_NoHooksFile— returns the base env unchanged when there is no hooks file.cc @tm-michael (issue reporter) — thanks for the clear repro.
🤖 Generated with Claude Code