From 8ec817e33cf2b0f1f03b90be5895d32b09e3cad4 Mon Sep 17 00:00:00 2001 From: DevelopmentCats Date: Tue, 24 Feb 2026 13:26:19 -0600 Subject: [PATCH] refactor: update VS Code Web settings handling to merge with existing settings - Changed test description to reflect merging behavior of settings. - Updated Terraform variable description to clarify merging of settings. - Implemented a new function in the run script to merge settings using jq or Python3. - Adjusted the settings file creation logic to merge new settings with existing ones. - Updated README to reflect changes in settings configuration and merging requirements. --- registry/coder/modules/vscode-web/README.md | 6 ++- .../coder/modules/vscode-web/main.test.ts | 19 +++++-- registry/coder/modules/vscode-web/main.tf | 9 ++-- registry/coder/modules/vscode-web/run.sh | 51 +++++++++++++++++-- 4 files changed, 70 insertions(+), 15 deletions(-) diff --git a/registry/coder/modules/vscode-web/README.md b/registry/coder/modules/vscode-web/README.md index 59a98de4..c8d76645 100644 --- a/registry/coder/modules/vscode-web/README.md +++ b/registry/coder/modules/vscode-web/README.md @@ -51,9 +51,9 @@ module "vscode-web" { } ``` -### Pre-configure Settings +### Pre-configure Machine Settings -Configure VS Code's [settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settings-json-file) file: +Configure VS Code's [Machine settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settings-json-file). These settings are merged with any existing machine settings on startup: ```tf module "vscode-web" { @@ -69,6 +69,8 @@ module "vscode-web" { } ``` +> **Note:** Merging settings requires `jq` or `python3`. If neither is available, existing machine settings will be preserved. User settings configured through the VS Code UI are stored in browser local storage and will not persist across different browsers or devices. + ### Open an existing workspace on startup To open an existing workspace on startup the `workspace` parameter can be used to represent a path on disk to a `code-workspace` file. diff --git a/registry/coder/modules/vscode-web/main.test.ts b/registry/coder/modules/vscode-web/main.test.ts index 2b7ad9a2..54fcb5d3 100644 --- a/registry/coder/modules/vscode-web/main.test.ts +++ b/registry/coder/modules/vscode-web/main.test.ts @@ -500,7 +500,7 @@ chmod +x /usr/local/bin/code`, expect(parentDirResult.stdout).toContain("Machine"); }); - it("does not overwrite existing settings file", async () => { + it("merges settings with existing settings file", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", accept_license: true, @@ -510,7 +510,15 @@ chmod +x /usr/local/bin/code`, const containerId = await runContainer("ubuntu:22.04"); cleanupContainers.push(containerId); - // Create a mock code CLI + // Install jq and create mock code CLI + await execContainer(containerId, ["apt-get", "update", "-qq"]); + await execContainer(containerId, [ + "apt-get", + "install", + "-y", + "-qq", + "jq", + ]); await execContainer(containerId, [ "bash", "-c", @@ -532,17 +540,18 @@ chmod +x /usr/local/bin/code`, await execContainer(containerId, ["bash", "-c", script.script]); - // Check that existing settings file was NOT overwritten + // Check that settings were merged (both existing and new should be present) const settingsResult = await execContainer(containerId, [ "cat", "/root/.vscode-server/data/Machine/settings.json", ]); expect(settingsResult.exitCode).toBe(0); - // Should contain existing setting, not the new one + // Should contain both existing and new settings expect(settingsResult.stdout).toContain("existing.setting"); expect(settingsResult.stdout).toContain("existing_value"); - expect(settingsResult.stdout).not.toContain("new.setting"); + expect(settingsResult.stdout).toContain("new.setting"); + expect(settingsResult.stdout).toContain("new_value"); }); it("creates valid JSON settings file", async () => { diff --git a/registry/coder/modules/vscode-web/main.tf b/registry/coder/modules/vscode-web/main.tf index 88c31210..c9ac9c4a 100644 --- a/registry/coder/modules/vscode-web/main.tf +++ b/registry/coder/modules/vscode-web/main.tf @@ -99,7 +99,7 @@ variable "group" { variable "settings" { type = any - description = "A map of settings to apply to VS Code web." + description = "A map of settings to apply to VS Code Web's Machine settings. These settings are merged with any existing machine settings on startup." default = {} } @@ -167,6 +167,10 @@ variable "commit_id" { data "coder_workspace_owner" "me" {} data "coder_workspace" "me" {} +locals { + settings_b64 = var.settings != {} ? base64encode(jsonencode(var.settings)) : "" +} + resource "coder_script" "vscode-web" { agent_id = var.agent_id display_name = "VS Code Web" @@ -177,8 +181,7 @@ resource "coder_script" "vscode-web" { INSTALL_PREFIX : var.install_prefix, EXTENSIONS : join(",", var.extensions), TELEMETRY_LEVEL : var.telemetry_level, - // This is necessary otherwise the quotes are stripped! - SETTINGS : replace(jsonencode(var.settings), "\"", "\\\""), + SETTINGS_B64 : local.settings_b64, OFFLINE : var.offline, USE_CACHED : var.use_cached, DISABLE_TRUST : var.disable_trust, diff --git a/registry/coder/modules/vscode-web/run.sh b/registry/coder/modules/vscode-web/run.sh index 6029053c..07acb5ba 100644 --- a/registry/coder/modules/vscode-web/run.sh +++ b/registry/coder/modules/vscode-web/run.sh @@ -5,6 +5,47 @@ RESET='\033[0m' CODE='\033[36;40;1m' EXTENSIONS=("${EXTENSIONS}") +# Merge settings from module with existing settings file +# Uses jq if available, falls back to Python3 for deep merge +merge_settings() { + local new_settings="$1" + local settings_file="$2" + + if [ -z "$new_settings" ] || [ "$new_settings" = "{}" ]; then + return 0 + fi + + if [ ! -f "$settings_file" ]; then + mkdir -p "$(dirname "$settings_file")" + printf '%s\n' "$new_settings" > "$settings_file" + printf "Creating settings file...\n" + return 0 + fi + + local tmpfile + tmpfile="$(mktemp)" + + if command -v jq > /dev/null 2>&1; then + if jq -s '.[0] * .[1]' "$settings_file" <(printf '%s\n' "$new_settings") > "$tmpfile" 2> /dev/null; then + mv "$tmpfile" "$settings_file" + printf "Merging settings...\n" + return 0 + fi + fi + + if command -v python3 > /dev/null 2>&1; then + if python3 -c "import json,sys;m=lambda a,b:{**a,**{k:m(a[k],v)if k in a and type(a[k])==type(v)==dict else v for k,v in b.items()}};print(json.dumps(m(json.load(open(sys.argv[1])),json.loads(sys.argv[2])),indent=2))" "$settings_file" "$new_settings" > "$tmpfile" 2> /dev/null; then + mv "$tmpfile" "$settings_file" + printf "Merging settings...\n" + return 0 + fi + fi + + rm -f "$tmpfile" + printf "Warning: Could not merge settings. Keeping existing settings.\n" + return 0 +} + # Set extension directory argument EXTENSION_ARG="" if [ -n "${EXTENSIONS_DIR}" ]; then @@ -251,11 +292,11 @@ install_extensions() { fi } -# Create settings file if it doesn't exist -if [ ! -f ~/.vscode-server/data/Machine/settings.json ]; then - printf "Creating settings file...\n" - mkdir -p ~/.vscode-server/data/Machine - echo "${SETTINGS}" > ~/.vscode-server/data/Machine/settings.json +# Apply machine settings (merge with existing if present) +SETTINGS_B64='${SETTINGS_B64}' +if [ -n "$SETTINGS_B64" ]; then + SETTINGS_JSON="$(echo -n "$SETTINGS_B64" | base64 -d)" + merge_settings "$SETTINGS_JSON" ~/.vscode-server/data/Machine/settings.json fi # Determine which command to use