Breaking Through VSCode Env Restrictions with Devbox + Nix

VSCode is the most popular code editor for developers nowadays. So when we first released Devbox, integration with code editors was a priority to improve the developer experience. Our vision for these integrations was, by a developer taking one action like pressing a button, we need to set their code editor as if it’s fully integrated with their Devbox shell environment. So that means the editor recognizes the packages they have installed in their Devbox shell, and it has the same environment variables as the shell. But this ambitious goal came with hurdles as we started looking into VSCode as the first candidate code editor.

As they say, the devil is in the details, so let’s look at the devil hiding in VSCode’s details.

VSCode’s Env Variable Restriction

To integrate Devbox with VSCode, we started by writing a VSCode extension. This extension allowed us to add features like devbox shell and devbox run to VSCode. However, since the goal of Devbox is to create isolated dev environments with their own variables and packages, we needed to update the environment VSCode was running in. But that’s where the devil starts to show up: VSCode is an electron app that contains multiple subprocesses. You can see those subprocesses by opening its Process Explorer, but to save you a click, I included a screenshot and diagram of the processes from VSCode’s blog below:

Process Explorer window from VSCode
Diagram of subprocesses and their communication in a VSCode app

A lot is going on in that diagram. The good news is you don’t need to understand all of it. I’ll try my best to give a summary but if you don’t want to read all that, skip to the next paragraph. Basically, there is a Main Process that spawns an Extension Host Process and each extension runs as a subprocess of Extension Host. Main Process also spawns a Renderer Process which is the GUI, a Terminal Host Process that all terminals inside VSCode are subprocesses of it, a File Watcher, and a Search Process that are self-explanatory. Lastly, the Shared Process is there so Main Process can offload performance-intensive code to a background process.

Phew! Ok! Now back to our goal, we want to update the environment variables of all VSCode’s processes. But we are writing an extension and our extension will be running as a subprocess of a subprocess of Main Process. So changing the environment variables of all the VSCode processes is not possible because we don’t control any of those processes. We only control our own extension. We can’t even be sure we can update environment variables for other extensions, because we don’t know if Extension Host will keep its extension subprocesses in sync.

So what can we do? With all this process communication going on, we are at the mercy of VSCode exposing an API that allows an extension to update environment variables and propagate them to the rest of VSCode. Luckily, some other developers before us have thought of that and requested this feature in a Github issue. That issue is a long thread, but here is the gist: VSCode is not going to allow this feature. Using the available API, from an extension, you can update the environment variables of the Terminal Process and a little warning icon will appear that the environment is updated. But you can’t update the environment variables of other parts of VSCode.

So that means all roads to a viable solution for our extension seem blocked. Time to give up? or time to think outside the box?

Thinking Outside The Box

Thinking outside the box meant not getting caught up in the web of VSCode subprocesses. Instead, we focused on launching VSCode from within a Devbox environment. We confirmed that opening VSCode from within devbox shell in the terminal (using the code CLI) properly configures the Main Process. So now we just needed a way for our extension to close VSCode and reopen it within a devbox shell.

The challenge is that if we close VSCode, our extension process also dies. We overcame this challenge by creating a Devbox subprocess that continues to live on after our extension process dies. On Unix systems, if you spawn a subprocess and terminate the parent process, the subprocess lives on. This was our winning card!

So to summarize, this was our solution:

Diagram of steps for Devbox’s VSCode extension
  1. Devbox’s VSCode extension spawns a Devbox subprocess from our VSCode extension and opens a communication channel between the subprocess and VSCode extension. Subprocess then sets up Devbox shell environment variables.
  2. Once done, the subprocess signals the VSCode extension that it’s ready to open a new VSCode window. The extension receives the signal and closes itself and the current VSCode window.
  3. Now that the old VSCode window is closed, Devbox subprocess opens a new VSCode window with all environment variables set up correctly and terminates itself.

To an end user, this looks like the VSCode window just reloaded. If the Devbox CLI is installed and set up, the whole thing takes less than a couple of seconds.

0:00
/
Demo: Reopen in Devbox shell environment 

Conclusion

Implementing this feature wasn’t easy but we’re glad we did it so that developing on VSCode can be a more pleasant experience for Devbox users. This feature is now available as a command in Devbox’s VSCode extension. It’s called “Reopen in Devbox shell environment”. We’re now looking into expanding this feature to other IDEs such as JetBrains as well as VSCode on Windows and Windows Linux Subsystem (WSL).

If you want to give Devbox a try, download it here, and if you want to check out Devbox’s VSCode extension, install it here.

If you'd like to keep up with our progress, you can follow us on Twitter, or chat with our developers on our Discord Server. We also welcome issues and pull requests on our Github Repo.