Skip to content
Closed
40 changes: 40 additions & 0 deletions registry/coder/modules/vscode-desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,43 @@ module "vscode" {
folder = "/home/coder/project"
}
```

### Auto-install extensions and configure settings

```tf
module "vscode" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/vscode-desktop/coder"
version = "1.1.0"
agent_id = coder_agent.example.id
folder = "/home/coder/project"

# Auto-install Python development extensions
extensions = [
"ms-python.python",
"ms-python.pylint",
"ms-toolsai.jupyter"
]

# Configure workspace settings
settings = {
"editor.fontSize" = 14
"editor.tabSize" = 2
"python.defaultInterpreterPath" = "/usr/bin/python3"
"workbench.colorTheme" = "Dark+ (default dark)"
}
}
```

### Disable automatic extension installation

```tf
module "vscode" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/vscode-desktop/coder"
version = "1.1.0"
agent_id = coder_agent.example.id
extensions = ["ms-python.python"]
install_extensions = false # Only create recommendations, don't install
}
```
57 changes: 56 additions & 1 deletion registry/coder/modules/vscode-desktop/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe("vscode-desktop", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
folder: "/foo/bar",
openRecent: "false",
open_recent: "false",
});
expect(state.outputs.vscode_url.value).toBe(
"vscode://coder.coder-remote/open?owner=default&workspace=default&folder=/foo/bar&url=https://mydeployment.coder.com&token=$SESSION_TOKEN",
Expand Down Expand Up @@ -86,4 +86,59 @@ describe("vscode-desktop", async () => {
expect(coder_app?.instances.length).toBe(1);
expect(coder_app?.instances[0].attributes.order).toBe(22);
});

it("creates setup script when extensions are provided", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
extensions: '["ms-python.python", "ms-vscode.cpptools"]',
});

const coder_script = state.resources.find(
(res) => res.type === "coder_script" && res.name === "vscode_desktop_setup",
);

expect(coder_script).not.toBeNull();
expect(coder_script?.instances.length).toBe(1);
expect(coder_script?.instances[0].attributes.run_on_start).toBe(true);
});

it("creates setup script when settings are provided", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
settings: '{"editor.fontSize": 14, "workbench.colorTheme": "Dark+ (default dark)"}',
});

const coder_script = state.resources.find(
(res) => res.type === "coder_script" && res.name === "vscode_desktop_setup",
);

expect(coder_script).not.toBeNull();
expect(coder_script?.instances.length).toBe(1);
});

it("does not create setup script when install_extensions is false", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
extensions: '["ms-python.python"]',
install_extensions: "false",
});

const coder_script = state.resources.find(
(res) => res.type === "coder_script" && res.name === "vscode_desktop_setup",
);

expect(coder_script).toBeUndefined();
});

it("does not create setup script when no extensions or settings", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
});

const coder_script = state.resources.find(
(res) => res.type === "coder_script" && res.name === "vscode_desktop_setup",
);

expect(coder_script).toBeUndefined();
});
});
42 changes: 42 additions & 0 deletions registry/coder/modules/vscode-desktop/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,51 @@ variable "group" {
default = null
}

variable "extensions" {
type = list(string)
description = "A list of VS Code extensions to install. Extensions should be specified in the format 'publisher.extension-name'."
default = []

validation {
condition = alltrue([
for ext in var.extensions : can(regex("^[a-zA-Z0-9][a-zA-Z0-9\\-_]*\\.[a-zA-Z0-9][a-zA-Z0-9\\-_]*$", ext))
])
error_message = "Extensions must be in the format 'publisher.extension-name' (e.g., 'ms-python.python')."
}
}

variable "settings" {
type = any
description = "A map of VS Code settings to apply to the workspace. These settings will be written to the workspace's settings.json file."
default = {}
}

variable "install_extensions" {
type = bool
description = "Whether to automatically install the specified extensions when the workspace starts."
default = true
}

data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}

# Script to install extensions and configure settings
resource "coder_script" "vscode_desktop_setup" {
count = var.install_extensions && (length(var.extensions) > 0 || length(var.settings) > 0) ? 1 : 0
agent_id = var.agent_id
display_name = "VS Code Desktop Setup"
icon = "/icon/code.svg"
run_on_start = true
run_on_stop = false
timeout = 300

script = templatefile("${path.module}/run.sh", {
EXTENSIONS = jsonencode(var.extensions)
SETTINGS = jsonencode(var.settings)
FOLDER = var.folder
})
}

resource "coder_app" "vscode" {
agent_id = var.agent_id
external = true
Expand Down
Loading
Loading