Skip to content
ocx
install

Use ocx in dev containers and editors

This page shows how to give an editor and a dev container the same pinned toolchain that your shell and your CI use. It covers the VS Code extension, a dev container recipe, and the one query any other editor needs.

An editor does not source your shell profile, so a tool that works in your terminal can be “not found” in the editor’s run configuration. The fix is the same everywhere: give the editor the directory that ocx resolved, and never build that path by hand.

OCX for VS Code is the maintained route for VS Code. Install ocx-sh.ocx from the extension marketplace. It needs the ocx CLI on PATH, or ocx.path.executable set.

Open a folder that holds an ocx.toml. The extension composes the project toolchain with the CLI and injects the result into the extension host. Language servers and other extensions then find the tools your project declares. Editing ocx.toml or ocx.lock reloads the environment.

Three settings decide the rest:

SettingDefaultEffect
ocx.env.applyToTerminalsfalsePuts the toolchain PATH on newly opened terminals and tasks
ocx.groups[]Composes the named tool groups instead of only the default group
ocx.restart.automaticfalseRestarts the extension host without asking when the environment changes

The environment changes PATH, so it loads only in a trusted workspace. The extension also provides palette commands for ocx lock, ocx pull, ocx upgrade and ocx clean. Its README lists the commands, the settings and how to wire them into tasks.json.

An editor without an extension needs one directory on its PATH. Ask ocx for it:

Terminal window
ocx pull
ocx --format json shell state | jq -r .toolchain_bin

toolchain_bin is the directory that holds a launcher for every tool of the project in effect. Paste it into the editor’s setting. In VS Code that is terminal.integrated.env.<platform> in .vscode/settings.json. Put the printed directory in front of the inherited PATH:

{
"terminal.integrated.env.linux": {
"PATH": "/home/dev/.ocx/toolchain/active/bin:${env:PATH}"
}
}

The directory above stands in for the value the query printed. Use terminal.integrated.env.osx or terminal.integrated.env.windows on those platforms.

Run ocx pull first. The query reports the directory even if nothing was rendered into it. A setting that points at an empty directory looks the same as a broken one. The shell state report describes the other fields, and Scripts and JSON covers the format.

A package can also publish editor configuration of its own. The integrations field carries a vendor-keyed block such as com.microsoft.vscode, and ocx env --format json returns every block of the composition with its declaring package. A tool that wants those blocks reads the integrations array. ocx never interprets or merges the payloads, as Integrations explains.

ocx has no dev container feature. Install the binary in the image, pull at creation time and put the toolchain on the shell’s PATH.

Start from the Dockerfile. Copy the binary from the official image, which works on any base:

FROM mcr.microsoft.com/devcontainers/base:ubuntu
COPY --from=ghcr.io/ocx-sh/ocx:0.9.2-alpine /usr/local/bin/ocx /usr/local/bin/ocx

Then let devcontainer.json pull the lock and wire the shell. The extension id installs the editor integration of the previous section inside the container:

{
"build": { "dockerfile": "Dockerfile" },
"postCreateCommand": "ocx pull && { grep -q 'ocx --offline env' ~/.bashrc || echo 'eval \"$(ocx --offline env --shell=bash)\"' >> ~/.bashrc; }",
"customizations": { "vscode": { "extensions": ["ocx-sh.ocx"] } }
}

ocx pull downloads every digest-pinned package once. ocx --offline env --shell=bash then prints export lines from the lock with no network access, so each new shell opens fast. The grep guard keeps a rebuild from appending the line twice. For a zsh login, pass --shell=zsh and append the line to ~/.zshrc instead, since a zsh login never reads ~/.bashrc. Bake the pull into the image with the Docker recipe when the container must start without a registry.

Private packages need credentials in the container. Registry authentication recipes lists the variables. Put them in remoteEnv from the host’s environment and never in the Dockerfile.