## Summary
Derives `coder-utils` script names from `module_directory` instead of a
separate `agent_name` input. The `module_directory` already encodes both
the namespace and the module name, so carrying both is redundant and
error-prone. Callers like `claude-code` no longer need to pass
`agent_name`.
Scripts this module materializes lose the `${agent_name}-utils-` prefix
because `module_directory` already namespaces them per-caller.
We will address multiple instances of coder-utils per caller in a future
iteration if needed.
## Versioning Note
Previous tags (`v1.0.0` through `v1.3.0`) have been deleted because no
published module ever consumed them — the module was effectively
unreleased. This PR ships the first real public version as **`v0.0.1`**,
treating it as a fresh start rather than a breaking bump from a version
that was never in production use.
## Changes
- Remove `agent_name` variable.
- Derive `caller_name = "${namespace}-${module_name}"` from
`module_directory`.
- Validate `module_directory` matches
`$HOME/.coder-modules/<namespace>/<module-name>`.
- Rename script files on disk from `${agent_name}-utils-<phase>.sh` to
plain `<phase>.sh`.
- Add a TS test for the `module_directory` validation.
- Ship as `v0.0.1` (first published version; all prior tags removed).
## Breaking Changes
| Before | After |
|---|---|
| `agent_name = "myagent"` | removed (derived from `module_directory`) |
| `module_directory = ".my-module"` | `module_directory =
"$HOME/.coder-modules/<ns>/<name>"` (validated) |
| Script files `${agent_name}-utils-install.sh` | `install.sh` |
| Script sync names `${agent_name}-install_script` |
`${namespace}-${module_name}-install_script` |
No callers were depending on the old format (prior tags were
unpublished).
## Validation
- `terraform fmt -recursive` clean
- `terraform validate` clean
- `terraform test` → 17/17 pass
- `bun test registry/coder/modules/coder-utils` → 5/5 pass
- `prettier --check` clean
## Consumer
coder/registry#861 (`claude-code`) consumes this and is currently pinned
to the commit SHA until this merges and ships as `v0.0.1`.
> 🤖 This PR was created with the help of Coder Agents, and needs a human
review. 🧑💻
629 lines
20 KiB
HCL
629 lines
20 KiB
HCL
# Test for coder-utils module
|
|
|
|
# Test with all scripts provided
|
|
run "test_with_all_scripts" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
pre_install_script = "echo 'pre-install'"
|
|
install_script = "echo 'install'"
|
|
post_install_script = "echo 'post-install'"
|
|
start_script = "echo 'start'"
|
|
}
|
|
|
|
# Verify pre_install_script is created when provided
|
|
assert {
|
|
condition = length(coder_script.pre_install_script) == 1
|
|
error_message = "Pre-install script should be created when pre_install_script is provided"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.pre_install_script[0].agent_id == "test-agent-id"
|
|
error_message = "Pre-install script agent ID should match input"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.pre_install_script[0].display_name == "Pre-Install Script"
|
|
error_message = "Pre-install script should have correct display name"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.pre_install_script[0].run_on_start == true
|
|
error_message = "Pre-install script should run on start"
|
|
}
|
|
|
|
# Verify install_script is always created
|
|
assert {
|
|
condition = coder_script.install_script.agent_id == "test-agent-id"
|
|
error_message = "Install script agent ID should match input"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.install_script.display_name == "Install Script"
|
|
error_message = "Install script should have correct display name"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.install_script.run_on_start == true
|
|
error_message = "Install script should run on start"
|
|
}
|
|
|
|
# install should sync-want pre_install
|
|
assert {
|
|
condition = can(regex("sync want test-example-install_script test-example-pre_install_script", coder_script.install_script.script))
|
|
error_message = "Install script should sync-want pre_install_script when pre_install is provided"
|
|
}
|
|
|
|
# Verify post_install_script is created when provided
|
|
assert {
|
|
condition = length(coder_script.post_install_script) == 1
|
|
error_message = "Post-install script should be created when post_install_script is provided"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.post_install_script[0].agent_id == "test-agent-id"
|
|
error_message = "Post-install script agent ID should match input"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.post_install_script[0].display_name == "Post-Install Script"
|
|
error_message = "Post-install script should have correct display name"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.post_install_script[0].run_on_start == true
|
|
error_message = "Post-install script should run on start"
|
|
}
|
|
|
|
# Verify start_script is created when provided
|
|
assert {
|
|
condition = length(coder_script.start_script) == 1
|
|
error_message = "Start script should be created when start_script is provided"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.start_script[0].agent_id == "test-agent-id"
|
|
error_message = "Start script agent ID should match input"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.start_script[0].display_name == "Start Script"
|
|
error_message = "Start script should have correct display name"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.start_script[0].run_on_start == true
|
|
error_message = "Start script should run on start"
|
|
}
|
|
}
|
|
|
|
run "test_invalid_module_directory" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test"
|
|
install_script = "echo 'install'"
|
|
}
|
|
|
|
expect_failures = [
|
|
var.module_directory,
|
|
]
|
|
}
|
|
|
|
# Test with only install_script (minimum required input)
|
|
run "test_install_only" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
install_script = "echo 'install'"
|
|
}
|
|
|
|
# Verify optional scripts are NOT created
|
|
assert {
|
|
condition = length(coder_script.pre_install_script) == 0
|
|
error_message = "Pre-install script should not be created when not provided"
|
|
}
|
|
|
|
assert {
|
|
condition = length(coder_script.post_install_script) == 0
|
|
error_message = "Post-install script should not be created when not provided"
|
|
}
|
|
|
|
assert {
|
|
condition = length(coder_script.start_script) == 0
|
|
error_message = "Start script should not be created when not provided"
|
|
}
|
|
|
|
# Verify install_script is created
|
|
assert {
|
|
condition = coder_script.install_script.agent_id == "test-agent-id"
|
|
error_message = "Install script should be created"
|
|
}
|
|
}
|
|
|
|
# Test with install and start scripts (no pre/post install)
|
|
run "test_install_and_start" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
install_script = "echo 'install'"
|
|
start_script = "echo 'start'"
|
|
}
|
|
|
|
assert {
|
|
condition = length(coder_script.pre_install_script) == 0
|
|
error_message = "Pre-install script should not be created when not provided"
|
|
}
|
|
|
|
assert {
|
|
condition = length(coder_script.post_install_script) == 0
|
|
error_message = "Post-install script should not be created when not provided"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.install_script.agent_id == "test-agent-id"
|
|
error_message = "Install script should be created"
|
|
}
|
|
|
|
assert {
|
|
condition = length(coder_script.start_script) == 1
|
|
error_message = "Start script should be created"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.start_script[0].agent_id == "test-agent-id"
|
|
error_message = "Start script agent ID should match input"
|
|
}
|
|
|
|
# start should sync-want install (no post_install)
|
|
assert {
|
|
condition = can(regex("sync want test-example-start_script test-example-install_script", coder_script.start_script[0].script))
|
|
error_message = "Start script should sync-want install_script"
|
|
}
|
|
}
|
|
|
|
# Test with mock data sources
|
|
run "test_with_mock_data" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "mock-agent"
|
|
module_directory = "$HOME/.coder-modules/test/mock"
|
|
install_script = "echo 'install'"
|
|
start_script = "echo 'start'"
|
|
}
|
|
|
|
override_data {
|
|
target = data.coder_workspace.me
|
|
values = {
|
|
id = "test-workspace-id"
|
|
name = "test-workspace"
|
|
owner = "test-owner"
|
|
owner_id = "test-owner-id"
|
|
template_id = "test-template-id"
|
|
template_name = "test-template"
|
|
access_url = "https://coder.example.com"
|
|
start_count = 1
|
|
transition = "start"
|
|
}
|
|
}
|
|
|
|
override_data {
|
|
target = data.coder_workspace_owner.me
|
|
values = {
|
|
id = "test-owner-id"
|
|
email = "test@example.com"
|
|
name = "Test User"
|
|
session_token = "mock-token"
|
|
}
|
|
}
|
|
|
|
override_data {
|
|
target = data.coder_task.me
|
|
values = {
|
|
id = "test-task-id"
|
|
}
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.install_script.agent_id == "mock-agent"
|
|
error_message = "Install script should use the mocked agent ID"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.start_script[0].agent_id == "mock-agent"
|
|
error_message = "Start script should use the mocked agent ID"
|
|
}
|
|
}
|
|
|
|
# Test sync naming derived from module_directory
|
|
run "test_script_naming_from_module_directory" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent"
|
|
module_directory = "$HOME/.coder-modules/custom/name"
|
|
install_script = "echo 'install'"
|
|
start_script = "echo 'start'"
|
|
}
|
|
|
|
assert {
|
|
condition = can(regex("custom-name-install_script", coder_script.install_script.script))
|
|
error_message = "Install script should derive sync names from module_directory"
|
|
}
|
|
|
|
assert {
|
|
condition = can(regex("custom-name-start_script", coder_script.start_script[0].script))
|
|
error_message = "Start script should derive sync names from module_directory"
|
|
}
|
|
}
|
|
|
|
# Test install syncs with pre_install when provided
|
|
run "test_install_syncs_with_pre_install" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
pre_install_script = "echo 'pre-install'"
|
|
install_script = "echo 'install'"
|
|
}
|
|
|
|
assert {
|
|
condition = length(coder_script.pre_install_script) == 1
|
|
error_message = "Pre-install script should be created"
|
|
}
|
|
|
|
assert {
|
|
condition = can(regex("sync want test-example-install_script test-example-pre_install_script", coder_script.install_script.script))
|
|
error_message = "Install script should sync-want pre_install_script"
|
|
}
|
|
}
|
|
|
|
# Test start script sync deps with post_install present
|
|
run "test_start_syncs_with_post_install" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
install_script = "echo 'install'"
|
|
post_install_script = "echo 'post-install'"
|
|
start_script = "echo 'start'"
|
|
}
|
|
|
|
# start should sync-want both install and post_install
|
|
assert {
|
|
condition = can(regex("sync want test-example-start_script test-example-install_script test-example-post_install_script", coder_script.start_script[0].script))
|
|
error_message = "Start script should sync-want both install_script and post_install_script"
|
|
}
|
|
|
|
# post_install should sync-want install
|
|
assert {
|
|
condition = can(regex("sync want test-example-post_install_script test-example-install_script", coder_script.post_install_script[0].script))
|
|
error_message = "Post-install script should sync-want install_script"
|
|
}
|
|
}
|
|
|
|
# Verify display_name_prefix is prepended to every script's display_name
|
|
run "test_display_name_prefix_applied" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
display_name_prefix = "Claude Code"
|
|
pre_install_script = "echo 'pre-install'"
|
|
install_script = "echo 'install'"
|
|
post_install_script = "echo 'post-install'"
|
|
start_script = "echo 'start'"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.pre_install_script[0].display_name == "Claude Code: Pre-Install Script"
|
|
error_message = "Pre-install script display_name should be prefixed"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.install_script.display_name == "Claude Code: Install Script"
|
|
error_message = "Install script display_name should be prefixed"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.post_install_script[0].display_name == "Claude Code: Post-Install Script"
|
|
error_message = "Post-install script display_name should be prefixed"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.start_script[0].display_name == "Claude Code: Start Script"
|
|
error_message = "Start script display_name should be prefixed"
|
|
}
|
|
}
|
|
|
|
# Verify icon is propagated to every coder_script
|
|
run "test_icon_applied" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
icon = "/icon/claude.svg"
|
|
pre_install_script = "echo 'pre-install'"
|
|
install_script = "echo 'install'"
|
|
post_install_script = "echo 'post-install'"
|
|
start_script = "echo 'start'"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.pre_install_script[0].icon == "/icon/claude.svg"
|
|
error_message = "Pre-install script icon should match input"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.install_script.icon == "/icon/claude.svg"
|
|
error_message = "Install script icon should match input"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.post_install_script[0].icon == "/icon/claude.svg"
|
|
error_message = "Post-install script icon should match input"
|
|
}
|
|
|
|
assert {
|
|
condition = coder_script.start_script[0].icon == "/icon/claude.svg"
|
|
error_message = "Start script icon should match input"
|
|
}
|
|
}
|
|
|
|
# Verify optional scripts are not created when their variables are unset
|
|
run "test_optional_scripts_absent_by_default" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
install_script = "echo install"
|
|
}
|
|
|
|
assert {
|
|
condition = length(coder_script.pre_install_script) == 0
|
|
error_message = "Pre-install coder_script should not be created when pre_install_script is unset"
|
|
}
|
|
|
|
assert {
|
|
condition = length(coder_script.post_install_script) == 0
|
|
error_message = "Post-install coder_script should not be created when post_install_script is unset"
|
|
}
|
|
|
|
assert {
|
|
condition = length(coder_script.start_script) == 0
|
|
error_message = "Start coder_script should not be created when start_script is unset"
|
|
}
|
|
}
|
|
|
|
# Verify `scripts` output is a filtered, run-order list
|
|
run "test_scripts_output_with_all" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
pre_install_script = "echo pre"
|
|
install_script = "echo install"
|
|
post_install_script = "echo post"
|
|
start_script = "echo start"
|
|
}
|
|
|
|
assert {
|
|
condition = length(output.scripts) == 4
|
|
error_message = "scripts should have 4 entries when every script is set"
|
|
}
|
|
|
|
assert {
|
|
condition = output.scripts[0] == "test-example-pre_install_script"
|
|
error_message = "scripts[0] must be the pre-install name"
|
|
}
|
|
|
|
assert {
|
|
condition = output.scripts[1] == "test-example-install_script"
|
|
error_message = "scripts[1] must be the install name"
|
|
}
|
|
|
|
assert {
|
|
condition = output.scripts[2] == "test-example-post_install_script"
|
|
error_message = "scripts[2] must be the post-install name"
|
|
}
|
|
|
|
assert {
|
|
condition = output.scripts[3] == "test-example-start_script"
|
|
error_message = "scripts[3] must be the start name"
|
|
}
|
|
}
|
|
|
|
run "test_scripts_output_with_install_only" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
install_script = "echo install"
|
|
}
|
|
|
|
assert {
|
|
condition = length(output.scripts) == 1
|
|
error_message = "scripts should have exactly 1 entry (install) when pre/post/start are unset"
|
|
}
|
|
|
|
assert {
|
|
condition = output.scripts[0] == "test-example-install_script"
|
|
error_message = "scripts[0] must be the install name"
|
|
}
|
|
}
|
|
|
|
run "test_scripts_output_with_install_and_post" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
install_script = "echo install"
|
|
post_install_script = "echo post"
|
|
}
|
|
|
|
assert {
|
|
condition = length(output.scripts) == 2
|
|
error_message = "scripts should have 2 entries (install, post)"
|
|
}
|
|
|
|
assert {
|
|
condition = output.scripts[0] == "test-example-install_script"
|
|
error_message = "scripts[0] must be the install name"
|
|
}
|
|
|
|
assert {
|
|
condition = output.scripts[1] == "test-example-post_install_script"
|
|
error_message = "scripts[1] must be the post-install name"
|
|
}
|
|
}
|
|
|
|
# Every script must stream combined stdout+stderr to both the agent log
|
|
# (via stdout) and the on-disk log file (via tee), so workspace users
|
|
# watching `coder_script` output in the UI see progress live and can
|
|
# read the same content from the log file after the fact.
|
|
run "test_scripts_tee_stdout_and_log_file" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
pre_install_script = "echo pre"
|
|
install_script = "echo install"
|
|
post_install_script = "echo post"
|
|
start_script = "echo start"
|
|
}
|
|
|
|
assert {
|
|
condition = can(regex("pre_install\\.sh 2>&1 \\| tee .*logs/pre_install\\.log", coder_script.pre_install_script[0].script))
|
|
error_message = "pre_install wrapper must tee combined output to the logs/ subdirectory"
|
|
}
|
|
|
|
assert {
|
|
condition = can(regex("install\\.sh 2>&1 \\| tee .*logs/install\\.log", coder_script.install_script.script))
|
|
error_message = "install wrapper must tee combined output to the logs/ subdirectory"
|
|
}
|
|
|
|
assert {
|
|
condition = can(regex("post_install\\.sh 2>&1 \\| tee .*logs/post_install\\.log", coder_script.post_install_script[0].script))
|
|
error_message = "post_install wrapper must tee combined output to the logs/ subdirectory"
|
|
}
|
|
|
|
assert {
|
|
condition = can(regex("start\\.sh 2>&1 \\| tee .*logs/start\\.log", coder_script.start_script[0].script))
|
|
error_message = "start wrapper must tee combined output to the logs/ subdirectory"
|
|
}
|
|
}
|
|
|
|
# Logs unconditionally land under ${module_directory}/logs/. Each script
|
|
# mkdirs that path before tee runs so the first script to execute creates it.
|
|
run "test_logs_nested_under_module_directory" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
pre_install_script = "echo pre"
|
|
install_script = "echo install"
|
|
post_install_script = "echo post"
|
|
start_script = "echo start"
|
|
}
|
|
|
|
assert {
|
|
condition = strcontains(coder_script.pre_install_script[0].script, "tee $HOME/.coder-modules/test/example/logs/pre_install.log")
|
|
error_message = "pre_install log must land under module_directory/logs"
|
|
}
|
|
|
|
assert {
|
|
condition = strcontains(coder_script.install_script.script, "tee $HOME/.coder-modules/test/example/logs/install.log")
|
|
error_message = "install log must land under module_directory/logs"
|
|
}
|
|
|
|
assert {
|
|
condition = strcontains(coder_script.post_install_script[0].script, "tee $HOME/.coder-modules/test/example/logs/post_install.log")
|
|
error_message = "post_install log must land under module_directory/logs"
|
|
}
|
|
|
|
assert {
|
|
condition = strcontains(coder_script.start_script[0].script, "tee $HOME/.coder-modules/test/example/logs/start.log")
|
|
error_message = "start log must land under module_directory/logs"
|
|
}
|
|
|
|
# Only pre_install and install mkdir the logs/ sub-path. post_install
|
|
# and start sync-depend on install so the directory already exists by
|
|
# the time they run.
|
|
assert {
|
|
condition = strcontains(coder_script.pre_install_script[0].script, "mkdir -p $HOME/.coder-modules/test/example/logs")
|
|
error_message = "pre_install script must mkdir -p the logs/ sub-path"
|
|
}
|
|
|
|
assert {
|
|
condition = strcontains(coder_script.install_script.script, "mkdir -p $HOME/.coder-modules/test/example/logs")
|
|
error_message = "install script must mkdir -p the logs/ sub-path"
|
|
}
|
|
}
|
|
|
|
# Scripts unconditionally land under ${module_directory}/scripts/. Each
|
|
# script that materializes its own `.sh` file mkdirs that path first; the
|
|
# first script to execute creates it for the rest.
|
|
run "test_scripts_nested_under_module_directory" {
|
|
command = plan
|
|
|
|
variables {
|
|
agent_id = "test-agent-id"
|
|
module_directory = "$HOME/.coder-modules/test/example"
|
|
pre_install_script = "echo pre"
|
|
install_script = "echo install"
|
|
post_install_script = "echo post"
|
|
start_script = "echo start"
|
|
}
|
|
|
|
assert {
|
|
condition = strcontains(coder_script.pre_install_script[0].script, "> $HOME/.coder-modules/test/example/scripts/pre_install.sh")
|
|
error_message = "pre_install script must be written under module_directory/scripts"
|
|
}
|
|
|
|
assert {
|
|
condition = strcontains(coder_script.install_script.script, "> $HOME/.coder-modules/test/example/scripts/install.sh")
|
|
error_message = "install script must be written under module_directory/scripts"
|
|
}
|
|
|
|
assert {
|
|
condition = strcontains(coder_script.post_install_script[0].script, "> $HOME/.coder-modules/test/example/scripts/post_install.sh")
|
|
error_message = "post_install script must be written under module_directory/scripts"
|
|
}
|
|
|
|
assert {
|
|
condition = strcontains(coder_script.start_script[0].script, "> $HOME/.coder-modules/test/example/scripts/start.sh")
|
|
error_message = "start script must be written under module_directory/scripts"
|
|
}
|
|
|
|
# Only pre_install and install mkdir the scripts/ sub-path. post_install
|
|
# and start sync-depend on install so the directory already exists by
|
|
# the time they run.
|
|
assert {
|
|
condition = strcontains(coder_script.pre_install_script[0].script, "mkdir -p $HOME/.coder-modules/test/example/scripts")
|
|
error_message = "pre_install script must mkdir -p the scripts/ sub-path"
|
|
}
|
|
|
|
assert {
|
|
condition = strcontains(coder_script.install_script.script, "mkdir -p $HOME/.coder-modules/test/example/scripts")
|
|
error_message = "install script must mkdir -p the scripts/ sub-path"
|
|
}
|
|
}
|