diff --git a/registry/coder/modules/code-server/README.md b/registry/coder/modules/code-server/README.md index af359d5f1..c6dbbb8e7 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.2" + 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.2" + 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.2" + version = "1.6.0" agent_id = coder_agent.example.id extensions = [ "dracula-theme.theme-dracula" @@ -53,6 +53,40 @@ module "code-server" { Enter the `.` into the extensions array and code-server will automatically install on start. +### Install Workspace Recommendations After a Repository Clone + +When `auto_install_extensions` reads recommendations from a repository cloned during workspace startup, coordinate the scripts so code-server waits for the clone to finish: + +```tf +module "git-clone" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/git-clone/coder" + version = "2.0.3" + agent_id = coder_agent.main.id + url = "https://github.com/example/project" + post_clone_script = <<-EOT + coder exp sync start git-clone + coder exp sync complete git-clone + EOT +} + +module "code-server" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/code-server/coder" + version = "1.6.0" + agent_id = coder_agent.main.id + folder = module.git-clone[count.index].repo_dir + auto_install_extensions = true + pre_auto_install_extensions_script = <<-EOT + coder exp sync want code-server-auto-install git-clone + coder exp sync start code-server-auto-install + coder exp sync complete code-server-auto-install + EOT +} +``` + +Startup coordination is experimental and requires Coder v2.30 or later. + ### Pre-configure User Settings Configure VS Code's [User settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settings-json-file). These settings are merged with any existing user settings on startup: @@ -61,7 +95,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.2" + version = "1.6.0" agent_id = coder_agent.example.id extensions = ["dracula-theme.theme-dracula"] settings = { @@ -81,7 +115,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.2" + version = "1.6.0" agent_id = coder_agent.example.id extensions = ["dracula-theme.theme-dracula", "ms-azuretools.vscode-docker"] } @@ -95,7 +129,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.2" + version = "1.6.0" agent_id = coder_agent.example.id workspace = "/home/coder/project/my.code-workspace" } @@ -109,7 +143,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.2" + version = "1.6.0" agent_id = coder_agent.example.id additional_args = "--disable-workspace-trust" } @@ -125,7 +159,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.2" + version = "1.6.0" agent_id = coder_agent.example.id use_cached = true extensions = ["dracula-theme.theme-dracula", "ms-azuretools.vscode-docker"] @@ -138,7 +172,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.2" + version = "1.6.0" agent_id = coder_agent.example.id offline = true } diff --git a/registry/coder/modules/code-server/main.test.ts b/registry/coder/modules/code-server/main.test.ts index c5a729109..3b93e09bf 100644 --- a/registry/coder/modules/code-server/main.test.ts +++ b/registry/coder/modules/code-server/main.test.ts @@ -292,6 +292,61 @@ JSONCEOF`, expect(result.stdout).toContain("INSTALLED:esbenp.prettier-vscode"); }); + it("runs a preparation script before auto-installing extensions", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + use_cached: true, + auto_install_extensions: true, + folder: "/workspace/project", + pre_auto_install_extensions_script: ` +for attempt in 1 2 3 4 5; do + if [ -f /workspace/project/.vscode/extensions.json ]; then + exit 0 + fi + sleep 1 +done +exit 1`, + }); + + const containerId = await runContainer("ubuntu:22.04"); + cleanupContainers.push(containerId); + + await execContainer(containerId, [ + "bash", + "-c", + `cat > /usr/local/bin/jq << 'JQEOF' +#!/bin/sh +cat > /dev/null +printf 'ms-python.python\n' +JQEOF +chmod +x /usr/local/bin/jq +mkdir -p /tmp/code-server/bin && cat > /tmp/code-server/bin/code-server << 'MOCKEOF' +${MOCK_CODE_SERVER} +MOCKEOF +chmod +x /tmp/code-server/bin/code-server`, + ]); + await execContainer(containerId, [ + "bash", + "-c", + `nohup bash -c 'sleep 2 +mkdir -p /workspace/project/.vscode +cat > /workspace/project/.vscode/extensions.json << 'JSONEOF' +{"recommendations":["ms-python.python"]} +JSONEOF +' > /tmp/delayed-workspace.log 2>&1 &`, + ]); + + const script = findResourceInstance(state, "coder_script"); + const result = await execContainer(containerId, [ + "bash", + "-c", + script.script, + ]); + + expect(result.exitCode).toBe(0); + expect(result.stdout).toContain("INSTALLED:ms-python.python"); + }); + it("does not error on an extensions.json without a recommendations key", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", diff --git a/registry/coder/modules/code-server/main.tf b/registry/coder/modules/code-server/main.tf index 71db40e22..f1f21ef85 100644 --- a/registry/coder/modules/code-server/main.tf +++ b/registry/coder/modules/code-server/main.tf @@ -138,6 +138,12 @@ variable "auto_install_extensions" { default = false } +variable "pre_auto_install_extensions_script" { + type = string + description = "A Bash script to run before reading workspace extension recommendations. Use this to coordinate with tasks such as repository cloning." + default = "" +} + variable "subdomain" { type = bool description = <<-EOT @@ -168,8 +174,9 @@ variable "additional_args" { } locals { - settings_b64 = var.settings != {} ? base64encode(jsonencode(var.settings)) : "" - machine_settings_b64 = var.machine_settings != {} ? base64encode(jsonencode(var.machine_settings)) : "" + settings_b64 = var.settings != {} ? base64encode(jsonencode(var.settings)) : "" + machine_settings_b64 = var.machine_settings != {} ? base64encode(jsonencode(var.machine_settings)) : "" + pre_auto_install_extensions_script_b64 = var.pre_auto_install_extensions_script != "" ? base64encode(var.pre_auto_install_extensions_script) : "" } resource "coder_script" "code-server" { @@ -192,6 +199,7 @@ resource "coder_script" "code-server" { FOLDER : var.folder, WORKSPACE : var.workspace, AUTO_INSTALL_EXTENSIONS : var.auto_install_extensions, + PRE_AUTO_INSTALL_EXTENSIONS_SCRIPT_B64 : local.pre_auto_install_extensions_script_b64, ADDITIONAL_ARGS : var.additional_args, }) run_on_start = true diff --git a/registry/coder/modules/code-server/run.sh b/registry/coder/modules/code-server/run.sh index 5d3d09092..2abcbdf26 100644 --- a/registry/coder/modules/code-server/run.sh +++ b/registry/coder/modules/code-server/run.sh @@ -190,6 +190,18 @@ s/,[^]}"]*([]}])/\1/g' } if [ "${AUTO_INSTALL_EXTENSIONS}" = true ]; then + PRE_AUTO_INSTALL_EXTENSIONS_SCRIPT_B64='${PRE_AUTO_INSTALL_EXTENSIONS_SCRIPT_B64}' + if [ -n "$PRE_AUTO_INSTALL_EXTENSIONS_SCRIPT_B64" ]; then + if ! PRE_AUTO_INSTALL_EXTENSIONS_SCRIPT=$(printf '%s' "$PRE_AUTO_INSTALL_EXTENSIONS_SCRIPT_B64" | base64 -d 2> /dev/null); then + echo "Failed to decode pre-auto-install extensions script." + exit 1 + fi + if ! bash -c "$PRE_AUTO_INSTALL_EXTENSIONS_SCRIPT"; then + echo "Pre-auto-install extensions script failed." + exit 1 + fi + fi + if ! command -v jq > /dev/null; then echo "jq is required to install extensions from a workspace file." exit 0