Conversation
…d in strings
The variable resolver regex used `^` and `$` anchors (`/^\$\{(.+?)(?::(.+))?\}$/`),
requiring the entire string to be a variable reference. Variables inside
larger strings like `source=${localEnv:HOME}/.gitconfig,target=...` were
never resolved.
Remove the anchors, make the regex global and the inner group non-greedy,
and rewrite `resolveVariable()` to use `String.replace()` with a callback
so variables are resolved wherever they appear in the string.
Contributes to #14294
Co-authored-by: Nina Doschek <ndoschek@eclipsesource.com>
`parseMountString()` only extracted `source`, `target`, and `type`,
silently dropping options like `consistency=cached` and `readonly`.
Additionally, values containing `=` (e.g. from resolved variables) were
truncated by `split('=')[1]`.
Use `substring(indexOf('=') + 1)` instead of `split('=')[1]` to handle
values containing `=`. Extract `readonly`/`ro` into `ReadOnly: true` and
`consistency=<value>` into `BindOptions.Propagation`.
Contributes to #14294
Co-authored-by: Nina Doschek <ndoschek@eclipsesource.com>
`--set-preference=key={"nested":"value"}` was passed through `sh -c` in
the container's exec, where the shell interpreted braces and quotes,
producing invalid JSON.
On the sender side (SettingsContribution.enhanceArgs), object/array
values are now base64-encoded with a `base64:` prefix. Scalar values
(strings, numbers, booleans) pass through unchanged.
On the consumer side (PreferenceCliContribution.setArguments), values
with a `base64:` prefix are decoded before `JSON.parse()`.
Contributes to #14294
Co-authored-by: Nina Doschek <ndoschek@eclipsesource.com>
`portsAttributes` was defined in the devcontainer type schema but `ForwardPortsContribution` only read `forwardPorts` and ignored port attributes entirely. Extend `ForwardedPort` interface with optional `label`, `protocol`, and `onAutoForward` fields. `ForwardPortsContribution.handlePostConnect()` now merges matching `portsAttributes` into forwarded port objects via a new `getPortAttributes()` method that supports exact port match and range patterns (e.g. `"8000-9000"`). Contributes to #14294 Co-authored-by: Nina Doschek <ndoschek@eclipsesource.com>
Previously only "Reopen in Container" (from a devcontainer.json) was supported. There was no way to connect to an already-running Docker container. Add `RunningContainerInfo` interface, `listRunningContainers()` and `attachToContainer(containerId)` methods to the RPC interface. The backend calls `docker.listContainers()` and creates a `RemoteDockerContainerConnection` with `RemoteSetupService.setup()`. On the frontend, a new `ATTACH_TO_CONTAINER` command shows a QuickPick of running containers (name, image, status), then connects and opens the remote workspace. Workspace path is inferred from container mounts or working directory. Contributes to #14294 Co-authored-by: Nina Doschek <ndoschek@eclipsesource.com>
…and shell detection Three issues addressed: SSH didn't work in containers, git identity wasn't carried over, and terminals defaulted to sh. SSH credentials (Fixes #14926): - Create an isolated SSH directory at `~/.theia/dev-container/ssh/` instead of exposing the real `~/.ssh` - Copy `known_hosts` and `config` from the host for host verification - Generate a dedicated ed25519 keypair for container use (printed to console for the user to register with their Git provider) - Detect SSH-based commit signing (`gpg.format = ssh`) and copy the signing key into the isolated directory - Bind-mount the isolated dir read-only; fix permissions in post-create (700 dir, 600 private keys, 644 public keys/known_hosts) - Start a shared ssh-agent via `/etc/profile.d/ssh-agent.sh` so the passphrase is only prompted once per container session Git identity: - Bind-mount host `~/.gitconfig` to `/tmp/host_gitconfig` (read-only) - In post-create, copy to the container user's `$HOME/.gitconfig` so the container user owns it - For SSH signing: rewrite `user.signingkey` to point to the container's SSH directory instead of the host-absolute path - For GPG signing: disable (keys aren't available in containers) - SSH signing is left enabled when the key is available Terminal shell (Fixes #14293): - Set `THEIA_SHELL=/bin/bash`, `SHELL=/bin/bash`, and `THEIA_SHELL_ARGS=-l` as container env vars at creation time - The `-l` flag starts bash as a login shell, which sources `/etc/profile.d/` (needed for ssh-agent env) and `~/.bashrc` (needed for the user's prompt customization) - Also set `TERM=xterm-256color` and `COLORTERM=truecolor` Contributes to #14294 Co-authored-by: Nina Doschek <ndoschek@eclipsesource.com>
…start When Theia restarted with a `devcontainer://` URI as the most recently used workspace, `WorkspaceService.doInit()` called `toFileStat()` which failed for non-file schemes, resulting in an empty workspace instead of reconnecting to the container. Check for non-`file://` URI schemes before calling `toFileStat()`. When detected, route through the registered `WorkspaceOpenHandlerContribution` handlers. The existing `ContainerConnectionContribution` (already registered as handler) handles `devcontainer://` URIs and triggers the container reconnection flow. Fixes #14292 Contributes to #14294 Co-authored-by: Nina Doschek <ndoschek@eclipsesource.com>
1a2d73a to
993502d
Compare
When opening a workspace that contains a devcontainer.json file, there was no prompt to the user about the available dev container configuration. Add `DevContainerSuggestionContribution` (FrontendApplicationContribution) that on startup checks if already connected to a remote container (skips if so), waits for the workspace to be ready, then checks for devcontainer.json files. If found, shows an info notification offering "Reopen in Container" and "Don't Show Again" actions. Contributes to #14294 Co-authored-by: Nina Doschek <ndoschek@eclipsesource.com>
Add a command that stops and removes the current dev container, clears the cached container info, and recreates it from the devcontainer.json. When inside a remote container, the command reads the stored connection context (devcontainer file path, host workspace path, container ID) from localStorage — scanning the filesystem won't work because the RPC goes to the local backend which doesn't have the container's paths. The connection context is stored by `doOpenInContainer` at connect time so it's available for rebuild regardless of session type. Command visibility: - "Reopen in Container" is shown only in local sessions - "Rebuild Container" is shown only in remote sessions Also fixes workspace path inference: `inferWorkspacePath()` skips injected mounts (.ssh, .gitconfig) that were being picked as `Mounts[0]` when `workspaceFolder` was not set in devcontainer.json. Contributes to #14294, #15121 Co-authored-by: Nina Doschek <ndoschek@eclipsesource.com>
The Ports view widget existed but was inaccessible — no toggle command
was registered. Add `toggleCommandId` to `PortForwardingContribution` so
the view can be opened via the command palette ("Ports: Focus on Ports
View").
Also add a "Label" column to the Ports table and pass through the
`label` field from `ForwardedPort` so that `portsAttributes` labels
from devcontainer.json are displayed.
993502d to
d5e7233
Compare
ndoschek
left a comment
There was a problem hiding this comment.
Thanks a lot for this PR @sgraband!
I tested it and things work well on my end 🎉
I've added a few inline comments, please have a look.
One bit of housekeeping: the commits reference several issues this PR fixes/contributes to, but they're not linked from the PR description yet. Could you add/link them?
On the bigger picture: a natural next step would be to make "open folder in dev container" smoother by having Theia determine the correct workspace folder automatically from the devcontainer metadata (workspaceFolder, mounts...) instead of relying on the user to navigate there. But of course fine as a follow-up.
| try { | ||
| // openWorkspace reloads the window on success, so if we | ||
| // reach the timeout the connection attempt is hanging. | ||
| await Promise.race([ |
There was a problem hiding this comment.
The 15-second timeout may be too short for dev container reconnection. openWorkspace() calls doOpenInContainer() → connectToContainer(), which can pull Docker images, create containers, and set up remote connections which can easily exceed 15 seconds on first run or slow networks. When the timeout fires, it falls back to a local workspace while the container creation continues in the background, leading to a confusing state. Consider increasing the timeout or making it configurable.
There was a problem hiding this comment.
I feel like increasing the timeout to 2 minutes should be fine. Without configuration (for now). This should only really be relevant, when the user deleted the docker image and then restarts Theia and the dev container is the most recent workspace if i am not missing something. So this does not really happen that often i believe, therefore a setting is not really necessary imho.
| const keyPath = path.join(isolatedDir, 'id_ed25519'); | ||
| if (!await fs.pathExists(keyPath)) { | ||
| await new Promise<void>((resolve, reject) => { | ||
| cp.exec(`ssh-keygen -t ed25519 -f "${keyPath}" -N "" -C "theia-dev-container"`, err => { |
There was a problem hiding this comment.
ensureIsolatedSshDir() silently generates a new ed25519 keypair at ~/.theia/dev-container/ssh/id_ed25519 if none exists. While it logs a message asking the user to register the public key, auto-generating cryptographic material on the user's machine could be surprising. Consider making this opt-in or using a more prominent notification (e.g., via MessageService).
| createOptions.Env!.push(`${key}=${value}`); | ||
| } | ||
| }; | ||
| setIfMissing('THEIA_SHELL', '/bin/bash'); |
There was a problem hiding this comment.
Hardcodes /bin/bash as the default shell. Some container images (e.g., Alpine-based) don't include bash. If /bin/bash doesn't exist, terminal sessions will fail to start. Consider detecting the available shell inside the container (e.g., which bash || which sh) in handlePostCreate, or at least falling back to /bin/sh.
- Localize user-facing strings (error messages, progress reports) - Persist "Don't Show Again" for reopen-in-container suggestion - Revert ILogger back to console per Theia logging guidelines - Fix shutdownAction operator precedence and guard missing compose file - Add DevContainerConfiguration.empty() factory for attach flow - Preserve remote MRU workspace across in-container saves/unloads - Increase remote workspace reconnection timeout to 120s - Add unit tests for cli-enhancing-creation-contributions
What it does
Contributes to #14294
This PR adds several new features, bug fixes, and quality improvements to the dev container support in Theia:
New features:
${localEnv:HOME}/path)portsAttributesfrom devcontainer.json, allowing port labels, protocols, and auto-forward behavior to be configured.gitconfig(with SSH signing key rewriting), configure a login shell with SSH agent, and share host credentials into the containerportsAttributesHow to test
Prerequisites: Docker must be installed and running.
Reopen in Container
.devcontainer/devcontainer.jsonDev Container: Reopen in Containerfrom the command paletteSuggestion notification
Rebuild Container
Dev Container: Rebuild ContainerAttach to Running Container
docker run -it ubuntu bash)Dev Container: Attach to Running ContainerSSH credentials and git identity
~/.ssh/configand~/.gitconfigon the hostgit config user.nameanduser.emailare inheritedssh -T git@github.comworks (key forwarding)echo $SSH_AUTH_SOCK)Port forwarding with labels
forwardPortsandportsAttributes(e.g. label, protocol)Variable resolver with embedded variables
{ "remoteEnv": { "MY_VAR": "${localEnv:HOME}/projects" } }echo $MY_VARresolves to the host's$HOME/projects(not the literal${localEnv:HOME}string)Mount options pass-through
mountsentry in devcontainer.json using the string form with extra options, e.g.:{ "mounts": ["type=bind,source=${localEnv:HOME}/.config,target=/home/vscode/.config,readonly"] }mount | grep .config), is read-only, and uses the correct source/target pathsSettings with complex JSON values
{ "settings": { "editor.tokenColorCustomizations": { "comments": "#FF0000" } } }--set-preferenceCLI argumentsAuto-reopen after restart
Follow-ups
Breaking changes
Attribution
Contributed on behalf of STMicroelectronics and TypeFox
Review checklist
nlsservice (for details, please see the Internationalization/Localization section in the Coding Guidelines)Reminder for reviewers