From 81bd6ed4527dd55c1516d635aab55b2628b2566a Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Jul 2026 14:08:10 -0500 Subject: [PATCH 1/7] enable run.sh debug logging --- registry/coder/modules/code-server/run.sh | 1 + 1 file changed, 1 insertion(+) mode change 100644 => 100755 registry/coder/modules/code-server/run.sh diff --git a/registry/coder/modules/code-server/run.sh b/registry/coder/modules/code-server/run.sh old mode 100644 new mode 100755 index 5ac2d12a2..0aaf5ba5b --- a/registry/coder/modules/code-server/run.sh +++ b/registry/coder/modules/code-server/run.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +set -x EXTENSIONS=("${EXTENSIONS}") BOLD='\033[0;1m' From 902c21c5a3048b3c708a250cb1aaee90cf98651b Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Jul 2026 14:47:14 -0500 Subject: [PATCH 2/7] enable dynamic script debug mode --- registry/coder/modules/code-server/main.tf | 7 +++++++ registry/coder/modules/code-server/run.sh | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/registry/coder/modules/code-server/main.tf b/registry/coder/modules/code-server/main.tf index 71db40e22..7c73dee5c 100644 --- a/registry/coder/modules/code-server/main.tf +++ b/registry/coder/modules/code-server/main.tf @@ -167,6 +167,12 @@ variable "additional_args" { default = "" } +variable "debug" { + type = bool + description = "Enable debug mode for the code-server startup script, which dynamically runs set -x." + default = false +} + locals { settings_b64 = var.settings != {} ? base64encode(jsonencode(var.settings)) : "" machine_settings_b64 = var.machine_settings != {} ? base64encode(jsonencode(var.machine_settings)) : "" @@ -193,6 +199,7 @@ resource "coder_script" "code-server" { WORKSPACE : var.workspace, AUTO_INSTALL_EXTENSIONS : var.auto_install_extensions, ADDITIONAL_ARGS : var.additional_args, + DEBUG : var.debug, }) run_on_start = true diff --git a/registry/coder/modules/code-server/run.sh b/registry/coder/modules/code-server/run.sh index 0aaf5ba5b..797094ef8 100755 --- a/registry/coder/modules/code-server/run.sh +++ b/registry/coder/modules/code-server/run.sh @@ -1,5 +1,9 @@ #!/usr/bin/env bash -set -x + +# Enable debug mode dynamically if the flag is true +if [ "${DEBUG}" = true ]; then + set -x +fi EXTENSIONS=("${EXTENSIONS}") BOLD='\033[0;1m' From df2fd9877f6a65c8eb63a292decd0d3aaf6937b7 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Jul 2026 14:48:57 -0500 Subject: [PATCH 3/7] fail on invalid existing settings.json --- registry/coder/modules/code-server/run.sh | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/registry/coder/modules/code-server/run.sh b/registry/coder/modules/code-server/run.sh index 797094ef8..64f1406b3 100755 --- a/registry/coder/modules/code-server/run.sh +++ b/registry/coder/modules/code-server/run.sh @@ -43,19 +43,32 @@ merge_settings() { local tmpfile tmpfile="$(mktemp)" + # Validate existing JSON if we are attempting a merge 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 + if ! jq empty "$settings_file" > /dev/null 2>&1; then + printf "❌ Error: Existing settings file %s contains invalid JSON.\n" "$settings_file" + return 1 + elif 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 + else + rm -f "$tmpfile" + printf "❌ Error: JQ failed to write the new settings file %s.\n" "$settings_file" + return 1 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 + elif command -v python3 > /dev/null 2>&1; then + if ! python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$settings_file" > /dev/null 2>&1; then + printf "❌ Error: Existing settings file %s contains invalid JSON.\n" "$settings_file" + return 1 + elif 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 + else + rm -f "$tmpfile" + printf "❌ Error: Python failed to write the new settings file %s.\n" "$settings_file" + return 1 fi fi From 8b22dffc42eea26a151892191bca12826ea6d525 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Jul 2026 14:50:13 -0500 Subject: [PATCH 4/7] allow user and machine settings overwrite --- registry/coder/modules/code-server/main.tf | 14 ++++++++++++++ registry/coder/modules/code-server/run.sh | 16 ++++++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/registry/coder/modules/code-server/main.tf b/registry/coder/modules/code-server/main.tf index 7c73dee5c..1746bf79a 100644 --- a/registry/coder/modules/code-server/main.tf +++ b/registry/coder/modules/code-server/main.tf @@ -44,12 +44,24 @@ variable "settings" { default = {} } +variable "overwrite_settings" { + type = bool + description = "If true, overwrites the User settings.json instead of attempting to merge." + default = false +} + variable "machine_settings" { type = any description = "A map of template level machine settings to apply to code-server. These settings are merged with any existing machine settings on startup." default = {} } +variable "overwrite_machine_settings" { + type = bool + description = "If true, overwrites the Machine settings.json instead of attempting to merge." + default = false +} + variable "folder" { type = string description = "The folder to open in code-server." @@ -190,7 +202,9 @@ resource "coder_script" "code-server" { LOG_PATH : var.log_path, INSTALL_PREFIX : var.install_prefix, SETTINGS_B64 : local.settings_b64, + OVERWRITE_SETTINGS : var.overwrite_settings, MACHINE_SETTINGS_B64 : local.machine_settings_b64, + OVERWRITE_MACHINE_SETTINGS : var.overwrite_machine_settings, OFFLINE : var.offline, USE_CACHED : var.use_cached, USE_CACHED_EXTENSIONS : var.use_cached_extensions, diff --git a/registry/coder/modules/code-server/run.sh b/registry/coder/modules/code-server/run.sh index 64f1406b3..d26937f00 100755 --- a/registry/coder/modules/code-server/run.sh +++ b/registry/coder/modules/code-server/run.sh @@ -28,15 +28,17 @@ function run_code_server() { merge_settings() { local new_settings="$1" local settings_file="$2" + local overwrite="$3" if [ -z "$new_settings" ] || [ "$new_settings" = "{}" ]; then return 0 fi - if [ ! -f "$settings_file" ]; then + # If file doesn't exist OR overwrite is explicitly true, write and skip merge + if [ ! -f "$settings_file" ] || [ "$overwrite" = "true" ]; then mkdir -p "$(dirname "$settings_file")" printf '%s\n' "$new_settings" > "$settings_file" - printf "⚙️ Creating settings file...\n" + printf "⚙️ Creating or overwriting settings file...\n" return 0 fi @@ -77,21 +79,23 @@ merge_settings() { return 0 } -# Apply user settings (merge with existing if present) +# Apply user settings (merge or overwrite based on flag) SETTINGS_B64='${SETTINGS_B64}' if [ -n "$SETTINGS_B64" ]; then if SETTINGS_JSON="$(echo -n "$SETTINGS_B64" | base64 -d 2> /dev/null)" && [ -n "$SETTINGS_JSON" ]; then - merge_settings "$SETTINGS_JSON" ~/.local/share/code-server/User/settings.json + # Return 1 triggers exit 1 to halt execution if validation fails + merge_settings "$SETTINGS_JSON" ~/.local/share/code-server/User/settings.json "${OVERWRITE_SETTINGS}" || exit 1 else printf "Warning: Failed to decode settings. Skipping settings configuration.\n" fi fi -# Apply machine settings (merge with existing if present) +# Apply machine settings (merge or overwrite based on flag) MACHINE_SETTINGS_B64='${MACHINE_SETTINGS_B64}' if [ -n "$MACHINE_SETTINGS_B64" ]; then if MACHINE_SETTINGS_JSON="$(echo -n "$MACHINE_SETTINGS_B64" | base64 -d 2> /dev/null)" && [ -n "$MACHINE_SETTINGS_JSON" ]; then - merge_settings "$MACHINE_SETTINGS_JSON" ~/.local/share/code-server/Machine/settings.json + # Return 1 triggers exit 1 to halt execution if validation fails + merge_settings "$MACHINE_SETTINGS_JSON" ~/.local/share/code-server/Machine/settings.json "${OVERWRITE_MACHINE_SETTINGS}" || exit 1 else printf "Warning: Failed to decode machine settings. Skipping machine settings configuration.\n" fi From 356b5d5de2101188085f935e7625211828f70ac1 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Jul 2026 15:51:00 -0500 Subject: [PATCH 5/7] cleanup function and document the steps --- registry/coder/modules/code-server/run.sh | 101 +++++++++++++--------- 1 file changed, 61 insertions(+), 40 deletions(-) diff --git a/registry/coder/modules/code-server/run.sh b/registry/coder/modules/code-server/run.sh index d26937f00..b8ff56965 100755 --- a/registry/coder/modules/code-server/run.sh +++ b/registry/coder/modules/code-server/run.sh @@ -24,59 +24,80 @@ function run_code_server() { $CODE_SERVER "$EXTENSION_ARG" --auth none --port "${PORT}" --app-name "${APP_NAME}" ${ADDITIONAL_ARGS} > "${LOG_PATH}" 2>&1 & } -# Merge settings from module with existing settings file -merge_settings() { +# Merge, validate, save, and format settings.json +save_settings() { local new_settings="$1" local settings_file="$2" - local overwrite="$3" + local overwrite="$3" if [ -z "$new_settings" ] || [ "$new_settings" = "{}" ]; then return 0 fi - # If file doesn't exist OR overwrite is explicitly true, write and skip merge - if [ ! -f "$settings_file" ] || [ "$overwrite" = "true" ]; then - mkdir -p "$(dirname "$settings_file")" - printf '%s\n' "$new_settings" > "$settings_file" - printf "⚙️ Creating or overwriting settings file...\n" - return 0 + local tool="" + if command -v jq > /dev/null 2>&1; then + tool="jq" + elif command -v python3 > /dev/null 2>&1; then + tool="python3" fi + mkdir -p "$(dirname "$settings_file")" local tmpfile tmpfile="$(mktemp)" - # Validate existing JSON if we are attempting a merge - if command -v jq > /dev/null 2>&1; then - if ! jq empty "$settings_file" > /dev/null 2>&1; then - printf "❌ Error: Existing settings file %s contains invalid JSON.\n" "$settings_file" - return 1 - elif 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 - else - rm -f "$tmpfile" - printf "❌ Error: JQ failed to write the new settings file %s.\n" "$settings_file" - return 1 - fi - elif command -v python3 > /dev/null 2>&1; then - if ! python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$settings_file" > /dev/null 2>&1; then - printf "❌ Error: Existing settings file %s contains invalid JSON.\n" "$settings_file" - return 1 - elif 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 - else - rm -f "$tmpfile" - printf "❌ Error: Python failed to write the new settings file %s.\n" "$settings_file" - return 1 + # Create or Replace settings.json + if [ ! -f "$settings_file" ] || [ "$overwrite" = "true" ]; then + if [ "$tool" = "jq" ]; then + jq . <(printf '%s' "$new_settings") > "$tmpfile" 2> /dev/null || printf '%s\n' "$new_settings" > "$tmpfile" + elif [ "$tool" = "python3" ]; then + python3 -c "import json,sys; print(json.dumps(json.loads(sys.argv[1]), indent=2))" "$new_settings" > "$tmpfile" 2> /dev/null || printf '%s\n' "$new_settings" > "$tmpfile" + else + printf '%s\n' "$new_settings" > "$tmpfile" fi + mv "$tmpfile" "$settings_file" + printf "⚙️ Creating or replacing settings file...\n" + return 0 + fi + + # Check if required tooling exists to facilitate the merge + if [ -z "$tool" ]; then + rm -f "$tmpfile" + printf "Warning: Could not merge settings (jq or python3 required). Keeping existing settings.\n" + return 0 + fi + + # Validate existing JSON + local is_valid=0 + if [ "$tool" = "jq" ]; then + jq empty "$settings_file" > /dev/null 2>&1 || is_valid=1 + else + python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$settings_file" > /dev/null 2>&1 || is_valid=1 + fi + + if [ $is_valid -ne 0 ]; then + rm -f "$tmpfile" + printf "❌ Error: Existing settings file %s contains invalid JSON.\n" "$settings_file" + return 1 fi - rm -f "$tmpfile" - printf "Warning: Could not merge settings (jq or python3 required). Keeping existing settings.\n" - return 0 + # Merge to temp settings.json + local merge_success=0 + if [ "$tool" = "jq" ]; then + jq -s '.[0] * .[1]' "$settings_file" <(printf '%s\n' "$new_settings") > "$tmpfile" 2> /dev/null || merge_success=1 + else + 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 || merge_success=1 + fi + + # Update settings.json with the newly merged configuration + if [ $merge_success -eq 0 ]; then + mv "$tmpfile" "$settings_file" + printf "⚙️ Merging settings...\n" + return 0 + else + rm -f "$tmpfile" + printf "❌ Error: %s failed to write the new settings file %s.\n" "$tool" "$settings_file" + return 1 + fi } # Apply user settings (merge or overwrite based on flag) @@ -84,7 +105,7 @@ SETTINGS_B64='${SETTINGS_B64}' if [ -n "$SETTINGS_B64" ]; then if SETTINGS_JSON="$(echo -n "$SETTINGS_B64" | base64 -d 2> /dev/null)" && [ -n "$SETTINGS_JSON" ]; then # Return 1 triggers exit 1 to halt execution if validation fails - merge_settings "$SETTINGS_JSON" ~/.local/share/code-server/User/settings.json "${OVERWRITE_SETTINGS}" || exit 1 + save_settings "$SETTINGS_JSON" ~/.local/share/code-server/User/settings.json "${OVERWRITE_SETTINGS}" || exit 1 else printf "Warning: Failed to decode settings. Skipping settings configuration.\n" fi @@ -95,7 +116,7 @@ MACHINE_SETTINGS_B64='${MACHINE_SETTINGS_B64}' if [ -n "$MACHINE_SETTINGS_B64" ]; then if MACHINE_SETTINGS_JSON="$(echo -n "$MACHINE_SETTINGS_B64" | base64 -d 2> /dev/null)" && [ -n "$MACHINE_SETTINGS_JSON" ]; then # Return 1 triggers exit 1 to halt execution if validation fails - merge_settings "$MACHINE_SETTINGS_JSON" ~/.local/share/code-server/Machine/settings.json "${OVERWRITE_MACHINE_SETTINGS}" || exit 1 + save_settings "$MACHINE_SETTINGS_JSON" ~/.local/share/code-server/Machine/settings.json "${OVERWRITE_MACHINE_SETTINGS}" || exit 1 else printf "Warning: Failed to decode machine settings. Skipping machine settings configuration.\n" fi From a5ba9077fb77795003619777504ea973c56781cd Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Jul 2026 15:51:09 -0500 Subject: [PATCH 6/7] create tests --- .../code-server/code-server.tftest.hcl | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/registry/coder/modules/code-server/code-server.tftest.hcl b/registry/coder/modules/code-server/code-server.tftest.hcl index 209a0f39d..3079e6055 100644 --- a/registry/coder/modules/code-server/code-server.tftest.hcl +++ b/registry/coder/modules/code-server/code-server.tftest.hcl @@ -100,3 +100,36 @@ run "workspace_extension_rejected" { } expect_failures = [var.workspace] } + +run "debug_flag_enabled" { + command = plan + + variables { + agent_id = "foo" + debug = true + } +} + +run "overwrite_settings_flags" { + command = plan + + variables { + agent_id = "foo" + overwrite_settings = true + overwrite_machine_settings = true + } +} + +run "settings_jsonencode_with_interpolation" { + command = plan + + variables { + agent_id = "foo" + settings = { + "window.title" = "My Window" + } + machine_settings = { + "git.blame.editorDecoration.template" = "$${authorName}" + } + } +} From ceef0fb4d4fd79103d4cf425177059ddb53d6b41 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Jul 2026 18:31:13 -0500 Subject: [PATCH 7/7] chore: bump module versions (minor) --- registry/coder/modules/code-server/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/registry/coder/modules/code-server/README.md b/registry/coder/modules/code-server/README.md index d6dcd6bd7..ae0f60e62 100644 --- a/registry/coder/modules/code-server/README.md +++ b/registry/coder/modules/code-server/README.md @@ -14,7 +14,7 @@ Automatically install [code-server](https://github.com/coder/code-server) in a w module "code-server" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/code-server/coder" - version = "1.5.1" + version = "1.6.0" agent_id = coder_agent.example.id } ``` @@ -29,7 +29,7 @@ module "code-server" { module "code-server" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/code-server/coder" - version = "1.5.1" + version = "1.6.0" agent_id = coder_agent.example.id install_version = "4.106.3" } @@ -43,7 +43,7 @@ Install the Dracula theme from [OpenVSX](https://open-vsx.org/): module "code-server" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/code-server/coder" - version = "1.5.1" + version = "1.6.0" agent_id = coder_agent.example.id extensions = [ "dracula-theme.theme-dracula" @@ -61,7 +61,7 @@ Configure VS Code's [User settings.json](https://code.visualstudio.com/docs/gets module "code-server" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/code-server/coder" - version = "1.5.1" + version = "1.6.0" agent_id = coder_agent.example.id extensions = ["dracula-theme.theme-dracula"] settings = { @@ -81,7 +81,7 @@ Install multiple extensions from [OpenVSX](https://open-vsx.org/) by adding them module "code-server" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/code-server/coder" - version = "1.5.1" + version = "1.6.0" agent_id = coder_agent.example.id extensions = ["dracula-theme.theme-dracula", "ms-azuretools.vscode-docker"] } @@ -95,7 +95,7 @@ Open a [`.code-workspace`](https://coder.com/docs/code-server/FAQ#how-does-code- module "code-server" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/code-server/coder" - version = "1.5.1" + version = "1.6.0" agent_id = coder_agent.example.id workspace = "/home/coder/project/my.code-workspace" } @@ -109,7 +109,7 @@ You can pass additional command-line arguments to code-server using the `additio module "code-server" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/code-server/coder" - version = "1.5.1" + version = "1.6.0" agent_id = coder_agent.example.id additional_args = "--disable-workspace-trust" } @@ -125,7 +125,7 @@ Run an existing copy of code-server if found, otherwise download from GitHub: module "code-server" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/code-server/coder" - version = "1.5.1" + version = "1.6.0" agent_id = coder_agent.example.id use_cached = true extensions = ["dracula-theme.theme-dracula", "ms-azuretools.vscode-docker"] @@ -138,7 +138,7 @@ Just run code-server in the background, don't fetch it from GitHub: module "code-server" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/code-server/coder" - version = "1.5.1" + version = "1.6.0" agent_id = coder_agent.example.id offline = true }