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
This commit is contained in:
parent
9606297620
commit
79cef2ecfc
@ -15,7 +15,7 @@ Automatically installs [Node.js](https://github.com/nodejs/node) via [`nvm`](htt
|
|||||||
module "nodejs" {
|
module "nodejs" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/thezoker/nodejs/coder"
|
source = "registry.coder.com/thezoker/nodejs/coder"
|
||||||
version = "1.0.13"
|
version = "1.0.14"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -28,17 +28,35 @@ This installs multiple versions of Node.js:
|
|||||||
module "nodejs" {
|
module "nodejs" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/thezoker/nodejs/coder"
|
source = "registry.coder.com/thezoker/nodejs/coder"
|
||||||
version = "1.0.13"
|
version = "1.0.14"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
node_versions = [
|
node_versions = [
|
||||||
"18",
|
"18",
|
||||||
"20",
|
"20",
|
||||||
"node"
|
"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
|
## Full example
|
||||||
|
|
||||||
A example with all available options:
|
A example with all available options:
|
||||||
@ -47,15 +65,17 @@ A example with all available options:
|
|||||||
module "nodejs" {
|
module "nodejs" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/thezoker/nodejs/coder"
|
source = "registry.coder.com/thezoker/nodejs/coder"
|
||||||
version = "1.0.13"
|
version = "1.0.14"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
nvm_version = "1.0.13"
|
nvm_version = "v0.39.7"
|
||||||
nvm_install_prefix = "/opt/nvm"
|
nvm_install_prefix = "/opt/nvm"
|
||||||
node_versions = [
|
node_versions = [
|
||||||
"16",
|
|
||||||
"18",
|
"18",
|
||||||
|
"20",
|
||||||
"node"
|
"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"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { describe } from "bun:test";
|
import { describe, expect, it } from "bun:test";
|
||||||
import { runTerraformInit, testRequiredVariables } from "~test";
|
import { runTerraformInit, testRequiredVariables, runTerraformApply } from "~test";
|
||||||
|
|
||||||
describe("nodejs", async () => {
|
describe("nodejs", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
@ -8,5 +8,19 @@ describe("nodejs", async () => {
|
|||||||
agent_id: "foo",
|
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();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -38,15 +38,114 @@ variable "default_node_version" {
|
|||||||
default = "node"
|
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" {
|
resource "coder_script" "nodejs" {
|
||||||
agent_id = var.agent_id
|
agent_id = var.agent_id
|
||||||
display_name = "Node.js:"
|
display_name = "Node.js: Install"
|
||||||
script = templatefile("${path.module}/run.sh", {
|
run_on_start = true
|
||||||
NVM_VERSION : var.nvm_version,
|
script = <<-EOT
|
||||||
INSTALL_PREFIX : var.nvm_install_prefix,
|
#!/bin/bash
|
||||||
NODE_VERSIONS : join(",", var.node_versions),
|
set -o errexit
|
||||||
DEFAULT : var.default_node_version,
|
set -o pipefail
|
||||||
})
|
|
||||||
run_on_start = true
|
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
|
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
|
||||||
|
}
|
||||||
|
|||||||
122
registry/thezoker/modules/nodejs/nodejs.tftest.hcl
Normal file
122
registry/thezoker/modules/nodejs/nodejs.tftest.hcl
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user