From f5d41520cfaddde9c2cb719e645cb065fcb5fecc Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Fri, 3 May 2024 10:36:28 -0700 Subject: [PATCH] feat(vscode-web): add offline, use_cached, extensions_dir and auto_install_extensions (#235) --- vscode-web/main.test.ts | 42 ++++++++++++++++++++++++++ vscode-web/main.tf | 41 +++++++++++++++++++++++++ vscode-web/run.sh | 67 +++++++++++++++++++++++++++++++++-------- 3 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 vscode-web/main.test.ts diff --git a/vscode-web/main.test.ts b/vscode-web/main.test.ts new file mode 100644 index 00000000..d8e0e68e --- /dev/null +++ b/vscode-web/main.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, it } from "bun:test"; +import { runTerraformApply, runTerraformInit } from "../test"; + +describe("vscode-web", async () => { + await runTerraformInit(import.meta.dir); + + it("accept_license should be set to true", () => { + const t = async () => { + await runTerraformApply(import.meta.dir, { + agent_id: "foo", + accept_license: "false", + }); + }; + expect(t).toThrow("Invalid value for variable"); + }); + + it("use_cached and offline can not be used together", () => { + const t = async () => { + await runTerraformApply(import.meta.dir, { + agent_id: "foo", + accept_license: "true", + use_cached: "true", + offline: "true", + }); + }; + expect(t).toThrow("Offline and Use Cached can not be used together"); + }); + + it("offline and extensions can not be used together", () => { + const t = async () => { + await runTerraformApply(import.meta.dir, { + agent_id: "foo", + accept_license: "true", + offline: "true", + extensions: '["1", "2"]', + }); + }; + expect(t).toThrow("Offline mode does not allow extensions to be installed"); + }); + + // More tests depend on shebang refactors +}); diff --git a/vscode-web/main.tf b/vscode-web/main.tf index dd2ab3b4..084f8306 100644 --- a/vscode-web/main.tf +++ b/vscode-web/main.tf @@ -97,6 +97,30 @@ variable "settings" { default = {} } +variable "offline" { + type = bool + description = "Just run VS Code Web in the background, don't fetch it from the internet." + default = false +} + +variable "use_cached" { + type = bool + description = "Uses cached copy of VS Code Web in the background, otherwise fetches it from internet." + default = false +} + +variable "extensions_dir" { + type = string + description = "Override the directory to store extensions in." + default = "" +} + +variable "auto_install_extensions" { + type = bool + description = "Automatically install recommended extensions when VS Code Web starts." + default = false +} + resource "coder_script" "vscode-web" { agent_id = var.agent_id display_name = "VS Code Web" @@ -109,8 +133,25 @@ resource "coder_script" "vscode-web" { TELEMETRY_LEVEL : var.telemetry_level, // This is necessary otherwise the quotes are stripped! SETTINGS : replace(jsonencode(var.settings), "\"", "\\\""), + OFFLINE : var.offline, + USE_CACHED : var.use_cached, + EXTENSIONS_DIR : var.extensions_dir, + FOLDER : var.folder, + AUTO_INSTALL_EXTENSIONS : var.auto_install_extensions, }) run_on_start = true + + lifecycle { + precondition { + condition = !var.offline || length(var.extensions) == 0 + error_message = "Offline mode does not allow extensions to be installed" + } + + precondition { + condition = !var.offline || !var.use_cached + error_message = "Offline and Use Cached can not be used together" + } + } } resource "coder_app" "vscode-web" { diff --git a/vscode-web/run.sh b/vscode-web/run.sh index 491906f8..ce8782f5 100755 --- a/vscode-web/run.sh +++ b/vscode-web/run.sh @@ -2,6 +2,40 @@ BOLD='\033[0;1m' EXTENSIONS=("${EXTENSIONS}") +VSCODE_WEB="${INSTALL_PREFIX}/bin/code-server" + +# Set extension directory +EXTENSION_ARG="" +if [ -n "${EXTENSIONS_DIR}" ]; then + EXTENSION_ARG="--extensions-dir=${EXTENSIONS_DIR}" +fi + +run_vscode_web() { + echo "👷 Running $VSCODE_WEB serve-local $EXTENSION_ARG --port ${PORT} --host 127.0.0.1 --accept-server-license-terms --without-connection-token --telemetry-level ${TELEMETRY_LEVEL} in the background..." + echo "Check logs at ${LOG_PATH}!" + "$VSCODE_WEB" serve-local "$EXTENSION_ARG" --port "${PORT}" --host 127.0.0.1 --accept-server-license-terms --without-connection-token --telemetry-level "${TELEMETRY_LEVEL}" > "${LOG_PATH}" 2>&1 & +} + +# Check if the settings file exists... +if [ ! -f ~/.vscode-server/data/Machine/settings.json ]; then + echo "⚙️ Creating settings file..." + mkdir -p ~/.vscode-server/data/Machine + echo "${SETTINGS}" > ~/.vscode-server/data/Machine/settings.json +fi + +# Check if vscode-server is already installed for offline or cached mode +if [ -f "$VSCODE_WEB" ]; then + if [ "${OFFLINE}" = true ] || [ "${USE_CACHED}" = true ]; then + echo "🥳 Found a copy of VS Code Web" + run_vscode_web + exit 0 + fi +fi +# Offline mode always expects a copy of vscode-server to be present +if [ "${OFFLINE}" = true ]; then + echo "Failed to find a copy of VS Code Web" + exit 1 +fi # Create install prefix mkdir -p ${INSTALL_PREFIX} @@ -26,9 +60,7 @@ if [ $? -ne 0 ]; then echo "Failed to install Microsoft Visual Studio Code Server: $output" exit 1 fi -printf "$${BOLD}Microsoft Visual Studio Code Server has been installed.\n" - -VSCODE_SERVER="${INSTALL_PREFIX}/bin/code-server" +printf "$${BOLD}VS Code Web has been installed.\n" # Install each extension... IFS=',' read -r -a EXTENSIONLIST <<< "$${EXTENSIONS}" @@ -37,20 +69,31 @@ for extension in "$${EXTENSIONLIST[@]}"; do continue fi printf "🧩 Installing extension $${CODE}$extension$${RESET}...\n" - output=$($VSCODE_SERVER --install-extension "$extension" --force) + output=$($VSCODE_WEB "$EXTENSION_ARG" --install-extension "$extension" --force) if [ $? -ne 0 ]; then echo "Failed to install extension: $extension: $output" exit 1 fi done -# Check if the settings file exists... -if [ ! -f ~/.vscode-server/data/Machine/settings.json ]; then - echo "⚙️ Creating settings file..." - mkdir -p ~/.vscode-server/data/Machine - echo "${SETTINGS}" > ~/.vscode-server/data/Machine/settings.json +if [ "${AUTO_INSTALL_EXTENSIONS}" = true ]; then + if ! command -v jq > /dev/null; then + echo "jq is required to install extensions from a workspace file." + exit 0 + fi + + WORKSPACE_DIR="$HOME" + if [ -n "${FOLDER}" ]; then + WORKSPACE_DIR="${FOLDER}" + fi + + if [ -f "$WORKSPACE_DIR/.vscode/extensions.json" ]; then + printf "🧩 Installing extensions from %s/.vscode/extensions.json...\n" "$WORKSPACE_DIR" + extensions=$(jq -r '.recommendations[]' "$WORKSPACE_DIR"/.vscode/extensions.json) + for extension in $extensions; do + $VSCODE_WEB "$EXTENSION_ARG" --install-extension "$extension" --force + done + fi fi -echo "👷 Running ${INSTALL_PREFIX}/bin/code-server serve-local --port ${PORT} --host 127.0.0.1 --accept-server-license-terms serve-local --without-connection-token --telemetry-level ${TELEMETRY_LEVEL} in the background..." -echo "Check logs at ${LOG_PATH}!" -"${INSTALL_PREFIX}/bin/code-server" serve-local --port "${PORT}" --host 127.0.0.1 --accept-server-license-terms serve-local --without-connection-token --telemetry-level "${TELEMETRY_LEVEL}" > "${LOG_PATH}" 2>&1 & +run_vscode_web