Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 36 additions & 7 deletions registry/thezoker/modules/nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
```
Expand All @@ -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_install_prefix>/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
}
```

Expand All @@ -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"
Comment thread
35C4n0r marked this conversation as resolved.
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
}
```
45 changes: 35 additions & 10 deletions registry/thezoker/modules/nodejs/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
115 changes: 115 additions & 0 deletions registry/thezoker/modules/nodejs/nodejs.tftest.hcl
Original file line number Diff line number Diff line change
@@ -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"
}
}
Loading