From d84c8a745c1faeb10be173f90c2ceded54269991 Mon Sep 17 00:00:00 2001 From: Pranith Tirumalasetti Date: Sun, 24 Aug 2025 23:08:48 +0530 Subject: [PATCH] feat: Pre-install VS Code extensions and settings --- .../coder/modules/vscode-desktop/README.md | 47 +++++++++++--- .../coder/modules/vscode-desktop/main.test.ts | 44 ++++++++++++- registry/coder/modules/vscode-desktop/main.tf | 61 ++++++++++++++++++- 3 files changed, 140 insertions(+), 12 deletions(-) diff --git a/registry/coder/modules/vscode-desktop/README.md b/registry/coder/modules/vscode-desktop/README.md index 23f02bfeb..ddab81766 100644 --- a/registry/coder/modules/vscode-desktop/README.md +++ b/registry/coder/modules/vscode-desktop/README.md @@ -1,6 +1,6 @@ --- display_name: VS Code Desktop -description: Add a one-click button to launch VS Code Desktop +description: Add a one-click button to launch VS Code Desktop with pre-installed extensions and settings. icon: ../../../../.icons/code.svg verified: true tags: [ide, vscode] @@ -8,29 +8,56 @@ tags: [ide, vscode] # VS Code Desktop -Add a button to open any workspace with a single click. +Add a button to open any workspace with a single click. This module can also pre-install VS Code extensions and apply custom settings for a ready-to-code environment. -Uses the [Coder Remote VS Code Extension](https://github.com/coder/vscode-coder). +It uses the [Coder Remote VS Code Extension](https://github.com/coder/vscode-coder). + +## Basic Usage ```tf module "vscode" { - count = data.coder_workspace.me.start_count - source = "registry.coder.com/coder/vscode-desktop/coder" - version = "1.1.1" + source = "[registry.coder.com/coder/vscode-desktop/coder](https://registry.coder.com/coder/vscode-desktop/coder)" + version = "1.2.0" # Or latest version agent_id = coder_agent.example.id } ``` - ## Examples ### Open in a specific directory ```tf module "vscode" { - count = data.coder_workspace.me.start_count - source = "registry.coder.com/coder/vscode-desktop/coder" - version = "1.1.1" + source = "[registry.coder.com/coder/vscode-desktop/coder](https://registry.coder.com/coder/vscode-desktop/coder)" + version = "1.2.0" # Or latest version + agent_id = coder_agent.example.id + folder = "/home/coder/project" +} +``` + +## Pre-install extensions and apply settings + +```tf +module "vscode" { + source = "[registry.coder.com/coder/vscode-desktop/coder](https://registry.coder.com/coder/vscode-desktop/coder)" + version = "1.2.0" # Or latest version agent_id = coder_agent.example.id folder = "/home/coder/project" + + # A list of extension IDs from the VS Code Marketplace to install on startup. + extensions = [ + "ms-python.python", + "golang.go", + "hashicorp.terraform", + "esbenp.prettier-vscode" + ] + + # A map of settings that will be converted to JSON + # and written to the settings file. Use the jsonencode function for this. + settings = jsonencode({ + "editor.fontSize": 14, + "terminal.integrated.fontSize": 12, + "workbench.colorTheme": "Default Dark+", + "editor.formatOnSave": true + }) } ``` diff --git a/registry/coder/modules/vscode-desktop/main.test.ts b/registry/coder/modules/vscode-desktop/main.test.ts index b59ef5dc6..3d154d27a 100644 --- a/registry/coder/modules/vscode-desktop/main.test.ts +++ b/registry/coder/modules/vscode-desktop/main.test.ts @@ -30,6 +30,8 @@ describe("vscode-desktop", async () => { expect(coder_app?.instances[0].attributes.order).toBeNull(); }); + // Keep all existing tests... + it("adds folder", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", @@ -86,4 +88,44 @@ describe("vscode-desktop", async () => { expect(coder_app?.instances.length).toBe(1); expect(coder_app?.instances[0].attributes.order).toBe(22); }); -}); + + // Add this new test case for extensions and settings + it("installs extensions and applies settings", async () => { + const settings = JSON.stringify( + { + "editor.fontSize": 14, + "terminal.integrated.fontSize": 12, + }, + null, + 2, + ); + + const extensions = ["ms-python.python", "golang.go"]; + + await runTerraformApply(import.meta.dir, { + agent_id: "foo", + extensions: extensions, + settings: settings, + }); + + const checkScript = ` + set -e + # The test environment may not have 'code' in the PATH immediately + # so we add the known location. + export PATH="$PATH:/tmp/coder/bin" + + # Verify extensions + INSTALLED_EXTENSIONS=$(code --list-extensions) + echo "$INSTALLED_EXTENSIONS" | grep -q "ms-python.python" + echo "$INSTALLED_EXTENSIONS" | grep -q "golang.go" + + # Verify settings + cat /home/coder/.vscode-server/data/Machine/settings.json + `; + + const result = await executeScriptInContainer(checkScript); + expect(result.exitCode).toBe(0); + // Use JSON.parse to compare objects, ignoring formatting differences. + expect(JSON.parse(result.stdout)).toEqual(JSON.parse(settings)); + }); +}); \ No newline at end of file diff --git a/registry/coder/modules/vscode-desktop/main.tf b/registry/coder/modules/vscode-desktop/main.tf index f93d14e30..bacc7ad25 100644 --- a/registry/coder/modules/vscode-desktop/main.tf +++ b/registry/coder/modules/vscode-desktop/main.tf @@ -38,9 +38,68 @@ variable "group" { default = null } +# New variable for extensions +variable "extensions" { + type = list(string) + description = "A list of VS Code extension IDs to install." + default = [] +} + +# New variable for settings +variable "settings" { + type = string + description = "A JSON string of settings to apply to VS Code." + default = "" +} + data "coder_workspace" "me" {} data "coder_workspace_owner" "me" {} +# This script will install extensions and apply settings +resource "coder_script" "setup_vscode" { + # Only run if extensions or settings are provided + count = length(var.extensions) > 0 || var.settings != "" ? 1 : 0 + + agent_id = var.agent_id + display_name = "Setup VS Code" + icon = "/icon/code.svg" + run_on_start = true + + script = <<-EOT + #!/bin/bash + set -e + + # Wait for code-server to be available + # VS Code Server is installed by the Coder agent, which can take a moment. + for i in {1..30}; do + if command -v code &> /dev/null; then + break + fi + echo "Waiting for 'code' command..." + sleep 1 + done + if ! command -v code &> /dev/null; then + echo "'code' command not found after 30s" + exit 1 + fi + + # Install extensions + %{ for ext in var.extensions ~} + code --install-extension ${ext} --force + %{ endfor ~} + + # Apply settings + %{ if var.settings != "" ~} + # Path for settings for remote VS Code Desktop + SETTINGS_DIR="/home/coder/.vscode-server/data/Machine" + mkdir -p "$SETTINGS_DIR" + cat <<'EOF' > "$SETTINGS_DIR/settings.json" + ${var.settings} + EOF + %{ endif ~} + EOT +} + resource "coder_app" "vscode" { agent_id = var.agent_id external = true @@ -67,4 +126,4 @@ resource "coder_app" "vscode" { output "vscode_url" { value = coder_app.vscode.url description = "VS Code Desktop URL." -} +} \ No newline at end of file