diff --git a/registry/coder/modules/dotfiles/README.md b/registry/coder/modules/dotfiles/README.md index c78b80c3..aae52284 100644 --- a/registry/coder/modules/dotfiles/README.md +++ b/registry/coder/modules/dotfiles/README.md @@ -18,7 +18,7 @@ Under the hood, this module uses the [coder dotfiles](https://coder.com/docs/v2/ module "dotfiles" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/dotfiles/coder" - version = "1.3.2" + version = "1.4.0" agent_id = coder_agent.example.id } ``` @@ -31,7 +31,7 @@ module "dotfiles" { module "dotfiles" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/dotfiles/coder" - version = "1.3.2" + version = "1.4.0" agent_id = coder_agent.example.id } ``` @@ -42,7 +42,7 @@ module "dotfiles" { module "dotfiles" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/dotfiles/coder" - version = "1.3.2" + version = "1.4.0" agent_id = coder_agent.example.id user = "root" } @@ -54,14 +54,14 @@ module "dotfiles" { module "dotfiles" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/dotfiles/coder" - version = "1.3.2" + version = "1.4.0" agent_id = coder_agent.example.id } module "dotfiles-root" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/dotfiles/coder" - version = "1.3.2" + version = "1.4.0" agent_id = coder_agent.example.id user = "root" dotfiles_uri = module.dotfiles.dotfiles_uri @@ -90,7 +90,7 @@ You can set a default dotfiles repository for all users by setting the `default_ module "dotfiles" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/dotfiles/coder" - version = "1.3.2" + version = "1.4.0" agent_id = coder_agent.example.id default_dotfiles_uri = "https://github.com/coder/dotfiles" } diff --git a/registry/coder/modules/dotfiles/main.test.ts b/registry/coder/modules/dotfiles/main.test.ts index 8cde2510..a9a8bf93 100644 --- a/registry/coder/modules/dotfiles/main.test.ts +++ b/registry/coder/modules/dotfiles/main.test.ts @@ -62,7 +62,41 @@ describe("dotfiles", async () => { agent_id: "foo", coder_parameter_order: order.toString(), }); + expect(state.resources).toHaveLength(3); + const parameters = state.resources.filter( + (r) => r.type === "coder_parameter", + ); + for (const param of parameters) { + expect(param.instances[0].attributes.order).toBe(order); + } + }); + + it("set custom dotfiles_branch", async () => { + const branch = "develop"; + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + dotfiles_branch: branch, + }); expect(state.resources).toHaveLength(2); - expect(state.resources[0].instances[0].attributes.order).toBe(order); + const scriptResource = state.resources.find( + (r) => r.type === "coder_script", + ); + expect(scriptResource?.instances[0].attributes.script).toContain( + `DOTFILES_BRANCH="${branch}"`, + ); + }); + + it("default dotfiles_branch creates parameter", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + }); + expect(state.resources).toHaveLength(3); + const branchParameter = state.resources.find( + (r) => + r.type === "coder_parameter" && + r.instances[0].attributes.name === "dotfiles_branch", + ); + expect(branchParameter).toBeDefined(); + expect(branchParameter?.instances[0].attributes.default).toBeNull(); }); }); diff --git a/registry/coder/modules/dotfiles/main.tf b/registry/coder/modules/dotfiles/main.tf index 7b15a391..ca1709d0 100644 --- a/registry/coder/modules/dotfiles/main.tf +++ b/registry/coder/modules/dotfiles/main.tf @@ -46,6 +46,12 @@ variable "default_dotfiles_uri" { } } +variable "default_dotfiles_branch" { + type = string + description = "The default dotfiles branch if the workspace user does not provide one" + default = "" +} + variable "dotfiles_uri" { type = string description = "The URL to a dotfiles repository. (optional, when set, the user isn't prompted for their dotfiles)" @@ -61,6 +67,17 @@ variable "dotfiles_uri" { } } +variable "dotfiles_branch" { + type = string + description = "The branch to use for the dotfiles repository (optional, when set, the user isn't prompted for the branch)" + default = null + + validation { + condition = var.dotfiles_branch == null || var.dotfiles_branch != "" + error_message = "dotfiles_branch cannot be an empty string. Use null to prompt the user or provide a valid branch name." + } +} + variable "user" { type = string description = "The name of the user to apply the dotfiles to. (optional, applies to the current user by default)" @@ -107,8 +124,21 @@ data "coder_parameter" "dotfiles_uri" { } } +data "coder_parameter" "dotfiles_branch" { + count = var.dotfiles_branch == null ? 1 : 0 + type = "string" + name = "dotfiles_branch" + display_name = "Dotfiles Branch" + order = var.coder_parameter_order + default = var.default_dotfiles_branch + description = "The branch to use for the dotfiles repository" + mutable = true + icon = "/icon/dotfiles.svg" +} + locals { dotfiles_uri = var.dotfiles_uri != null ? var.dotfiles_uri : data.coder_parameter.dotfiles_uri[0].value + dotfiles_branch = var.dotfiles_branch != null ? var.dotfiles_branch : data.coder_parameter.dotfiles_branch[0].value user = var.user != null ? var.user : "" encoded_post_clone_script = var.post_clone_script != null ? base64encode(var.post_clone_script) : "" } @@ -118,6 +148,7 @@ resource "coder_script" "dotfiles" { script = templatefile("${path.module}/run.sh", { DOTFILES_URI : local.dotfiles_uri, DOTFILES_USER : local.user, + DOTFILES_BRANCH : local.dotfiles_branch, POST_CLONE_SCRIPT : local.encoded_post_clone_script }) display_name = "Dotfiles" @@ -136,6 +167,7 @@ resource "coder_app" "dotfiles" { command = templatefile("${path.module}/run.sh", { DOTFILES_URI : local.dotfiles_uri, DOTFILES_USER : local.user, + DOTFILES_BRANCH : local.dotfiles_branch, POST_CLONE_SCRIPT : local.encoded_post_clone_script }) } diff --git a/registry/coder/modules/dotfiles/run.sh b/registry/coder/modules/dotfiles/run.sh index 49ab3ec5..f7f275f8 100644 --- a/registry/coder/modules/dotfiles/run.sh +++ b/registry/coder/modules/dotfiles/run.sh @@ -4,6 +4,7 @@ set -euo pipefail DOTFILES_URI="${DOTFILES_URI}" DOTFILES_USER="${DOTFILES_USER}" +DOTFILES_BRANCH="${DOTFILES_BRANCH}" # Validate DOTFILES_URI to prevent command injection (defense in depth) if [ -n "$DOTFILES_URI" ]; then @@ -24,10 +25,18 @@ if [ -n "$${DOTFILES_URI// }" ]; then DOTFILES_USER="$USER" fi - echo "✨ Applying dotfiles for user $DOTFILES_USER" + if [ -n "$DOTFILES_BRANCH" ]; then + echo "✨ Applying dotfiles for user $DOTFILES_USER from branch $DOTFILES_BRANCH" + else + echo "✨ Applying dotfiles for user $DOTFILES_USER" + fi if [ "$DOTFILES_USER" = "$USER" ]; then - coder dotfiles "$DOTFILES_URI" -y 2>&1 | tee ~/.dotfiles.log + if [ -n "$DOTFILES_BRANCH" ]; then + coder dotfiles "$DOTFILES_URI" --branch "$DOTFILES_BRANCH" -y 2>&1 | tee ~/.dotfiles.log + else + coder dotfiles "$DOTFILES_URI" -y 2>&1 | tee ~/.dotfiles.log + fi else if command -v getent > /dev/null 2>&1; then DOTFILES_USER_HOME=$(getent passwd "$DOTFILES_USER" | cut -d: -f6) @@ -40,7 +49,11 @@ if [ -n "$${DOTFILES_URI// }" ]; then fi CODER_BIN=$(command -v coder) - sudo -u "$DOTFILES_USER" "$CODER_BIN" dotfiles "$DOTFILES_URI" -y 2>&1 | tee "$DOTFILES_USER_HOME/.dotfiles.log" + if [ -n "$DOTFILES_BRANCH" ]; then + sudo -u "$DOTFILES_USER" "$CODER_BIN" dotfiles "$DOTFILES_URI" --branch "$DOTFILES_BRANCH" -y 2>&1 | tee "$DOTFILES_USER_HOME/.dotfiles.log" + else + sudo -u "$DOTFILES_USER" "$CODER_BIN" dotfiles "$DOTFILES_URI" -y 2>&1 | tee "$DOTFILES_USER_HOME/.dotfiles.log" + fi fi fi