Skip to content

Feature: Pre-Install VS Code extensions in desktop VS Code module #218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
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