Skip to content

fix(integrate): run init_hook when reopening an editor in the Devbox environment#2874

Open
mikeland73 wants to merge 3 commits into
mainfrom
claude/focused-goldberg-6qy8fs
Open

fix(integrate): run init_hook when reopening an editor in the Devbox environment#2874
mikeland73 wants to merge 3 commits into
mainfrom
claude/focused-goldberg-6qy8fs

Conversation

@mikeland73

Copy link
Copy Markdown
Collaborator

Summary

Fixes #2703.

devbox integrate vscode powers the VSCode/Cursor "Reopen in Devbox" action. It computes the Devbox environment and relaunches the editor with it — but it used Devbox.EnvVars, which deliberately excludes the init hook:

// internal/devbox/devbox.go
func (d *Devbox) EnvVars(ctx context.Context) ([]string, error) {
	// this only returns env variables for the shell environment excluding hooks
	...
}

So any environment variables exported by a project's init_hook (and any other init-hook side effects) were missing from the reopened editor, even though they are present in a normal devbox shell. Editors like Cursor that open their own terminals — and don't get the devbox shell injection — were left without those variables.

Fix

Add Devbox.EnvVarsWithInitHook, which sources the init hook in a subshell and captures the resulting environment, then have devbox integrate vscode use it instead of EnvVars.

Implementation details:

  • The init hook is sourced with its stdout redirected to stderr (. "<hooks>" 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).
  • The environment is dumped NUL-separated (env -0) so values containing newlines survive intact.
  • If the init hook errors, it falls back to the hook-less environment rather than failing, so the integration keeps working.
  • The hooks file is written via the existing shellgen.WriteScriptsToFiles, and sourced the same way EnvExports/direnv already source it — so this matches existing behavior of running hooks outside an interactive shell.

How was it tested?

  • go build ./internal/... and go vet ./internal/devbox/ ./internal/boxcli/ pass.
  • New unit tests in internal/devbox/inithookenv_test.go:
    • TestParseNulEnv — parsing of env -0 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.

Note: the integrate vscode command itself requires a Node parent process (go2node), so it isn't exercised by a testscript here; the new logic is covered by the unit tests above and CI runs the full suite.

cc @tm-michael (issue reporter) — thanks for the clear repro.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DYWzKQqM51H27NT2ARCrYy


Generated by Claude Code

`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.
Copilot AI review requested due to automatic review settings June 18, 2026 14:14
- 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).

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

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.EnvVarsWithInitHook to 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 vscode to use EnvVarsWithInitHook instead of EnvVars.

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.

Comment thread internal/devbox/inithookenv.go Outdated
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.

Copy link
Copy Markdown
Collaborator Author

Heads up on CI: the one red check — test (not-main, ubuntu-latest, project-tests-off, 2.18.0) — failed only on TestScripts/add_platforms_flakeref.test, which is an unrelated, network-heavy devbox add flakeref test (it fetches ~100 MiB across 80 nixpkgs store paths and ran ~200s). It is not exercised by this PR's changes (a new isolated internal/devbox/inithookenv.go helper plus one line in internal/boxcli/integrate.go).

Evidence it's a flake, not a regression:

  • test-nix-versions (ubuntu-latest, 2.18.0) — the same Nix version, running the full testscript suite — passed, as did all other test-nix-versions jobs (2.18.0/2.19.2/2.24.7/2.30.2 × ubuntu/macos).
  • golangci-lint (ubuntu + macos), build-devbox (both), Test Flake Build, and Spell Check all passed.
  • The other test (not-main, …) jobs show cancelled — that's the matrix fail-fast cascade from this single failure, not independent failures.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

devbox integrate does NOT run init_hook

3 participants