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 } 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}" + } + } +} diff --git a/registry/coder/modules/code-server/main.tf b/registry/coder/modules/code-server/main.tf index 71db40e22..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." @@ -167,6 +179,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)) : "" @@ -184,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, @@ -193,6 +213,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 old mode 100644 new mode 100755 index 5ac2d12a2..b8ff56965 --- a/registry/coder/modules/code-server/run.sh +++ b/registry/coder/modules/code-server/run.sh @@ -1,5 +1,10 @@ #!/usr/bin/env bash +# Enable debug mode dynamically if the flag is true +if [ "${DEBUG}" = true ]; then + set -x +fi + EXTENSIONS=("${EXTENSIONS}") BOLD='\033[0;1m' CODE='\033[36;40;1m' @@ -19,61 +24,99 @@ 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" 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 + 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)" - 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 + # 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 - 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 + # 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 + + # 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 - rm -f "$tmpfile" - printf "Warning: Could not merge settings (jq or python3 required). Keeping existing settings.\n" - return 0 + # 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 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 + 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 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 + 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