From 79cef2ecfce6c53c7c71ca21616c406847a11d46 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 14:07:52 +0000 Subject: [PATCH 1/7] feat(nodejs): add pre and post install scripts with coder exp sync support Add pre_install_script and post_install_script variables to the nodejs module following the pattern used by other registry modules (agent-helper, claude-code, aider, etc.). Scripts use coder exp sync for reliable execution ordering, enabling dependency coordination between modules. Changes: - Add pre_install_script and post_install_script optional variables - Wrap install script with coder exp sync want/start/complete - Add conditional pre/post install coder_script resources - Export sync script names as outputs for cross-module coordination - Add nodejs.tftest.hcl with 5 test cases - Update README with pre/post install documentation and examples - Bump version references to 1.0.14 --- registry/thezoker/modules/nodejs/README.md | 34 ++++- registry/thezoker/modules/nodejs/main.test.ts | 20 ++- registry/thezoker/modules/nodejs/main.tf | 115 +++++++++++++++-- .../thezoker/modules/nodejs/nodejs.tftest.hcl | 122 ++++++++++++++++++ 4 files changed, 273 insertions(+), 18 deletions(-) create mode 100644 registry/thezoker/modules/nodejs/nodejs.tftest.hcl diff --git a/registry/thezoker/modules/nodejs/README.md b/registry/thezoker/modules/nodejs/README.md index d7293aacd..477b13347 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.0.14" agent_id = coder_agent.example.id } ``` @@ -28,17 +28,35 @@ 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.0.14" 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 installation. These use `coder exp sync` for reliable script ordering, making them useful for dependency coordination between modules. + +```tf +module "nodejs" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/thezoker/nodejs/coder" + version = "1.0.14" + agent_id = coder_agent.example.id + + pre_install_script = "echo 'Setting up prerequisites...'" + post_install_script = "npm install -g yarn pnpm" +} +``` + +The module exports sync script names (`pre_install_script_name`, `install_script_name`, `post_install_script_name`) that other modules can use with `coder exp sync want` to coordinate execution order. + ## Full example A example with all available options: @@ -47,15 +65,17 @@ 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.0.14" agent_id = coder_agent.example.id - nvm_version = "1.0.13" + nvm_version = "v0.39.7" nvm_install_prefix = "/opt/nvm" node_versions = [ - "16", "18", + "20", "node" ] - default_node_version = "1.0.13" + default_node_version = "20" + pre_install_script = "echo 'Pre-install setup'" + post_install_script = "npm install -g typescript" } ``` diff --git a/registry/thezoker/modules/nodejs/main.test.ts b/registry/thezoker/modules/nodejs/main.test.ts index 14f8a066f..c0cb4032e 100644 --- a/registry/thezoker/modules/nodejs/main.test.ts +++ b/registry/thezoker/modules/nodejs/main.test.ts @@ -1,5 +1,5 @@ -import { describe } from "bun:test"; -import { runTerraformInit, testRequiredVariables } from "~test"; +import { describe, expect, it } from "bun:test"; +import { runTerraformInit, testRequiredVariables, runTerraformApply } from "~test"; describe("nodejs", async () => { await runTerraformInit(import.meta.dir); @@ -8,5 +8,19 @@ describe("nodejs", async () => { agent_id: "foo", }); - // More tests depend on shebang refactors + it("accepts pre_install_script and post_install_script", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + pre_install_script: "echo pre", + post_install_script: "echo post", + }); + expect(state).toBeDefined(); + }); + + it("works without pre/post install scripts", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + }); + expect(state).toBeDefined(); + }); }); diff --git a/registry/thezoker/modules/nodejs/main.tf b/registry/thezoker/modules/nodejs/main.tf index 9c9c5c760..b4b00ab58 100644 --- a/registry/thezoker/modules/nodejs/main.tf +++ b/registry/thezoker/modules/nodejs/main.tf @@ -38,15 +38,114 @@ variable "default_node_version" { default = "node" } +variable "pre_install_script" { + type = string + description = "Custom script to run before installing Node.js. Can be used for dependency ordering between modules." + default = null +} + +variable "post_install_script" { + type = string + description = "Custom script to run after installing Node.js." + default = null +} + +locals { + encoded_pre_install_script = var.pre_install_script != null ? base64encode(var.pre_install_script) : "" + encoded_post_install_script = var.post_install_script != null ? base64encode(var.post_install_script) : "" + + module_dir_path = "$HOME/.nodejs-module" + + pre_install_script_name = "nodejs-pre_install_script" + install_script_name = "nodejs-install_script" + post_install_script_name = "nodejs-post_install_script" +} + +resource "coder_script" "nodejs_pre_install" { + count = var.pre_install_script != null ? 1 : 0 + agent_id = var.agent_id + display_name = "Node.js: Pre-Install" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -o errexit + set -o pipefail + + mkdir -p ${local.module_dir_path} + + trap 'coder exp sync complete ${local.pre_install_script_name}' EXIT + coder exp sync start ${local.pre_install_script_name} + + echo -n '${local.encoded_pre_install_script}' | base64 -d > ${local.module_dir_path}/pre_install.sh + chmod +x ${local.module_dir_path}/pre_install.sh + + ${local.module_dir_path}/pre_install.sh 2>&1 + EOT +} + 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, - }) - run_on_start = true + display_name = "Node.js: Install" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -o errexit + set -o pipefail + + mkdir -p ${local.module_dir_path} + + trap 'coder exp sync complete ${local.install_script_name}' EXIT + %{if var.pre_install_script != null~} + coder exp sync want ${local.install_script_name} ${local.pre_install_script_name} + %{endif~} + coder exp sync start ${local.install_script_name} + + echo -n '${base64encode(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, +}))}' | base64 -d > ${local.module_dir_path}/install.sh + chmod +x ${local.module_dir_path}/install.sh + + ${local.module_dir_path}/install.sh 2>&1 + EOT + start_blocks_login = true } + +resource "coder_script" "nodejs_post_install" { + count = var.post_install_script != null ? 1 : 0 + agent_id = var.agent_id + display_name = "Node.js: Post-Install" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -o errexit + set -o pipefail + + trap 'coder exp sync complete ${local.post_install_script_name}' EXIT + coder exp sync want ${local.post_install_script_name} ${local.install_script_name} + coder exp sync start ${local.post_install_script_name} + + echo -n '${local.encoded_post_install_script}' | base64 -d > ${local.module_dir_path}/post_install.sh + chmod +x ${local.module_dir_path}/post_install.sh + + ${local.module_dir_path}/post_install.sh 2>&1 + EOT +} + +output "pre_install_script_name" { + description = "The name of the pre-install script for sync." + value = local.pre_install_script_name +} + +output "install_script_name" { + description = "The name of the install script for sync." + value = local.install_script_name +} + +output "post_install_script_name" { + description = "The name of the post-install script for sync." + value = local.post_install_script_name +} diff --git a/registry/thezoker/modules/nodejs/nodejs.tftest.hcl b/registry/thezoker/modules/nodejs/nodejs.tftest.hcl new file mode 100644 index 000000000..e8024e567 --- /dev/null +++ b/registry/thezoker/modules/nodejs/nodejs.tftest.hcl @@ -0,0 +1,122 @@ +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_with_scripts" { + command = plan + + variables { + agent_id = "test-agent-scripts" + pre_install_script = "echo 'Pre-install script'" + post_install_script = "echo 'Post-install script'" + } + + assert { + condition = var.pre_install_script == "echo 'Pre-install script'" + error_message = "Pre-install script should be set correctly" + } + + assert { + condition = var.post_install_script == "echo 'Post-install script'" + error_message = "Post-install script should be set correctly" + } +} + +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 = var.nvm_install_prefix == ".custom-nvm" + error_message = "nvm_install_prefix should be set correctly" + } + + assert { + condition = length(var.node_versions) == 3 + error_message = "node_versions should have 3 entries" + } + + assert { + condition = var.default_node_version == "20" + error_message = "default_node_version should be set to 20" + } +} + +run "test_with_pre_install_only" { + command = plan + + variables { + agent_id = "test-agent-pre" + pre_install_script = "echo 'pre-install'" + } + + assert { + condition = var.pre_install_script != null + error_message = "Pre-install script should be set" + } + + assert { + condition = var.post_install_script == null + error_message = "Post-install script should default to null" + } +} + +run "test_with_post_install_only" { + command = plan + + variables { + agent_id = "test-agent-post" + post_install_script = "echo 'post-install'" + } + + 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 be set" + } +} From 8bd24f8b44bc982b9363d93ad4bb625eb95fa106 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 14:23:14 +0000 Subject: [PATCH 2/7] simplify: just add pre/post install script variables inline in run.sh Remove all the coder exp sync machinery, separate script resources, locals, and outputs. Simply pass the scripts as template variables into run.sh and eval them before/after the nvm install. --- registry/thezoker/modules/nodejs/README.md | 4 +- registry/thezoker/modules/nodejs/main.test.ts | 20 +--- registry/thezoker/modules/nodejs/main.tf | 107 ++---------------- registry/thezoker/modules/nodejs/run.sh | 12 ++ 4 files changed, 26 insertions(+), 117 deletions(-) diff --git a/registry/thezoker/modules/nodejs/README.md b/registry/thezoker/modules/nodejs/README.md index 477b13347..2874af681 100644 --- a/registry/thezoker/modules/nodejs/README.md +++ b/registry/thezoker/modules/nodejs/README.md @@ -41,7 +41,7 @@ module "nodejs" { ## Pre and Post Install Scripts -Use `pre_install_script` and `post_install_script` to run custom scripts before and after Node.js installation. These use `coder exp sync` for reliable script ordering, making them useful for dependency coordination between modules. +Use `pre_install_script` and `post_install_script` to run custom scripts before and after Node.js installation. ```tf module "nodejs" { @@ -55,8 +55,6 @@ module "nodejs" { } ``` -The module exports sync script names (`pre_install_script_name`, `install_script_name`, `post_install_script_name`) that other modules can use with `coder exp sync want` to coordinate execution order. - ## Full example A example with all available options: diff --git a/registry/thezoker/modules/nodejs/main.test.ts b/registry/thezoker/modules/nodejs/main.test.ts index c0cb4032e..876f71225 100644 --- a/registry/thezoker/modules/nodejs/main.test.ts +++ b/registry/thezoker/modules/nodejs/main.test.ts @@ -1,5 +1,5 @@ -import { describe, expect, it } from "bun:test"; -import { runTerraformInit, testRequiredVariables, runTerraformApply } from "~test"; +import { describe } from "bun:test"; +import { runTerraformInit, testRequiredVariables } from "~test"; describe("nodejs", async () => { await runTerraformInit(import.meta.dir); @@ -7,20 +7,4 @@ describe("nodejs", async () => { testRequiredVariables(import.meta.dir, { agent_id: "foo", }); - - it("accepts pre_install_script and post_install_script", async () => { - const state = await runTerraformApply(import.meta.dir, { - agent_id: "foo", - pre_install_script: "echo pre", - post_install_script: "echo post", - }); - expect(state).toBeDefined(); - }); - - it("works without pre/post install scripts", async () => { - const state = await runTerraformApply(import.meta.dir, { - agent_id: "foo", - }); - expect(state).toBeDefined(); - }); }); diff --git a/registry/thezoker/modules/nodejs/main.tf b/registry/thezoker/modules/nodejs/main.tf index b4b00ab58..0f420b38e 100644 --- a/registry/thezoker/modules/nodejs/main.tf +++ b/registry/thezoker/modules/nodejs/main.tf @@ -40,7 +40,7 @@ variable "default_node_version" { variable "pre_install_script" { type = string - description = "Custom script to run before installing Node.js. Can be used for dependency ordering between modules." + description = "Custom script to run before installing Node.js." default = null } @@ -50,102 +50,17 @@ variable "post_install_script" { default = null } -locals { - encoded_pre_install_script = var.pre_install_script != null ? base64encode(var.pre_install_script) : "" - encoded_post_install_script = var.post_install_script != null ? base64encode(var.post_install_script) : "" - - module_dir_path = "$HOME/.nodejs-module" - - pre_install_script_name = "nodejs-pre_install_script" - install_script_name = "nodejs-install_script" - post_install_script_name = "nodejs-post_install_script" -} - -resource "coder_script" "nodejs_pre_install" { - count = var.pre_install_script != null ? 1 : 0 - agent_id = var.agent_id - display_name = "Node.js: Pre-Install" - run_on_start = true - script = <<-EOT - #!/bin/bash - set -o errexit - set -o pipefail - - mkdir -p ${local.module_dir_path} - - trap 'coder exp sync complete ${local.pre_install_script_name}' EXIT - coder exp sync start ${local.pre_install_script_name} - - echo -n '${local.encoded_pre_install_script}' | base64 -d > ${local.module_dir_path}/pre_install.sh - chmod +x ${local.module_dir_path}/pre_install.sh - - ${local.module_dir_path}/pre_install.sh 2>&1 - EOT -} - resource "coder_script" "nodejs" { agent_id = var.agent_id - display_name = "Node.js: Install" - run_on_start = true - script = <<-EOT - #!/bin/bash - set -o errexit - set -o pipefail - - mkdir -p ${local.module_dir_path} - - trap 'coder exp sync complete ${local.install_script_name}' EXIT - %{if var.pre_install_script != null~} - coder exp sync want ${local.install_script_name} ${local.pre_install_script_name} - %{endif~} - coder exp sync start ${local.install_script_name} - - echo -n '${base64encode(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, -}))}' | base64 -d > ${local.module_dir_path}/install.sh - chmod +x ${local.module_dir_path}/install.sh - - ${local.module_dir_path}/install.sh 2>&1 - EOT - + 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, + PRE_INSTALL_SCRIPT : var.pre_install_script != null ? var.pre_install_script : "", + POST_INSTALL_SCRIPT : var.post_install_script != null ? var.post_install_script : "", + }) + run_on_start = true start_blocks_login = true } - -resource "coder_script" "nodejs_post_install" { - count = var.post_install_script != null ? 1 : 0 - agent_id = var.agent_id - display_name = "Node.js: Post-Install" - run_on_start = true - script = <<-EOT - #!/bin/bash - set -o errexit - set -o pipefail - - trap 'coder exp sync complete ${local.post_install_script_name}' EXIT - coder exp sync want ${local.post_install_script_name} ${local.install_script_name} - coder exp sync start ${local.post_install_script_name} - - echo -n '${local.encoded_post_install_script}' | base64 -d > ${local.module_dir_path}/post_install.sh - chmod +x ${local.module_dir_path}/post_install.sh - - ${local.module_dir_path}/post_install.sh 2>&1 - EOT -} - -output "pre_install_script_name" { - description = "The name of the pre-install script for sync." - value = local.pre_install_script_name -} - -output "install_script_name" { - description = "The name of the install script for sync." - value = local.install_script_name -} - -output "post_install_script_name" { - description = "The name of the post-install script for sync." - value = local.post_install_script_name -} diff --git a/registry/thezoker/modules/nodejs/run.sh b/registry/thezoker/modules/nodejs/run.sh index 31044eb5b..53e4b6caf 100644 --- a/registry/thezoker/modules/nodejs/run.sh +++ b/registry/thezoker/modules/nodejs/run.sh @@ -4,10 +4,17 @@ NVM_VERSION='${NVM_VERSION}' NODE_VERSIONS='${NODE_VERSIONS}' INSTALL_PREFIX='${INSTALL_PREFIX}' DEFAULT='${DEFAULT}' +PRE_INSTALL_SCRIPT='${PRE_INSTALL_SCRIPT}' +POST_INSTALL_SCRIPT='${POST_INSTALL_SCRIPT}' BOLD='\033[0;1m' CODE='\033[36;40;1m' RESET='\033[0m' +if [ -n "$${PRE_INSTALL_SCRIPT}" ]; then + printf "$${BOLD}Running pre-install script...$${RESET}\n" + eval "$${PRE_INSTALL_SCRIPT}" +fi + printf "$${BOLD}Installing nvm!$${RESET}\n" export NVM_DIR="$HOME/$${INSTALL_PREFIX}/nvm" @@ -51,3 +58,8 @@ if [ -n "$${DEFAULT}" ]; then printf "🛠️ Setting default node version $${CODE}$DEFAULT$${RESET}...\n" output=$(nvm alias default $DEFAULT 2>&1) fi + +if [ -n "$${POST_INSTALL_SCRIPT}" ]; then + printf "$${BOLD}Running post-install script...$${RESET}\n" + eval "$${POST_INSTALL_SCRIPT}" +fi From acdbd5777f745cdcc1a4791a9c65c8f3902c04ed Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 14:24:57 +0000 Subject: [PATCH 3/7] fix: align terraform blocks to pass prettier formatting --- registry/thezoker/modules/nodejs/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/registry/thezoker/modules/nodejs/README.md b/registry/thezoker/modules/nodejs/README.md index 2874af681..9d494dcb7 100644 --- a/registry/thezoker/modules/nodejs/README.md +++ b/registry/thezoker/modules/nodejs/README.md @@ -72,8 +72,8 @@ module "nodejs" { "20", "node" ] - default_node_version = "20" - pre_install_script = "echo 'Pre-install setup'" - post_install_script = "npm install -g typescript" + default_node_version = "20" + pre_install_script = "echo 'Pre-install setup'" + post_install_script = "npm install -g typescript" } ``` From 872fdbcf35448cfb9350c6abe908605dcd5bc80e Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 13:23:26 +0000 Subject: [PATCH 4/7] fix: bump version to 1.1.0 (minor) for new input variables --- registry/thezoker/modules/nodejs/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/registry/thezoker/modules/nodejs/README.md b/registry/thezoker/modules/nodejs/README.md index 9d494dcb7..873aac06e 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.14" + version = "1.1.0" agent_id = coder_agent.example.id } ``` @@ -28,7 +28,7 @@ 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.14" + version = "1.1.0" agent_id = coder_agent.example.id node_versions = [ "18", @@ -47,7 +47,7 @@ Use `pre_install_script` and `post_install_script` to run custom scripts before module "nodejs" { count = data.coder_workspace.me.start_count source = "registry.coder.com/thezoker/nodejs/coder" - version = "1.0.14" + version = "1.1.0" agent_id = coder_agent.example.id pre_install_script = "echo 'Setting up prerequisites...'" @@ -63,7 +63,7 @@ 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.14" + version = "1.1.0" agent_id = coder_agent.example.id nvm_version = "v0.39.7" nvm_install_prefix = "/opt/nvm" From d0ef879e5f76ce73f28895d4e53a1d45e44ccfd7 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 13:24:41 +0000 Subject: [PATCH 5/7] revert: restore unrelated comment in main.test.ts --- registry/thezoker/modules/nodejs/main.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/registry/thezoker/modules/nodejs/main.test.ts b/registry/thezoker/modules/nodejs/main.test.ts index 876f71225..14f8a066f 100644 --- a/registry/thezoker/modules/nodejs/main.test.ts +++ b/registry/thezoker/modules/nodejs/main.test.ts @@ -7,4 +7,6 @@ describe("nodejs", async () => { testRequiredVariables(import.meta.dir, { agent_id: "foo", }); + + // More tests depend on shebang refactors }); From 817238ea64a28748cd1681e2e767b552bd067407 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:59:22 +0000 Subject: [PATCH 6/7] refactor(nodejs): use base64 encoding and coder exp sync for pre/post install scripts - Base64 encode pre/post install scripts to safely handle special characters - Use separate coder_script resources for pre_install, install, and post_install - Add coder exp sync want/start/complete for execution ordering - Base64 encode the main install script (run.sh) via templatefile + base64encode - Revert run.sh to original (no pre/post install handling) - Add sync name outputs for cross-module dependency coordination - Update README with cross-module coordination documentation - Add output assertions to tests --- registry/thezoker/modules/nodejs/README.md | 22 ++++ registry/thezoker/modules/nodejs/main.tf | 114 ++++++++++++++++-- .../thezoker/modules/nodejs/nodejs.tftest.hcl | 15 +++ registry/thezoker/modules/nodejs/run.sh | 12 -- 4 files changed, 142 insertions(+), 21 deletions(-) diff --git a/registry/thezoker/modules/nodejs/README.md b/registry/thezoker/modules/nodejs/README.md index 873aac06e..1c3e4eac6 100644 --- a/registry/thezoker/modules/nodejs/README.md +++ b/registry/thezoker/modules/nodejs/README.md @@ -55,6 +55,28 @@ module "nodejs" { } ``` +## Cross-Module Dependency Ordering + +This module uses `coder exp sync` to coordinate execution ordering with other modules. It exposes the following outputs for use with `coder exp sync want`: + +- `install_script_name` — the sync name for the main Node.js installation script +- `pre_install_script_name` — the sync name for the pre-install script +- `post_install_script_name` — the sync name for the post-install script + +For example, to ensure another module waits for Node.js to be fully installed: + +```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 +} + +# In another module's coder_script, wait for Node.js installation: +# coder exp sync want my-script ${module.nodejs[0].install_script_name} +``` + ## Full example A example with all available options: diff --git a/registry/thezoker/modules/nodejs/main.tf b/registry/thezoker/modules/nodejs/main.tf index 0f420b38e..c11b89960 100644 --- a/registry/thezoker/modules/nodejs/main.tf +++ b/registry/thezoker/modules/nodejs/main.tf @@ -50,17 +50,113 @@ variable "post_install_script" { default = null } +locals { + encoded_pre_install_script = var.pre_install_script != null ? base64encode(var.pre_install_script) : "" + encoded_post_install_script = var.post_install_script != null ? base64encode(var.post_install_script) : "" + + 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, + }) + encoded_install_script = base64encode(local.install_script) + + pre_install_script_name = "nodejs-pre_install_script" + install_script_name = "nodejs-install_script" + post_install_script_name = "nodejs-post_install_script" + + module_dir_path = "$HOME/.nodejs-module" + + pre_install_path = "${local.module_dir_path}/pre_install.sh" + pre_install_log_path = "${local.module_dir_path}/pre_install.log" + install_path = "${local.module_dir_path}/install.sh" + install_log_path = "${local.module_dir_path}/install.log" + post_install_path = "${local.module_dir_path}/post_install.sh" + post_install_log_path = "${local.module_dir_path}/post_install.log" +} + +resource "coder_script" "pre_install_script" { + count = var.pre_install_script == null ? 0 : 1 + agent_id = var.agent_id + display_name = "Node.js: Pre-Install" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -o errexit + set -o pipefail + + mkdir -p ${local.module_dir_path} + + trap 'coder exp sync complete ${local.pre_install_script_name}' EXIT + coder exp sync start ${local.pre_install_script_name} + + echo -n '${local.encoded_pre_install_script}' | base64 -d > ${local.pre_install_path} + chmod +x ${local.pre_install_path} + + ${local.pre_install_path} 2>&1 | tee ${local.pre_install_log_path} + EOT +} + 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, - PRE_INSTALL_SCRIPT : var.pre_install_script != null ? var.pre_install_script : "", - POST_INSTALL_SCRIPT : var.post_install_script != null ? var.post_install_script : "", - }) + display_name = "Node.js: Install" + script = <<-EOT + #!/bin/bash + set -o errexit + set -o pipefail + + mkdir -p ${local.module_dir_path} + + trap 'coder exp sync complete ${local.install_script_name}' EXIT + %{if var.pre_install_script != null~} + coder exp sync want ${local.install_script_name} ${local.pre_install_script_name} + %{endif~} + coder exp sync start ${local.install_script_name} + + echo -n '${local.encoded_install_script}' | base64 -d > ${local.install_path} + chmod +x ${local.install_path} + + ${local.install_path} 2>&1 | tee ${local.install_log_path} + EOT run_on_start = true start_blocks_login = true } + +resource "coder_script" "post_install_script" { + count = var.post_install_script != null ? 1 : 0 + agent_id = var.agent_id + display_name = "Node.js: Post-Install" + run_on_start = true + script = <<-EOT + #!/bin/bash + set -o errexit + set -o pipefail + + mkdir -p ${local.module_dir_path} + + trap 'coder exp sync complete ${local.post_install_script_name}' EXIT + coder exp sync want ${local.post_install_script_name} ${local.install_script_name} + coder exp sync start ${local.post_install_script_name} + + echo -n '${local.encoded_post_install_script}' | base64 -d > ${local.post_install_path} + chmod +x ${local.post_install_path} + + ${local.post_install_path} 2>&1 | tee ${local.post_install_log_path} + EOT +} + +output "pre_install_script_name" { + description = "The name of the pre-install script for coder exp sync coordination." + value = local.pre_install_script_name +} + +output "install_script_name" { + description = "The name of the install script for coder exp sync coordination." + value = local.install_script_name +} + +output "post_install_script_name" { + description = "The name of the post-install script for coder exp sync coordination." + value = local.post_install_script_name +} diff --git a/registry/thezoker/modules/nodejs/nodejs.tftest.hcl b/registry/thezoker/modules/nodejs/nodejs.tftest.hcl index e8024e567..1897eed03 100644 --- a/registry/thezoker/modules/nodejs/nodejs.tftest.hcl +++ b/registry/thezoker/modules/nodejs/nodejs.tftest.hcl @@ -29,6 +29,11 @@ run "test_nodejs_basic" { condition = var.post_install_script == null error_message = "post_install_script should default to null" } + + assert { + condition = output.install_script_name == "nodejs-install_script" + error_message = "install_script_name output should be set" + } } run "test_with_scripts" { @@ -49,6 +54,16 @@ run "test_with_scripts" { condition = var.post_install_script == "echo 'Post-install script'" error_message = "Post-install script should be set correctly" } + + assert { + condition = output.pre_install_script_name == "nodejs-pre_install_script" + error_message = "pre_install_script_name output should be set" + } + + assert { + condition = output.post_install_script_name == "nodejs-post_install_script" + error_message = "post_install_script_name output should be set" + } } run "test_custom_options" { diff --git a/registry/thezoker/modules/nodejs/run.sh b/registry/thezoker/modules/nodejs/run.sh index 53e4b6caf..31044eb5b 100644 --- a/registry/thezoker/modules/nodejs/run.sh +++ b/registry/thezoker/modules/nodejs/run.sh @@ -4,17 +4,10 @@ NVM_VERSION='${NVM_VERSION}' NODE_VERSIONS='${NODE_VERSIONS}' INSTALL_PREFIX='${INSTALL_PREFIX}' DEFAULT='${DEFAULT}' -PRE_INSTALL_SCRIPT='${PRE_INSTALL_SCRIPT}' -POST_INSTALL_SCRIPT='${POST_INSTALL_SCRIPT}' BOLD='\033[0;1m' CODE='\033[36;40;1m' RESET='\033[0m' -if [ -n "$${PRE_INSTALL_SCRIPT}" ]; then - printf "$${BOLD}Running pre-install script...$${RESET}\n" - eval "$${PRE_INSTALL_SCRIPT}" -fi - printf "$${BOLD}Installing nvm!$${RESET}\n" export NVM_DIR="$HOME/$${INSTALL_PREFIX}/nvm" @@ -58,8 +51,3 @@ if [ -n "$${DEFAULT}" ]; then printf "🛠️ Setting default node version $${CODE}$DEFAULT$${RESET}...\n" output=$(nvm alias default $DEFAULT 2>&1) fi - -if [ -n "$${POST_INSTALL_SCRIPT}" ]; then - printf "$${BOLD}Running post-install script...$${RESET}\n" - eval "$${POST_INSTALL_SCRIPT}" -fi From 0fd0d08d2fbff9aa56f8311b115ea662bb237c69 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 18:02:37 +0000 Subject: [PATCH 7/7] fix: terraform fmt alignment in coder_script.nodejs resource --- registry/thezoker/modules/nodejs/main.tf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/registry/thezoker/modules/nodejs/main.tf b/registry/thezoker/modules/nodejs/main.tf index c11b89960..08a77c91e 100644 --- a/registry/thezoker/modules/nodejs/main.tf +++ b/registry/thezoker/modules/nodejs/main.tf @@ -99,9 +99,9 @@ resource "coder_script" "pre_install_script" { } resource "coder_script" "nodejs" { - agent_id = var.agent_id - display_name = "Node.js: Install" - script = <<-EOT + agent_id = var.agent_id + display_name = "Node.js: Install" + script = <<-EOT #!/bin/bash set -o errexit set -o pipefail