diff --git a/code-server/main.tf b/code-server/main.tf index 30b92bc..8c0f1e0 100644 --- a/code-server/main.tf +++ b/code-server/main.tf @@ -95,6 +95,12 @@ variable "use_cached" { default = false } +variable "extensions_dir" { + type = string + description = "Override the directory to store extensions in." + default = "" +} + resource "coder_script" "code-server" { agent_id = var.agent_id display_name = "code-server" @@ -110,6 +116,7 @@ resource "coder_script" "code-server" { SETTINGS : replace(jsonencode(var.settings), "\"", "\\\""), OFFLINE : var.offline, USE_CACHED : var.use_cached, + EXTENSIONS_DIR : var.extensions_dir, }) run_on_start = true diff --git a/code-server/run.sh b/code-server/run.sh index 2444324..b04e131 100755 --- a/code-server/run.sh +++ b/code-server/run.sh @@ -6,10 +6,16 @@ CODE='\033[36;40;1m' RESET='\033[0m' CODE_SERVER="${INSTALL_PREFIX}/bin/code-server" +# Set extension directory +EXTENSION_ARG="" +if [ -n "${EXTENSIONS_DIR}" ]; then + EXTENSION_ARG="--extensions-dir=${EXTENSIONS_DIR}" +fi + function run_code_server() { echo "👷 Running code-server in the background..." echo "Check logs at ${LOG_PATH}!" - $CODE_SERVER --auth none --port "${PORT}" --app-name "${APP_NAME}" > "${LOG_PATH}" 2>&1 & + $CODE_SERVER "$EXTENSION_ARG" --auth none --port "${PORT}" --app-name "${APP_NAME}" > "${LOG_PATH}" 2>&1 & } # Check if the settings file exists... @@ -57,7 +63,7 @@ for extension in "$${EXTENSIONLIST[@]}"; do continue fi printf "🧩 Installing extension $${CODE}$extension$${RESET}...\n" - output=$($CODE_SERVER --install-extension "$extension") + output=$($CODE_SERVER "$EXTENSION_ARG" --install-extension "$extension") if [ $? -ne 0 ]; then echo "Failed to install extension: $extension: $output" exit 1 diff --git a/dotfiles/README.md b/dotfiles/README.md index eb64563..2939025 100644 --- a/dotfiles/README.md +++ b/dotfiles/README.md @@ -18,3 +18,16 @@ module "dotfiles" { agent_id = coder_agent.example.id } ``` + +## Setting a default dotfiles repository + +You can set a default dotfiles repository for all users by setting the `default_dotfiles_repo` variable: + +```tf +module "dotfiles" { + source = "registry.coder.com/modules/dotfiles/coder" + version = "1.0.12" + agent_id = coder_agent.example.id + default_dotfiles_repo = "https://github.com/coder/dotfiles" +} +``` diff --git a/dotfiles/main.test.ts b/dotfiles/main.test.ts index a075a27..6026719 100644 --- a/dotfiles/main.test.ts +++ b/dotfiles/main.test.ts @@ -19,6 +19,15 @@ describe("dotfiles", async () => { expect(state.outputs.dotfiles_uri.value).toBe(""); }); + it("set a default dotfiles_uri", async () => { + const default_dotfiles_uri = "foo"; + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + default_dotfiles_uri, + }); + expect(state.outputs.dotfiles_uri.value).toBe(default_dotfiles_uri); + }); + it("set custom order for coder_parameter", async () => { const order = 99; const state = await runTerraformApply(import.meta.dir, { diff --git a/dotfiles/main.tf b/dotfiles/main.tf index 3e19fb9..cf21864 100644 --- a/dotfiles/main.tf +++ b/dotfiles/main.tf @@ -14,6 +14,12 @@ variable "agent_id" { description = "The ID of a Coder agent." } +variable "default_dotfiles_uri" { + type = string + description = "The default dotfiles URI if the workspace user does not provide one." + default = "" +} + variable "coder_parameter_order" { type = number description = "The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order)." @@ -24,8 +30,8 @@ data "coder_parameter" "dotfiles_uri" { type = "string" name = "dotfiles_uri" display_name = "Dotfiles URL (optional)" - default = "" order = var.coder_parameter_order + default = var.default_dotfiles_uri description = "Enter a URL for a [dotfiles repository](https://dotfiles.github.io) to personalize your workspace" mutable = true icon = "/icon/dotfiles.svg" @@ -47,4 +53,9 @@ resource "coder_script" "personalize" { output "dotfiles_uri" { description = "Dotfiles URI" value = data.coder_parameter.dotfiles_uri.value +} + +output "dotfiles_default_uri" { + description = "Dotfiles Default URI" + value = var.default_dotfiles_uri } \ No newline at end of file diff --git a/git-config/main.test.ts b/git-config/main.test.ts new file mode 100644 index 0000000..1241956 --- /dev/null +++ b/git-config/main.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, it } from "bun:test"; +import { + runTerraformApply, + runTerraformInit, + testRequiredVariables, +} from "../test"; + +describe("git-config", async () => { + await runTerraformInit(import.meta.dir); + + testRequiredVariables(import.meta.dir, { + agent_id: "foo", + }); + + it("can run apply allow_username_change and allow_email_change disabled", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + allow_username_change: "false", + allow_email_change: "false", + }); + + const resources = state.resources; + expect(resources).toHaveLength(3); + expect(resources).toMatchObject([ + { type: "coder_workspace", name: "me" }, + { type: "coder_env", name: "git_author_name" }, + { type: "coder_env", name: "git_commmiter_name" }, + ]); + }); + + it("can run apply allow_email_change enabled", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + allow_email_change: "true", + }); + + const resources = state.resources; + expect(resources).toHaveLength(5); + expect(resources).toMatchObject([ + { type: "coder_parameter", name: "user_email" }, + { type: "coder_parameter", name: "username" }, + { type: "coder_workspace", name: "me" }, + { type: "coder_env", name: "git_author_name" }, + { type: "coder_env", name: "git_commmiter_name" }, + ]); + }); + + it("can run apply allow_email_change enabled", async () => { + const state = await runTerraformApply( + import.meta.dir, + { + agent_id: "foo", + allow_username_change: "false", + allow_email_change: "false", + }, + { CODER_WORKSPACE_OWNER_EMAIL: "foo@emai.com" }, + ); + + const resources = state.resources; + expect(resources).toHaveLength(5); + expect(resources).toMatchObject([ + { type: "coder_workspace", name: "me" }, + { type: "coder_env", name: "git_author_email" }, + { type: "coder_env", name: "git_author_name" }, + { type: "coder_env", name: "git_commmiter_email" }, + { type: "coder_env", name: "git_commmiter_name" }, + ]); + }); +}); diff --git a/git-config/main.tf b/git-config/main.tf index 050df61..2b9544a 100644 --- a/git-config/main.tf +++ b/git-config/main.tf @@ -78,10 +78,12 @@ resource "coder_env" "git_author_email" { agent_id = var.agent_id name = "GIT_AUTHOR_EMAIL" value = coalesce(try(data.coder_parameter.user_email[0].value, ""), data.coder_workspace.me.owner_email) + count = data.coder_workspace.me.owner_email != "" ? 1 : 0 } resource "coder_env" "git_commmiter_email" { agent_id = var.agent_id name = "GIT_COMMITTER_EMAIL" value = coalesce(try(data.coder_parameter.user_email[0].value, ""), data.coder_workspace.me.owner_email) + count = data.coder_workspace.me.owner_email != "" ? 1 : 0 } diff --git a/test.ts b/test.ts index 37e0805..97416cf 100644 --- a/test.ts +++ b/test.ts @@ -171,9 +171,9 @@ export const testRequiredVariables = ( export const runTerraformApply = async ( dir: string, vars: Record, + env: Record = {}, ): Promise => { const stateFile = `${dir}/${crypto.randomUUID()}.tfstate`; - const env = {}; Object.keys(vars).forEach((key) => (env[`TF_VAR_${key}`] = vars[key])); const proc = spawn( [