diff --git a/registry/thezoker/modules/nodejs/README.md b/registry/thezoker/modules/nodejs/README.md index d7293aacd..6730df98f 100644 --- a/registry/thezoker/modules/nodejs/README.md +++ b/registry/thezoker/modules/nodejs/README.md @@ -15,7 +15,7 @@ Automatically installs [Node.js](https://github.com/nodejs/node) via [`nvm`](htt module "nodejs" { count = data.coder_workspace.me.start_count source = "registry.coder.com/thezoker/nodejs/coder" - version = "1.0.13" + version = "1.1.0" agent_id = coder_agent.example.id } ``` @@ -28,14 +28,37 @@ This installs multiple versions of Node.js: module "nodejs" { count = data.coder_workspace.me.start_count source = "registry.coder.com/thezoker/nodejs/coder" - version = "1.0.13" + version = "1.1.0" agent_id = coder_agent.example.id node_versions = [ "18", "20", "node" ] - default_node_version = "1.0.13" + default_node_version = "20" +} +``` + +## Pre and Post Install Scripts + +Use `pre_install_script` and `post_install_script` to run custom scripts before and after Node.js is installed. They are orchestrated with the [`coder-utils`](https://registry.coder.com/modules/coder/coder-utils) module, which runs them in order via `coder exp sync`. + +> [!NOTE] +> Node.js is installed via nvm, which only loads automatically in interactive login shells. `post_install_script` runs in a fresh non-interactive shell, so source nvm first to put `node` and `npm` on `PATH`. nvm is installed at `$HOME//nvm` (default `$HOME/.nvm/nvm`). + +```tf +module "nodejs" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/thezoker/nodejs/coder" + version = "1.1.0" + agent_id = coder_agent.example.id + + pre_install_script = "echo 'Setting up prerequisites...'" + post_install_script = <<-EOT + export NVM_DIR="$HOME/.nvm/nvm" + [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" + npm install -g yarn pnpm + EOT } ``` @@ -47,15 +70,21 @@ A example with all available options: module "nodejs" { count = data.coder_workspace.me.start_count source = "registry.coder.com/thezoker/nodejs/coder" - version = "1.0.13" + version = "1.1.0" agent_id = coder_agent.example.id - nvm_version = "1.0.13" - nvm_install_prefix = "/opt/nvm" + nvm_version = "v0.40.1" + nvm_install_prefix = ".nvm" node_versions = [ "16", "18", "node" ] - default_node_version = "1.0.13" + default_node_version = "18" + pre_install_script = "echo 'Pre-install setup'" + post_install_script = <<-EOT + export NVM_DIR="$HOME/.nvm/nvm" + [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" + npm install -g typescript + EOT } ``` diff --git a/registry/thezoker/modules/nodejs/main.tf b/registry/thezoker/modules/nodejs/main.tf index 9c9c5c760..34b2d05ac 100644 --- a/registry/thezoker/modules/nodejs/main.tf +++ b/registry/thezoker/modules/nodejs/main.tf @@ -38,15 +38,40 @@ variable "default_node_version" { default = "node" } -resource "coder_script" "nodejs" { - agent_id = var.agent_id - display_name = "Node.js:" - script = templatefile("${path.module}/run.sh", { - NVM_VERSION : var.nvm_version, - INSTALL_PREFIX : var.nvm_install_prefix, - NODE_VERSIONS : join(",", var.node_versions), - DEFAULT : var.default_node_version, +variable "pre_install_script" { + type = string + description = "Custom script to run before installing Node.js." + default = null +} + +variable "post_install_script" { + type = string + description = "Custom script to run after installing Node.js." + default = null +} + +locals { + install_script = templatefile("${path.module}/run.sh", { + NVM_VERSION = var.nvm_version, + INSTALL_PREFIX = var.nvm_install_prefix, + NODE_VERSIONS = join(",", var.node_versions), + DEFAULT = var.default_node_version, }) - run_on_start = true - start_blocks_login = true +} + +module "coder_utils" { + source = "registry.coder.com/coder/coder-utils/coder" + version = "0.0.1" + + agent_id = var.agent_id + module_directory = "$HOME/.coder-modules/thezoker/nodejs" + display_name_prefix = "Node.js" + pre_install_script = var.pre_install_script + post_install_script = var.post_install_script + install_script = local.install_script +} + +output "scripts" { + description = "Ordered list of coder exp sync names for the coder_script resources this module creates, in run order (pre_install, install, post_install). Scripts that were not configured are absent from the list." + value = module.coder_utils.scripts } diff --git a/registry/thezoker/modules/nodejs/nodejs.tftest.hcl b/registry/thezoker/modules/nodejs/nodejs.tftest.hcl new file mode 100644 index 000000000..a228c28d0 --- /dev/null +++ b/registry/thezoker/modules/nodejs/nodejs.tftest.hcl @@ -0,0 +1,115 @@ +run "test_nodejs_basic" { + command = plan + + variables { + agent_id = "test-agent-123" + } + + assert { + condition = var.agent_id == "test-agent-123" + error_message = "Agent ID variable should be set correctly" + } + + assert { + condition = var.nvm_version == "master" + error_message = "nvm_version should default to master" + } + + assert { + condition = var.default_node_version == "node" + error_message = "default_node_version should default to node" + } + + assert { + condition = var.pre_install_script == null + error_message = "pre_install_script should default to null" + } + + assert { + condition = var.post_install_script == null + error_message = "post_install_script should default to null" + } +} + +run "test_custom_options" { + command = plan + + variables { + agent_id = "test-agent-custom" + nvm_version = "v0.39.7" + nvm_install_prefix = ".custom-nvm" + node_versions = ["18", "20", "node"] + default_node_version = "20" + } + + assert { + condition = var.nvm_version == "v0.39.7" + error_message = "nvm_version should be set to v0.39.7" + } + + assert { + condition = length(var.node_versions) == 3 + error_message = "node_versions should have 3 entries" + } + + assert { + condition = strcontains(local.install_script, "v0.39.7") + error_message = "install script should embed the configured nvm_version" + } +} + +run "test_script_outputs_install_only" { + command = plan + + variables { + agent_id = "test-agent-outputs" + } + + assert { + condition = length(output.scripts) == 1 && output.scripts[0] == "thezoker-nodejs-install_script" + error_message = "scripts output should list only the install script when pre/post are not configured" + } +} + +run "test_script_outputs_with_pre_and_post" { + command = plan + + variables { + agent_id = "test-agent-outputs-all" + pre_install_script = "echo 'Pre-install script'" + post_install_script = "echo 'Post-install script'" + } + + assert { + condition = output.scripts == ["thezoker-nodejs-pre_install_script", "thezoker-nodejs-install_script", "thezoker-nodejs-post_install_script"] + error_message = "scripts output should list pre_install, install, post_install in run order" + } +} + +run "test_script_outputs_with_pre_install_only" { + command = plan + + variables { + agent_id = "test-agent-pre" + pre_install_script = "echo 'pre-install'" + } + + assert { + condition = output.scripts == ["thezoker-nodejs-pre_install_script", "thezoker-nodejs-install_script"] + error_message = "scripts output should list pre_install then install when only pre is configured" + } +} + +run "test_script_outputs_with_post_install_only" { + command = plan + + variables { + agent_id = "test-agent-post" + post_install_script = "echo 'post-install'" + } + + assert { + condition = output.scripts == ["thezoker-nodejs-install_script", "thezoker-nodejs-post_install_script"] + error_message = "scripts output should list install then post_install when only post is configured" + } +}