From 57f52388ef7df7b9246a8704ed882bbf8c88a448 Mon Sep 17 00:00:00 2001 From: Jay Kumar Date: Mon, 13 Apr 2026 16:02:24 +0000 Subject: [PATCH 01/17] feat(registry/coder/modules/coder-utils): make install_script and start_script optional Both variables now default to null. Their coder_script resources are conditionally created with count, matching the existing pattern for pre_install_script and post_install_script. Sync dependency resolution is computed in locals so each stage waits for its closest existing predecessor in the chain: pre_install -> install -> post_install -> start. Outputs return null when the corresponding variable is not provided. --- .../coder/modules/coder-utils/main.test.ts | 1 - registry/coder/modules/coder-utils/main.tf | 42 ++- .../coder/modules/coder-utils/main.tftest.hcl | 250 ++++++++++++++++-- 3 files changed, 256 insertions(+), 37 deletions(-) diff --git a/registry/coder/modules/coder-utils/main.test.ts b/registry/coder/modules/coder-utils/main.test.ts index 5987c3288..bb64b5c59 100644 --- a/registry/coder/modules/coder-utils/main.test.ts +++ b/registry/coder/modules/coder-utils/main.test.ts @@ -8,6 +8,5 @@ describe("coder-utils", async () => { agent_id: "test-agent-id", agent_name: "test-agent", module_dir_name: ".test-module", - start_script: "echo 'start'", }); }); diff --git a/registry/coder/modules/coder-utils/main.tf b/registry/coder/modules/coder-utils/main.tf index cfb8b778a..8d9b868db 100644 --- a/registry/coder/modules/coder-utils/main.tf +++ b/registry/coder/modules/coder-utils/main.tf @@ -41,6 +41,7 @@ variable "post_install_script" { variable "start_script" { type = string description = "Script that starts AgentAPI." + default = null } variable "agent_name" { @@ -58,7 +59,7 @@ locals { encoded_pre_install_script = var.pre_install_script != null ? base64encode(var.pre_install_script) : "" encoded_install_script = var.install_script != null ? base64encode(var.install_script) : "" encoded_post_install_script = var.post_install_script != null ? base64encode(var.post_install_script) : "" - encoded_start_script = base64encode(var.start_script) + encoded_start_script = var.start_script != null ? base64encode(var.start_script) : "" pre_install_script_name = "${var.agent_name}-pre_install_script" install_script_name = "${var.agent_name}-install_script" @@ -76,6 +77,25 @@ locals { install_log_path = "${local.module_dir_path}/install.log" post_install_log_path = "${local.module_dir_path}/post_install.log" start_log_path = "${local.module_dir_path}/start.log" + + # Sync dependency resolution: each stage waits for its closest predecessor. + # Chain order: pre_install -> install -> post_install -> start + post_install_sync_deps = ( + var.install_script != null ? local.install_script_name : ( + var.pre_install_script != null ? local.pre_install_script_name : null + ) + ) + + start_sync_deps = ( + var.post_install_script != null && var.install_script != null + ? "${local.install_script_name} ${local.post_install_script_name}" : ( + var.post_install_script != null ? local.post_install_script_name : ( + var.install_script != null ? local.install_script_name : ( + var.pre_install_script != null ? local.pre_install_script_name : null + ) + ) + ) + ) } resource "coder_script" "pre_install_script" { @@ -101,6 +121,7 @@ resource "coder_script" "pre_install_script" { } resource "coder_script" "install_script" { + count = var.install_script != null ? 1 : 0 agent_id = var.agent_id display_name = "Install Script" run_on_start = true @@ -134,7 +155,9 @@ resource "coder_script" "post_install_script" { 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} + %{if local.post_install_sync_deps != null~} + coder exp sync want ${local.post_install_script_name} ${local.post_install_sync_deps} + %{endif~} coder exp sync start ${local.post_install_script_name} echo -n '${local.encoded_post_install_script}' | base64 -d > ${local.post_install_path} @@ -145,6 +168,7 @@ resource "coder_script" "post_install_script" { } resource "coder_script" "start_script" { + count = var.start_script != null ? 1 : 0 agent_id = var.agent_id display_name = "Start Script" run_on_start = true @@ -155,10 +179,8 @@ resource "coder_script" "start_script" { trap 'coder exp sync complete ${local.start_script_name}' EXIT - %{if var.post_install_script != null~} - coder exp sync want ${local.start_script_name} ${local.install_script_name} ${local.post_install_script_name} - %{else~} - coder exp sync want ${local.start_script_name} ${local.install_script_name} + %{if local.start_sync_deps != null~} + coder exp sync want ${local.start_script_name} ${local.start_sync_deps} %{endif~} coder exp sync start ${local.start_script_name} @@ -171,20 +193,20 @@ resource "coder_script" "start_script" { output "pre_install_script_name" { description = "The name of the pre-install script for sync." - value = local.pre_install_script_name + value = var.pre_install_script != null ? local.pre_install_script_name : null } output "install_script_name" { description = "The name of the install script for sync." - value = local.install_script_name + value = var.install_script != null ? local.install_script_name : null } output "post_install_script_name" { description = "The name of the post-install script for sync." - value = local.post_install_script_name + value = var.post_install_script != null ? local.post_install_script_name : null } output "start_script_name" { description = "The name of the start script for sync." - value = local.start_script_name + value = var.start_script != null ? local.start_script_name : null } \ No newline at end of file diff --git a/registry/coder/modules/coder-utils/main.tftest.hcl b/registry/coder/modules/coder-utils/main.tftest.hcl index d1228a6d4..4432e4be3 100644 --- a/registry/coder/modules/coder-utils/main.tftest.hcl +++ b/registry/coder/modules/coder-utils/main.tftest.hcl @@ -35,19 +35,24 @@ run "test_with_all_scripts" { error_message = "Pre-install script should run on start" } - # Verify install_script is created + # Verify install_script is created when provided assert { - condition = coder_script.install_script.agent_id == "test-agent-id" + condition = length(coder_script.install_script) == 1 + error_message = "Install script should be created when install_script is provided" + } + + assert { + condition = coder_script.install_script[0].agent_id == "test-agent-id" error_message = "Install script agent ID should match input" } assert { - condition = coder_script.install_script.display_name == "Install Script" + condition = coder_script.install_script[0].display_name == "Install Script" error_message = "Install script should have correct display name" } assert { - condition = coder_script.install_script.run_on_start == true + condition = coder_script.install_script[0].run_on_start == true error_message = "Install script should run on start" } @@ -72,19 +77,24 @@ run "test_with_all_scripts" { error_message = "Post-install script should run on start" } - # Verify start_script is created + # Verify start_script is created when provided assert { - condition = coder_script.start_script.agent_id == "test-agent-id" + 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.display_name == "Start Script" + 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.run_on_start == true + condition = coder_script.start_script[0].run_on_start == true error_message = "Start script should run on start" } @@ -110,8 +120,8 @@ run "test_with_all_scripts" { } } -# Test with only required scripts (no pre/post install) -run "test_without_optional_scripts" { +# Test with only install and start scripts (no pre/post install) +run "test_without_optional_pre_post_scripts" { command = plan variables { @@ -134,21 +144,31 @@ run "test_without_optional_scripts" { error_message = "Post-install script should not be created when post_install_script is null" } - # Verify required scripts are still created + # Verify install and start scripts are still created assert { - condition = coder_script.install_script.agent_id == "test-agent-id" + condition = length(coder_script.install_script) == 1 error_message = "Install script should be created" } assert { - condition = coder_script.start_script.agent_id == "test-agent-id" + condition = coder_script.install_script[0].agent_id == "test-agent-id" + error_message = "Install script agent ID should match input" + } + + assert { + condition = length(coder_script.start_script) == 1 error_message = "Start script should be created" } - # Verify outputs assert { - condition = output.pre_install_script_name == "test-agent-pre_install_script" - error_message = "Pre-install script name output should be generated even when script is not created" + condition = coder_script.start_script[0].agent_id == "test-agent-id" + error_message = "Start script agent ID should match input" + } + + # Verify outputs are null for unprovided scripts + assert { + condition = output.pre_install_script_name == null + error_message = "Pre-install script name output should be null when script is not provided" } assert { @@ -157,8 +177,8 @@ run "test_without_optional_scripts" { } assert { - condition = output.post_install_script_name == "test-agent-post_install_script" - error_message = "Post-install script name output should be generated even when script is not created" + condition = output.post_install_script_name == null + error_message = "Post-install script name output should be null when script is not provided" } assert { @@ -167,6 +187,128 @@ run "test_without_optional_scripts" { } } +# Test with no scripts provided (all optional) +run "test_no_scripts" { + command = plan + + variables { + agent_id = "test-agent-id" + agent_name = "test-agent" + module_dir_name = ".test-module" + } + + # Verify no scripts are 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.install_script) == 0 + error_message = "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 all outputs are null + assert { + condition = output.pre_install_script_name == null + error_message = "Pre-install script name output should be null" + } + + assert { + condition = output.install_script_name == null + error_message = "Install script name output should be null" + } + + assert { + condition = output.post_install_script_name == null + error_message = "Post-install script name output should be null" + } + + assert { + condition = output.start_script_name == null + error_message = "Start script name output should be null" + } +} + +# Test with only start_script (no install) +run "test_start_only" { + command = plan + + variables { + agent_id = "test-agent-id" + agent_name = "test-agent" + module_dir_name = ".test-module" + start_script = "echo 'start'" + } + + assert { + condition = length(coder_script.install_script) == 0 + error_message = "Install script should not be created when not provided" + } + + assert { + condition = length(coder_script.start_script) == 1 + error_message = "Start script should be created when provided" + } + + assert { + condition = coder_script.start_script[0].agent_id == "test-agent-id" + error_message = "Start script agent ID should match input" + } + + assert { + condition = output.install_script_name == null + error_message = "Install script name output should be null" + } + + assert { + condition = output.start_script_name == "test-agent-start_script" + error_message = "Start script name output should be correctly formatted" + } +} + +# Test with only install_script (no start) +run "test_install_only" { + command = plan + + variables { + agent_id = "test-agent-id" + agent_name = "test-agent" + module_dir_name = ".test-module" + install_script = "echo 'install'" + } + + assert { + condition = length(coder_script.install_script) == 1 + error_message = "Install script should be created when provided" + } + + assert { + condition = length(coder_script.start_script) == 0 + error_message = "Start script should not be created when not provided" + } + + assert { + condition = output.install_script_name == "test-agent-install_script" + error_message = "Install script name output should be correctly formatted" + } + + assert { + condition = output.start_script_name == null + error_message = "Start script name output should be null" + } +} + # Test with mock data sources run "test_with_mock_data" { command = plan @@ -214,12 +356,12 @@ run "test_with_mock_data" { # Verify scripts are created with mocked data assert { - condition = coder_script.install_script.agent_id == "mock-agent" + condition = coder_script.install_script[0].agent_id == "mock-agent" error_message = "Install script should use the mocked agent ID" } assert { - condition = coder_script.start_script.agent_id == "mock-agent" + condition = coder_script.start_script[0].agent_id == "mock-agent" error_message = "Start script should use the mocked agent ID" } } @@ -239,19 +381,19 @@ run "test_script_naming" { # Verify script names are constructed correctly # The script should contain references to custom-name-* in the sync commands assert { - condition = can(regex("custom-name-install_script", coder_script.install_script.script)) + condition = can(regex("custom-name-install_script", coder_script.install_script[0].script)) error_message = "Install script should use custom agent_name in sync commands" } assert { - condition = can(regex("custom-name-start_script", coder_script.start_script.script)) + condition = can(regex("custom-name-start_script", coder_script.start_script[0].script)) error_message = "Start script should use custom agent_name in sync commands" } # Verify outputs use custom agent_name assert { - condition = output.pre_install_script_name == "custom-name-pre_install_script" - error_message = "Pre-install script name output should use custom agent_name" + condition = output.pre_install_script_name == null + error_message = "Pre-install script name output should be null when not provided" } assert { @@ -260,8 +402,8 @@ run "test_script_naming" { } assert { - condition = output.post_install_script_name == "custom-name-post_install_script" - error_message = "Post-install script name output should use custom agent_name" + condition = output.post_install_script_name == null + error_message = "Post-install script name output should be null when not provided" } assert { @@ -269,3 +411,59 @@ run "test_script_naming" { error_message = "Start script name output should use custom agent_name" } } + +# Test with post_install but no install (sync deps should fall back to pre_install) +run "test_post_install_without_install" { + command = plan + + variables { + agent_id = "test-agent-id" + agent_name = "test-agent" + module_dir_name = ".test-module" + pre_install_script = "echo 'pre-install'" + post_install_script = "echo 'post-install'" + start_script = "echo 'start'" + } + + assert { + condition = length(coder_script.pre_install_script) == 1 + error_message = "Pre-install script should be created" + } + + assert { + condition = length(coder_script.install_script) == 0 + error_message = "Install script should not be created" + } + + assert { + condition = length(coder_script.post_install_script) == 1 + error_message = "Post-install script should be created" + } + + assert { + condition = length(coder_script.start_script) == 1 + error_message = "Start script should be created" + } + + # post_install should sync-want pre_install (since install is absent) + assert { + condition = can(regex("sync want test-agent-post_install_script test-agent-pre_install_script", coder_script.post_install_script[0].script)) + error_message = "Post-install script should sync-want pre_install_script when install_script is absent" + } + + # start should sync-want post_install (since install is absent) + assert { + condition = can(regex("sync want test-agent-start_script test-agent-post_install_script", coder_script.start_script[0].script)) + error_message = "Start script should sync-want post_install_script when install_script is absent" + } + + assert { + condition = output.pre_install_script_name == "test-agent-pre_install_script" + error_message = "Pre-install script name output should be set" + } + + assert { + condition = output.install_script_name == null + error_message = "Install script name output should be null" + } +} From fadaee88985223dc6f279a41e226fe8b35545e22 Mon Sep 17 00:00:00 2001 From: Jay Kumar Date: Mon, 13 Apr 2026 17:19:00 +0000 Subject: [PATCH 02/17] fix(registry/coder/modules/coder-utils): add mkdir -p to all script blocks and update module_dir_path Every script block now ensures the module directory exists, since any script can be the first (and only) one to run. Updated module_dir_path to use the namespaced path $HOME/.coder-modules/coder/coder-utils/. --- registry/coder/modules/coder-utils/main.tf | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/registry/coder/modules/coder-utils/main.tf b/registry/coder/modules/coder-utils/main.tf index 8d9b868db..cb621262a 100644 --- a/registry/coder/modules/coder-utils/main.tf +++ b/registry/coder/modules/coder-utils/main.tf @@ -66,7 +66,7 @@ locals { post_install_script_name = "${var.agent_name}-post_install_script" start_script_name = "${var.agent_name}-start_script" - module_dir_path = "$HOME/${var.module_dir_name}" + module_dir_path = "$HOME/.coder-modules/coder/coder-utils/${var.module_dir_name}" pre_install_path = "${local.module_dir_path}/pre_install.sh" install_path = "${local.module_dir_path}/install.sh" @@ -154,6 +154,8 @@ resource "coder_script" "post_install_script" { set -o errexit set -o pipefail + mkdir -p ${local.module_dir_path} + trap 'coder exp sync complete ${local.post_install_script_name}' EXIT %{if local.post_install_sync_deps != null~} coder exp sync want ${local.post_install_script_name} ${local.post_install_sync_deps} @@ -177,6 +179,8 @@ resource "coder_script" "start_script" { set -o errexit set -o pipefail + mkdir -p ${local.module_dir_path} + trap 'coder exp sync complete ${local.start_script_name}' EXIT %{if local.start_sync_deps != null~} From 87291223e3f552e9d8c15212a8cd7ccbf0465001 Mon Sep 17 00:00:00 2001 From: Jay Kumar Date: Mon, 13 Apr 2026 17:23:46 +0000 Subject: [PATCH 03/17] fix(registry/coder/modules/coder-utils): return empty string instead of null for unused outputs --- registry/coder/modules/coder-utils/main.tf | 8 ++-- .../coder/modules/coder-utils/main.tftest.hcl | 46 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/registry/coder/modules/coder-utils/main.tf b/registry/coder/modules/coder-utils/main.tf index cb621262a..5d4173907 100644 --- a/registry/coder/modules/coder-utils/main.tf +++ b/registry/coder/modules/coder-utils/main.tf @@ -197,20 +197,20 @@ resource "coder_script" "start_script" { output "pre_install_script_name" { description = "The name of the pre-install script for sync." - value = var.pre_install_script != null ? local.pre_install_script_name : null + value = var.pre_install_script != null ? local.pre_install_script_name : "" } output "install_script_name" { description = "The name of the install script for sync." - value = var.install_script != null ? local.install_script_name : null + value = var.install_script != null ? local.install_script_name : "" } output "post_install_script_name" { description = "The name of the post-install script for sync." - value = var.post_install_script != null ? local.post_install_script_name : null + value = var.post_install_script != null ? local.post_install_script_name : "" } output "start_script_name" { description = "The name of the start script for sync." - value = var.start_script != null ? local.start_script_name : null + value = var.start_script != null ? local.start_script_name : "" } \ No newline at end of file diff --git a/registry/coder/modules/coder-utils/main.tftest.hcl b/registry/coder/modules/coder-utils/main.tftest.hcl index 4432e4be3..f25ec4243 100644 --- a/registry/coder/modules/coder-utils/main.tftest.hcl +++ b/registry/coder/modules/coder-utils/main.tftest.hcl @@ -167,8 +167,8 @@ run "test_without_optional_pre_post_scripts" { # Verify outputs are null for unprovided scripts assert { - condition = output.pre_install_script_name == null - error_message = "Pre-install script name output should be null when script is not provided" + condition = output.pre_install_script_name == "" + error_message = "Pre-install script name output should be empty when script is not provided" } assert { @@ -177,8 +177,8 @@ run "test_without_optional_pre_post_scripts" { } assert { - condition = output.post_install_script_name == null - error_message = "Post-install script name output should be null when script is not provided" + condition = output.post_install_script_name == "" + error_message = "Post-install script name output should be empty when script is not provided" } assert { @@ -218,25 +218,25 @@ run "test_no_scripts" { error_message = "Start script should not be created when not provided" } - # Verify all outputs are null + # Verify all outputs are empty strings assert { - condition = output.pre_install_script_name == null - error_message = "Pre-install script name output should be null" + condition = output.pre_install_script_name == "" + error_message = "Pre-install script name output should be empty" } assert { - condition = output.install_script_name == null - error_message = "Install script name output should be null" + condition = output.install_script_name == "" + error_message = "Install script name output should be empty" } assert { - condition = output.post_install_script_name == null - error_message = "Post-install script name output should be null" + condition = output.post_install_script_name == "" + error_message = "Post-install script name output should be empty" } assert { - condition = output.start_script_name == null - error_message = "Start script name output should be null" + condition = output.start_script_name == "" + error_message = "Start script name output should be empty" } } @@ -267,8 +267,8 @@ run "test_start_only" { } assert { - condition = output.install_script_name == null - error_message = "Install script name output should be null" + condition = output.install_script_name == "" + error_message = "Install script name output should be empty" } assert { @@ -304,8 +304,8 @@ run "test_install_only" { } assert { - condition = output.start_script_name == null - error_message = "Start script name output should be null" + condition = output.start_script_name == "" + error_message = "Start script name output should be empty" } } @@ -392,8 +392,8 @@ run "test_script_naming" { # Verify outputs use custom agent_name assert { - condition = output.pre_install_script_name == null - error_message = "Pre-install script name output should be null when not provided" + condition = output.pre_install_script_name == "" + error_message = "Pre-install script name output should be empty when not provided" } assert { @@ -402,8 +402,8 @@ run "test_script_naming" { } assert { - condition = output.post_install_script_name == null - error_message = "Post-install script name output should be null when not provided" + condition = output.post_install_script_name == "" + error_message = "Post-install script name output should be empty when not provided" } assert { @@ -463,7 +463,7 @@ run "test_post_install_without_install" { } assert { - condition = output.install_script_name == null - error_message = "Install script name output should be null" + condition = output.install_script_name == "" + error_message = "Install script name output should be empty" } } From 9127a111e3c019f14a9760f631037077eeec0990 Mon Sep 17 00:00:00 2001 From: Jay Kumar Date: Mon, 13 Apr 2026 17:29:38 +0000 Subject: [PATCH 04/17] docs(registry/coder/modules/coder-utils): bump version to 1.1.0 --- registry/coder/modules/coder-utils/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/registry/coder/modules/coder-utils/README.md b/registry/coder/modules/coder-utils/README.md index 25ae28544..976e00463 100644 --- a/registry/coder/modules/coder-utils/README.md +++ b/registry/coder/modules/coder-utils/README.md @@ -20,7 +20,7 @@ The Coder Utils module is a building block for modules that need to run multiple ```tf module "coder_utils" { source = "registry.coder.com/coder/coder-utils/coder" - version = "1.0.1" + version = "1.1.0" agent_id = coder_agent.main.id agent_name = "myagent" @@ -58,8 +58,8 @@ The module orchestrates scripts in the following order: 1. **Log File Creation** - Creates module directory and log files 2. **Pre-Install Script** (optional) - Runs before installation -3. **Install Script** - Main installation +3. **Install Script** (optional) - Main installation 4. **Post-Install Script** (optional) - Runs after installation -5. **Start Script** - Starts the application +5. **Start Script** (optional) - Starts the application Each script waits for its prerequisites to complete before running using `coder exp sync` dependency management. From f203a780e8f18f07f56d8d1ea365b8ae26e86a40 Mon Sep 17 00:00:00 2001 From: Jay Kumar Date: Mon, 13 Apr 2026 17:49:21 +0000 Subject: [PATCH 05/17] refactor(registry/coder/modules/coder-utils): make install_script required, keep others optional install_script is now mandatory (no default). pre_install_script, post_install_script, and start_script remain optional with default null. Since install is always present, sync deps simplify: post_install always waits for install, start always waits for install (plus post_install when present). Removed validation blocks and fallback chains that are no longer needed. Bumped required Terraform version to >= 1.9. --- .../coder/modules/coder-utils/main.test.ts | 1 + registry/coder/modules/coder-utils/main.tf | 34 +-- .../coder/modules/coder-utils/main.tftest.hcl | 206 ++++-------------- 3 files changed, 58 insertions(+), 183 deletions(-) diff --git a/registry/coder/modules/coder-utils/main.test.ts b/registry/coder/modules/coder-utils/main.test.ts index bb64b5c59..42456fa7d 100644 --- a/registry/coder/modules/coder-utils/main.test.ts +++ b/registry/coder/modules/coder-utils/main.test.ts @@ -8,5 +8,6 @@ describe("coder-utils", async () => { agent_id: "test-agent-id", agent_name: "test-agent", module_dir_name: ".test-module", + install_script: "echo 'install'", }); }); diff --git a/registry/coder/modules/coder-utils/main.tf b/registry/coder/modules/coder-utils/main.tf index 5d4173907..7d7d5c7bf 100644 --- a/registry/coder/modules/coder-utils/main.tf +++ b/registry/coder/modules/coder-utils/main.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.9" required_providers { coder = { @@ -29,7 +29,6 @@ variable "pre_install_script" { variable "install_script" { type = string description = "Script to install the agent used by AgentAPI." - default = null } variable "post_install_script" { @@ -57,7 +56,7 @@ variable "module_dir_name" { locals { encoded_pre_install_script = var.pre_install_script != null ? base64encode(var.pre_install_script) : "" - encoded_install_script = var.install_script != null ? base64encode(var.install_script) : "" + encoded_install_script = base64encode(var.install_script) encoded_post_install_script = var.post_install_script != null ? base64encode(var.post_install_script) : "" encoded_start_script = var.start_script != null ? base64encode(var.start_script) : "" @@ -78,23 +77,13 @@ locals { post_install_log_path = "${local.module_dir_path}/post_install.log" start_log_path = "${local.module_dir_path}/start.log" - # Sync dependency resolution: each stage waits for its closest predecessor. + # Sync dependency resolution: each stage waits for its predecessors. # Chain order: pre_install -> install -> post_install -> start - post_install_sync_deps = ( - var.install_script != null ? local.install_script_name : ( - var.pre_install_script != null ? local.pre_install_script_name : null - ) - ) - + # install_script is always present (required), so it anchors the chain. start_sync_deps = ( - var.post_install_script != null && var.install_script != null - ? "${local.install_script_name} ${local.post_install_script_name}" : ( - var.post_install_script != null ? local.post_install_script_name : ( - var.install_script != null ? local.install_script_name : ( - var.pre_install_script != null ? local.pre_install_script_name : null - ) - ) - ) + var.post_install_script != null + ? "${local.install_script_name} ${local.post_install_script_name}" + : local.install_script_name ) } @@ -121,7 +110,6 @@ resource "coder_script" "pre_install_script" { } resource "coder_script" "install_script" { - count = var.install_script != null ? 1 : 0 agent_id = var.agent_id display_name = "Install Script" run_on_start = true @@ -157,9 +145,7 @@ resource "coder_script" "post_install_script" { mkdir -p ${local.module_dir_path} trap 'coder exp sync complete ${local.post_install_script_name}' EXIT - %{if local.post_install_sync_deps != null~} - coder exp sync want ${local.post_install_script_name} ${local.post_install_sync_deps} - %{endif~} + 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} @@ -183,9 +169,7 @@ resource "coder_script" "start_script" { trap 'coder exp sync complete ${local.start_script_name}' EXIT - %{if local.start_sync_deps != null~} coder exp sync want ${local.start_script_name} ${local.start_sync_deps} - %{endif~} coder exp sync start ${local.start_script_name} echo -n '${local.encoded_start_script}' | base64 -d > ${local.start_path} @@ -202,7 +186,7 @@ output "pre_install_script_name" { output "install_script_name" { description = "The name of the install script for sync." - value = var.install_script != null ? local.install_script_name : "" + value = local.install_script_name } output "post_install_script_name" { diff --git a/registry/coder/modules/coder-utils/main.tftest.hcl b/registry/coder/modules/coder-utils/main.tftest.hcl index f25ec4243..9968a3f1c 100644 --- a/registry/coder/modules/coder-utils/main.tftest.hcl +++ b/registry/coder/modules/coder-utils/main.tftest.hcl @@ -35,24 +35,19 @@ run "test_with_all_scripts" { error_message = "Pre-install script should run on start" } - # Verify install_script is created when provided + # Verify install_script is always created assert { - condition = length(coder_script.install_script) == 1 - error_message = "Install script should be created when install_script is provided" - } - - assert { - condition = coder_script.install_script[0].agent_id == "test-agent-id" + 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[0].display_name == "Install Script" + condition = coder_script.install_script.display_name == "Install Script" error_message = "Install script should have correct display name" } assert { - condition = coder_script.install_script[0].run_on_start == true + condition = coder_script.install_script.run_on_start == true error_message = "Install script should run on start" } @@ -120,8 +115,8 @@ run "test_with_all_scripts" { } } -# Test with only install and start scripts (no pre/post install) -run "test_without_optional_pre_post_scripts" { +# Test with only install_script (minimum required input) +run "test_install_only" { command = plan variables { @@ -129,46 +124,34 @@ run "test_without_optional_pre_post_scripts" { agent_name = "test-agent" module_dir_name = ".test-module" install_script = "echo 'install'" - start_script = "echo 'start'" } - # Verify pre_install_script is NOT created when not provided + # 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 pre_install_script is null" + error_message = "Pre-install script should not be created when not provided" } - # Verify post_install_script is NOT created when not provided assert { condition = length(coder_script.post_install_script) == 0 - error_message = "Post-install script should not be created when post_install_script is null" - } - - # Verify install and start scripts are still created - assert { - condition = length(coder_script.install_script) == 1 - error_message = "Install script should be created" - } - - assert { - condition = coder_script.install_script[0].agent_id == "test-agent-id" - error_message = "Install script agent ID should match input" + error_message = "Post-install script should not be created when not provided" } assert { - condition = length(coder_script.start_script) == 1 - error_message = "Start script should be created" + 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.start_script[0].agent_id == "test-agent-id" - error_message = "Start script agent ID should match input" + condition = coder_script.install_script.agent_id == "test-agent-id" + error_message = "Install script should be created" } - # Verify outputs are null for unprovided scripts + # Verify outputs assert { condition = output.pre_install_script_name == "" - error_message = "Pre-install script name output should be empty when script is not provided" + error_message = "Pre-install script name output should be empty" } assert { @@ -178,87 +161,45 @@ run "test_without_optional_pre_post_scripts" { assert { condition = output.post_install_script_name == "" - error_message = "Post-install script name output should be empty when script is not provided" + error_message = "Post-install script name output should be empty" } assert { - condition = output.start_script_name == "test-agent-start_script" - error_message = "Start script name output should be correctly formatted" + condition = output.start_script_name == "" + error_message = "Start script name output should be empty" } } -# Test with no scripts provided (all optional) -run "test_no_scripts" { +# Test with install and start scripts (no pre/post install) +run "test_install_and_start" { command = plan variables { agent_id = "test-agent-id" agent_name = "test-agent" module_dir_name = ".test-module" + install_script = "echo 'install'" + start_script = "echo 'start'" } - # Verify no scripts are 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.install_script) == 0 - error_message = "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 all outputs are empty strings - assert { - condition = output.pre_install_script_name == "" - error_message = "Pre-install script name output should be empty" - } - - assert { - condition = output.install_script_name == "" - error_message = "Install script name output should be empty" - } - - assert { - condition = output.post_install_script_name == "" - error_message = "Post-install script name output should be empty" - } - - assert { - condition = output.start_script_name == "" - error_message = "Start script name output should be empty" - } -} - -# Test with only start_script (no install) -run "test_start_only" { - command = plan - - variables { - agent_id = "test-agent-id" - agent_name = "test-agent" - module_dir_name = ".test-module" - start_script = "echo 'start'" - } - - assert { - condition = length(coder_script.install_script) == 0 - error_message = "Install script should not be created when not provided" + 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 when provided" + error_message = "Start script should be created" } assert { @@ -266,46 +207,30 @@ run "test_start_only" { error_message = "Start script agent ID should match input" } + # start should sync-want install (no post_install) assert { - condition = output.install_script_name == "" - error_message = "Install script name output should be empty" + condition = can(regex("sync want test-agent-start_script test-agent-install_script", coder_script.start_script[0].script)) + error_message = "Start script should sync-want install_script" } assert { - condition = output.start_script_name == "test-agent-start_script" - error_message = "Start script name output should be correctly formatted" - } -} - -# Test with only install_script (no start) -run "test_install_only" { - command = plan - - variables { - agent_id = "test-agent-id" - agent_name = "test-agent" - module_dir_name = ".test-module" - install_script = "echo 'install'" - } - - assert { - condition = length(coder_script.install_script) == 1 - error_message = "Install script should be created when provided" + condition = output.pre_install_script_name == "" + error_message = "Pre-install script name output should be empty" } assert { - condition = length(coder_script.start_script) == 0 - error_message = "Start script should not be created when not provided" + condition = output.install_script_name == "test-agent-install_script" + error_message = "Install script name output should be correctly formatted" } assert { - condition = output.install_script_name == "test-agent-install_script" - error_message = "Install script name output should be correctly formatted" + condition = output.post_install_script_name == "" + error_message = "Post-install script name output should be empty" } assert { - condition = output.start_script_name == "" - error_message = "Start script name output should be empty" + condition = output.start_script_name == "test-agent-start_script" + error_message = "Start script name output should be correctly formatted" } } @@ -321,7 +246,6 @@ run "test_with_mock_data" { start_script = "echo 'start'" } - # Mock the data sources for testing override_data { target = data.coder_workspace.me values = { @@ -354,9 +278,8 @@ run "test_with_mock_data" { } } - # Verify scripts are created with mocked data assert { - condition = coder_script.install_script[0].agent_id == "mock-agent" + condition = coder_script.install_script.agent_id == "mock-agent" error_message = "Install script should use the mocked agent ID" } @@ -378,10 +301,8 @@ run "test_script_naming" { start_script = "echo 'start'" } - # Verify script names are constructed correctly - # The script should contain references to custom-name-* in the sync commands assert { - condition = can(regex("custom-name-install_script", coder_script.install_script[0].script)) + condition = can(regex("custom-name-install_script", coder_script.install_script.script)) error_message = "Install script should use custom agent_name in sync commands" } @@ -390,7 +311,6 @@ run "test_script_naming" { error_message = "Start script should use custom agent_name in sync commands" } - # Verify outputs use custom agent_name assert { condition = output.pre_install_script_name == "" error_message = "Pre-install script name output should be empty when not provided" @@ -412,58 +332,28 @@ run "test_script_naming" { } } -# Test with post_install but no install (sync deps should fall back to pre_install) -run "test_post_install_without_install" { +# Test start script sync deps with post_install present +run "test_start_syncs_with_post_install" { command = plan variables { agent_id = "test-agent-id" agent_name = "test-agent" module_dir_name = ".test-module" - pre_install_script = "echo 'pre-install'" + 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 = length(coder_script.pre_install_script) == 1 - error_message = "Pre-install script should be created" - } - - assert { - condition = length(coder_script.install_script) == 0 - error_message = "Install script should not be created" - } - - assert { - condition = length(coder_script.post_install_script) == 1 - error_message = "Post-install script should be created" - } - - assert { - condition = length(coder_script.start_script) == 1 - error_message = "Start script should be created" - } - - # post_install should sync-want pre_install (since install is absent) - assert { - condition = can(regex("sync want test-agent-post_install_script test-agent-pre_install_script", coder_script.post_install_script[0].script)) - error_message = "Post-install script should sync-want pre_install_script when install_script is absent" - } - - # start should sync-want post_install (since install is absent) - assert { - condition = can(regex("sync want test-agent-start_script test-agent-post_install_script", coder_script.start_script[0].script)) - error_message = "Start script should sync-want post_install_script when install_script is absent" - } - - assert { - condition = output.pre_install_script_name == "test-agent-pre_install_script" - error_message = "Pre-install script name output should be set" + condition = can(regex("sync want test-agent-start_script test-agent-install_script test-agent-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 = output.install_script_name == "" - error_message = "Install script name output should be empty" + condition = can(regex("sync want test-agent-post_install_script test-agent-install_script", coder_script.post_install_script[0].script)) + error_message = "Post-install script should sync-want install_script" } } From 8a9ab447d12e108d27840027cc89ef14bfbfa39e Mon Sep 17 00:00:00 2001 From: Jay Kumar Date: Mon, 13 Apr 2026 17:55:58 +0000 Subject: [PATCH 06/17] refactor(registry/coder/modules/coder-utils): move install sync deps to locals --- registry/coder/modules/coder-utils/main.tf | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/registry/coder/modules/coder-utils/main.tf b/registry/coder/modules/coder-utils/main.tf index 7d7d5c7bf..d89bd99cb 100644 --- a/registry/coder/modules/coder-utils/main.tf +++ b/registry/coder/modules/coder-utils/main.tf @@ -77,9 +77,8 @@ locals { post_install_log_path = "${local.module_dir_path}/post_install.log" start_log_path = "${local.module_dir_path}/start.log" - # Sync dependency resolution: each stage waits for its predecessors. - # Chain order: pre_install -> install -> post_install -> start - # install_script is always present (required), so it anchors the chain. + install_sync_deps = var.pre_install_script != null ? local.pre_install_script_name : null + start_sync_deps = ( var.post_install_script != null ? "${local.install_script_name} ${local.post_install_script_name}" @@ -121,8 +120,8 @@ resource "coder_script" "install_script" { 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} + %{if local.install_sync_deps != null~} + coder exp sync want ${local.install_script_name} ${local.install_sync_deps} %{endif~} coder exp sync start ${local.install_script_name} echo -n '${local.encoded_install_script}' | base64 -d > ${local.install_path} From 0d51427c131738544abe16c8075454c18a24d246 Mon Sep 17 00:00:00 2001 From: Jay Kumar Date: Mon, 13 Apr 2026 17:59:39 +0000 Subject: [PATCH 07/17] test(registry/coder/modules/coder-utils): add install sync deps test and update README Add test_install_syncs_with_pre_install to verify install waits for pre_install via install_sync_deps local. Add sync assertion to test_with_all_scripts. Update README execution order to reflect install_script is required. --- registry/coder/modules/coder-utils/README.md | 9 +++-- .../coder/modules/coder-utils/main.tftest.hcl | 34 +++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/registry/coder/modules/coder-utils/README.md b/registry/coder/modules/coder-utils/README.md index 976e00463..073d00c13 100644 --- a/registry/coder/modules/coder-utils/README.md +++ b/registry/coder/modules/coder-utils/README.md @@ -56,10 +56,9 @@ module "coder_utils" { The module orchestrates scripts in the following order: -1. **Log File Creation** - Creates module directory and log files -2. **Pre-Install Script** (optional) - Runs before installation -3. **Install Script** (optional) - Main installation -4. **Post-Install Script** (optional) - Runs after installation -5. **Start Script** (optional) - Starts the application +1. **Pre-Install Script** (optional) - Runs before installation +2. **Install Script** (required) - Main installation +3. **Post-Install Script** (optional) - Runs after installation +4. **Start Script** (optional) - Starts the application Each script waits for its prerequisites to complete before running using `coder exp sync` dependency management. diff --git a/registry/coder/modules/coder-utils/main.tftest.hcl b/registry/coder/modules/coder-utils/main.tftest.hcl index 9968a3f1c..6c8ab70bd 100644 --- a/registry/coder/modules/coder-utils/main.tftest.hcl +++ b/registry/coder/modules/coder-utils/main.tftest.hcl @@ -51,6 +51,12 @@ run "test_with_all_scripts" { error_message = "Install script should run on start" } + # install should sync-want pre_install + assert { + condition = can(regex("sync want test-agent-install_script test-agent-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 @@ -332,6 +338,34 @@ run "test_script_naming" { } } +# Test install syncs with pre_install when provided +run "test_install_syncs_with_pre_install" { + command = plan + + variables { + agent_id = "test-agent-id" + agent_name = "test-agent" + module_dir_name = ".test-module" + 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-agent-install_script test-agent-pre_install_script", coder_script.install_script.script)) + error_message = "Install script should sync-want pre_install_script" + } + + assert { + condition = output.pre_install_script_name == "test-agent-pre_install_script" + error_message = "Pre-install script name output should be set" + } +} + # Test start script sync deps with post_install present run "test_start_syncs_with_post_install" { command = plan From e942c7cfb3b55c161d8974da144dc9c250d0849c Mon Sep 17 00:00:00 2001 From: Jay Kumar Date: Mon, 13 Apr 2026 18:01:17 +0000 Subject: [PATCH 08/17] fix(registry/coder/modules/coder-utils): remove redundant mkdir from post_install and start Both scripts sync-wait on install, which always creates the directory. --- registry/coder/modules/coder-utils/main.tf | 4 ---- 1 file changed, 4 deletions(-) diff --git a/registry/coder/modules/coder-utils/main.tf b/registry/coder/modules/coder-utils/main.tf index d89bd99cb..2072ae703 100644 --- a/registry/coder/modules/coder-utils/main.tf +++ b/registry/coder/modules/coder-utils/main.tf @@ -141,8 +141,6 @@ resource "coder_script" "post_install_script" { 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} @@ -164,8 +162,6 @@ resource "coder_script" "start_script" { set -o errexit set -o pipefail - mkdir -p ${local.module_dir_path} - trap 'coder exp sync complete ${local.start_script_name}' EXIT coder exp sync want ${local.start_script_name} ${local.start_sync_deps} From e0a467066a694c4bd03c63579b9043236be6955f Mon Sep 17 00:00:00 2001 From: 35C4n0r Date: Tue, 14 Apr 2026 15:07:48 +0530 Subject: [PATCH 09/17] refactor(coder/modules/coder-utils): rename module_dir_name to module_directory for clarity --- registry/coder/modules/coder-utils/main.tf | 26 +++++------ .../coder/modules/coder-utils/main.tftest.hcl | 44 +++++++++---------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/registry/coder/modules/coder-utils/main.tf b/registry/coder/modules/coder-utils/main.tf index 2072ae703..6dba2b3a6 100644 --- a/registry/coder/modules/coder-utils/main.tf +++ b/registry/coder/modules/coder-utils/main.tf @@ -49,9 +49,9 @@ variable "agent_name" { } -variable "module_dir_name" { +variable "module_directory" { type = string - description = "The name of the module directory." + description = "The module's working directory for scripts and logs." } locals { @@ -65,17 +65,15 @@ locals { post_install_script_name = "${var.agent_name}-post_install_script" start_script_name = "${var.agent_name}-start_script" - module_dir_path = "$HOME/.coder-modules/coder/coder-utils/${var.module_dir_name}" + pre_install_path = "${var.module_directory}/pre_install.sh" + install_path = "${var.module_directory}/install.sh" + post_install_path = "${var.module_directory}/post_install.sh" + start_path = "${var.module_directory}/start.sh" - pre_install_path = "${local.module_dir_path}/pre_install.sh" - install_path = "${local.module_dir_path}/install.sh" - post_install_path = "${local.module_dir_path}/post_install.sh" - start_path = "${local.module_dir_path}/start.sh" - - pre_install_log_path = "${local.module_dir_path}/pre_install.log" - install_log_path = "${local.module_dir_path}/install.log" - post_install_log_path = "${local.module_dir_path}/post_install.log" - start_log_path = "${local.module_dir_path}/start.log" + pre_install_log_path = "${var.module_directory}/pre_install.log" + install_log_path = "${var.module_directory}/install.log" + post_install_log_path = "${var.module_directory}/post_install.log" + start_log_path = "${var.module_directory}/start.log" install_sync_deps = var.pre_install_script != null ? local.pre_install_script_name : null @@ -96,7 +94,7 @@ resource "coder_script" "pre_install_script" { set -o errexit set -o pipefail - mkdir -p ${local.module_dir_path} + mkdir -p ${var.module_directory} trap 'coder exp sync complete ${local.pre_install_script_name}' EXIT coder exp sync start ${local.pre_install_script_name} @@ -117,7 +115,7 @@ resource "coder_script" "install_script" { set -o errexit set -o pipefail - mkdir -p ${local.module_dir_path} + mkdir -p ${var.module_directory} trap 'coder exp sync complete ${local.install_script_name}' EXIT %{if local.install_sync_deps != null~} diff --git a/registry/coder/modules/coder-utils/main.tftest.hcl b/registry/coder/modules/coder-utils/main.tftest.hcl index 6c8ab70bd..cb5ad9d04 100644 --- a/registry/coder/modules/coder-utils/main.tftest.hcl +++ b/registry/coder/modules/coder-utils/main.tftest.hcl @@ -7,7 +7,7 @@ run "test_with_all_scripts" { variables { agent_id = "test-agent-id" agent_name = "test-agent" - module_dir_name = ".test-module" + module_directory = ".test-module" pre_install_script = "echo 'pre-install'" install_script = "echo 'install'" post_install_script = "echo 'post-install'" @@ -126,10 +126,10 @@ run "test_install_only" { command = plan variables { - agent_id = "test-agent-id" - agent_name = "test-agent" - module_dir_name = ".test-module" - install_script = "echo 'install'" + agent_id = "test-agent-id" + agent_name = "test-agent" + module_directory = ".test-module" + install_script = "echo 'install'" } # Verify optional scripts are NOT created @@ -181,11 +181,11 @@ run "test_install_and_start" { command = plan variables { - agent_id = "test-agent-id" - agent_name = "test-agent" - module_dir_name = ".test-module" - install_script = "echo 'install'" - start_script = "echo 'start'" + agent_id = "test-agent-id" + agent_name = "test-agent" + module_directory = ".test-module" + install_script = "echo 'install'" + start_script = "echo 'start'" } assert { @@ -245,11 +245,11 @@ run "test_with_mock_data" { command = plan variables { - agent_id = "mock-agent" - agent_name = "mock-agent" - module_dir_name = ".mock-module" - install_script = "echo 'install'" - start_script = "echo 'start'" + agent_id = "mock-agent" + agent_name = "mock-agent" + module_directory = ".mock-module" + install_script = "echo 'install'" + start_script = "echo 'start'" } override_data { @@ -300,11 +300,11 @@ run "test_script_naming" { command = plan variables { - agent_id = "test-agent" - agent_name = "custom-name" - module_dir_name = ".test-module" - install_script = "echo 'install'" - start_script = "echo 'start'" + agent_id = "test-agent" + agent_name = "custom-name" + module_directory = ".test-module" + install_script = "echo 'install'" + start_script = "echo 'start'" } assert { @@ -345,7 +345,7 @@ run "test_install_syncs_with_pre_install" { variables { agent_id = "test-agent-id" agent_name = "test-agent" - module_dir_name = ".test-module" + module_directory = ".test-module" pre_install_script = "echo 'pre-install'" install_script = "echo 'install'" } @@ -373,7 +373,7 @@ run "test_start_syncs_with_post_install" { variables { agent_id = "test-agent-id" agent_name = "test-agent" - module_dir_name = ".test-module" + module_directory = ".test-module" install_script = "echo 'install'" post_install_script = "echo 'post-install'" start_script = "echo 'start'" From 22966c56db1f7aec700547a7d938ca35638ac76b Mon Sep 17 00:00:00 2001 From: 35C4n0r Date: Tue, 14 Apr 2026 15:08:16 +0530 Subject: [PATCH 10/17] fix(terraform): update required_version to support Terraform 1.0 and above --- registry/coder/modules/coder-utils/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder/modules/coder-utils/main.tf b/registry/coder/modules/coder-utils/main.tf index 6dba2b3a6..447f35efb 100644 --- a/registry/coder/modules/coder-utils/main.tf +++ b/registry/coder/modules/coder-utils/main.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 1.9" + required_version = ">= 1.0" required_providers { coder = { From 8216a7177bcbc06bb0ef4818ec0077aecb771c8c Mon Sep 17 00:00:00 2001 From: 35C4n0r Date: Tue, 14 Apr 2026 15:18:44 +0530 Subject: [PATCH 11/17] chore: update test --- registry/coder/modules/coder-utils/main.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder/modules/coder-utils/main.test.ts b/registry/coder/modules/coder-utils/main.test.ts index 42456fa7d..a7e8e2e9f 100644 --- a/registry/coder/modules/coder-utils/main.test.ts +++ b/registry/coder/modules/coder-utils/main.test.ts @@ -7,7 +7,7 @@ describe("coder-utils", async () => { testRequiredVariables(import.meta.dir, { agent_id: "test-agent-id", agent_name: "test-agent", - module_dir_name: ".test-module", + module_directory: ".test-module", install_script: "echo 'install'", }); }); From 94f1d3ad66f3abf7426fc52e23f011cb0d71b376 Mon Sep 17 00:00:00 2001 From: Jay Kumar Date: Tue, 14 Apr 2026 10:43:59 +0000 Subject: [PATCH 12/17] refactor(registry/coder/modules/coder-utils): consolidate outputs into single script_names object Replace four individual outputs with a single script_names output containing pre_install, install, post_install, and start fields. --- registry/coder/modules/coder-utils/main.tf | 26 +++++--------- .../coder/modules/coder-utils/main.tftest.hcl | 34 +++++++++---------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/registry/coder/modules/coder-utils/main.tf b/registry/coder/modules/coder-utils/main.tf index 447f35efb..20b383d14 100644 --- a/registry/coder/modules/coder-utils/main.tf +++ b/registry/coder/modules/coder-utils/main.tf @@ -172,22 +172,12 @@ resource "coder_script" "start_script" { EOT } -output "pre_install_script_name" { - description = "The name of the pre-install script for sync." - value = var.pre_install_script != null ? 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 = var.post_install_script != null ? local.post_install_script_name : "" -} - -output "start_script_name" { - description = "The name of the start script for sync." - value = var.start_script != null ? local.start_script_name : "" +output "script_names" { + description = "The names of the scripts for sync." + value = { + pre_install = var.pre_install_script != null ? local.pre_install_script_name : "" + install = local.install_script_name + post_install = var.post_install_script != null ? local.post_install_script_name : "" + start = var.start_script != null ? local.start_script_name : "" + } } \ No newline at end of file diff --git a/registry/coder/modules/coder-utils/main.tftest.hcl b/registry/coder/modules/coder-utils/main.tftest.hcl index cb5ad9d04..0e3aa9ebb 100644 --- a/registry/coder/modules/coder-utils/main.tftest.hcl +++ b/registry/coder/modules/coder-utils/main.tftest.hcl @@ -101,22 +101,22 @@ run "test_with_all_scripts" { # Verify outputs for script names assert { - condition = output.pre_install_script_name == "test-agent-pre_install_script" + condition = output.script_names.pre_install == "test-agent-pre_install_script" error_message = "Pre-install script name output should be correctly formatted" } assert { - condition = output.install_script_name == "test-agent-install_script" + condition = output.script_names.install == "test-agent-install_script" error_message = "Install script name output should be correctly formatted" } assert { - condition = output.post_install_script_name == "test-agent-post_install_script" + condition = output.script_names.post_install == "test-agent-post_install_script" error_message = "Post-install script name output should be correctly formatted" } assert { - condition = output.start_script_name == "test-agent-start_script" + condition = output.script_names.start == "test-agent-start_script" error_message = "Start script name output should be correctly formatted" } } @@ -156,22 +156,22 @@ run "test_install_only" { # Verify outputs assert { - condition = output.pre_install_script_name == "" + condition = output.script_names.pre_install == "" error_message = "Pre-install script name output should be empty" } assert { - condition = output.install_script_name == "test-agent-install_script" + condition = output.script_names.install == "test-agent-install_script" error_message = "Install script name output should be correctly formatted" } assert { - condition = output.post_install_script_name == "" + condition = output.script_names.post_install == "" error_message = "Post-install script name output should be empty" } assert { - condition = output.start_script_name == "" + condition = output.script_names.start == "" error_message = "Start script name output should be empty" } } @@ -220,22 +220,22 @@ run "test_install_and_start" { } assert { - condition = output.pre_install_script_name == "" + condition = output.script_names.pre_install == "" error_message = "Pre-install script name output should be empty" } assert { - condition = output.install_script_name == "test-agent-install_script" + condition = output.script_names.install == "test-agent-install_script" error_message = "Install script name output should be correctly formatted" } assert { - condition = output.post_install_script_name == "" + condition = output.script_names.post_install == "" error_message = "Post-install script name output should be empty" } assert { - condition = output.start_script_name == "test-agent-start_script" + condition = output.script_names.start == "test-agent-start_script" error_message = "Start script name output should be correctly formatted" } } @@ -318,22 +318,22 @@ run "test_script_naming" { } assert { - condition = output.pre_install_script_name == "" + condition = output.script_names.pre_install == "" error_message = "Pre-install script name output should be empty when not provided" } assert { - condition = output.install_script_name == "custom-name-install_script" + condition = output.script_names.install == "custom-name-install_script" error_message = "Install script name output should use custom agent_name" } assert { - condition = output.post_install_script_name == "" + condition = output.script_names.post_install == "" error_message = "Post-install script name output should be empty when not provided" } assert { - condition = output.start_script_name == "custom-name-start_script" + condition = output.script_names.start == "custom-name-start_script" error_message = "Start script name output should use custom agent_name" } } @@ -361,7 +361,7 @@ run "test_install_syncs_with_pre_install" { } assert { - condition = output.pre_install_script_name == "test-agent-pre_install_script" + condition = output.script_names.pre_install == "test-agent-pre_install_script" error_message = "Pre-install script name output should be set" } } From 6678b04b993fa505740838f55252cce4a9c8bfe3 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Wed, 22 Apr 2026 07:06:04 +0000 Subject: [PATCH 13/17] feat(coder-utils): add display_name_prefix and icon variables Downstream modules (e.g. claude-code) can now brand the four scripts this module emits so the Coder UI shows 'Claude Code: Install Script' with the Claude icon instead of the generic defaults. - display_name_prefix (string, default "") prepends ': ' to every coder_script display_name. - icon (string, default null) sets icon on every coder_script. - Drive-by: fix stale 'module_dir_name' reference in README; the var was renamed to 'module_directory' earlier on this branch. --- registry/coder/modules/coder-utils/README.md | 27 ++++++- registry/coder/modules/coder-utils/main.tf | 26 +++++-- .../coder/modules/coder-utils/main.tftest.hcl | 72 +++++++++++++++++++ 3 files changed, 118 insertions(+), 7 deletions(-) diff --git a/registry/coder/modules/coder-utils/README.md b/registry/coder/modules/coder-utils/README.md index 073d00c13..07eb223a6 100644 --- a/registry/coder/modules/coder-utils/README.md +++ b/registry/coder/modules/coder-utils/README.md @@ -22,9 +22,9 @@ module "coder_utils" { source = "registry.coder.com/coder/coder-utils/coder" version = "1.1.0" - agent_id = coder_agent.main.id - agent_name = "myagent" - module_dir_name = ".my-module" + agent_id = coder_agent.main.id + agent_name = "myagent" + module_directory = ".my-module" pre_install_script = <<-EOT #!/bin/bash @@ -62,3 +62,24 @@ The module orchestrates scripts in the following order: 4. **Start Script** (optional) - Starts the application Each script waits for its prerequisites to complete before running using `coder exp sync` dependency management. + +## Customizing Script Display + +By default each `coder_script` renders in the Coder UI as plain "Install Script", "Pre-Install Script", etc. Downstream modules can brand them: + +```tf +module "coder_utils" { + source = "registry.coder.com/coder/coder-utils/coder" + version = "1.1.0" + + agent_id = coder_agent.main.id + agent_name = "myagent" + module_directory = ".my-module" + install_script = "echo installing" + + display_name_prefix = "Claude Code" # yields "Claude Code: Install Script", etc. + icon = "/icon/claude.svg" +} +``` + +Both variables are optional. `display_name_prefix` defaults to `""` (no prefix), and `icon` defaults to `null` (use the Coder provider's default). diff --git a/registry/coder/modules/coder-utils/main.tf b/registry/coder/modules/coder-utils/main.tf index 20b383d14..359294658 100644 --- a/registry/coder/modules/coder-utils/main.tf +++ b/registry/coder/modules/coder-utils/main.tf @@ -54,6 +54,18 @@ variable "module_directory" { description = "The module's working directory for scripts and logs." } +variable "display_name_prefix" { + type = string + description = "Prefix for each coder_script display_name. Example: setting 'Claude Code' yields 'Claude Code: Install Script', 'Claude Code: Pre-Install Script', etc. When unset, scripts show as plain 'Install Script'." + default = "" +} + +variable "icon" { + type = string + description = "Icon shown in the Coder UI for every coder_script this module creates. Falls back to the Coder provider's default when unset." + default = null +} + locals { encoded_pre_install_script = var.pre_install_script != null ? base64encode(var.pre_install_script) : "" encoded_install_script = base64encode(var.install_script) @@ -82,12 +94,15 @@ locals { ? "${local.install_script_name} ${local.post_install_script_name}" : local.install_script_name ) + + display_name_prefix = var.display_name_prefix != "" ? "${var.display_name_prefix}: " : "" } resource "coder_script" "pre_install_script" { count = var.pre_install_script == null ? 0 : 1 agent_id = var.agent_id - display_name = "Pre-Install Script" + display_name = "${local.display_name_prefix}Pre-Install Script" + icon = var.icon run_on_start = true script = <<-EOT #!/bin/bash @@ -108,7 +123,8 @@ resource "coder_script" "pre_install_script" { resource "coder_script" "install_script" { agent_id = var.agent_id - display_name = "Install Script" + display_name = "${local.display_name_prefix}Install Script" + icon = var.icon run_on_start = true script = <<-EOT #!/bin/bash @@ -132,7 +148,8 @@ resource "coder_script" "install_script" { resource "coder_script" "post_install_script" { count = var.post_install_script != null ? 1 : 0 agent_id = var.agent_id - display_name = "Post-Install Script" + display_name = "${local.display_name_prefix}Post-Install Script" + icon = var.icon run_on_start = true script = <<-EOT #!/bin/bash @@ -153,7 +170,8 @@ resource "coder_script" "post_install_script" { resource "coder_script" "start_script" { count = var.start_script != null ? 1 : 0 agent_id = var.agent_id - display_name = "Start Script" + display_name = "${local.display_name_prefix}Start Script" + icon = var.icon run_on_start = true script = <<-EOT #!/bin/bash diff --git a/registry/coder/modules/coder-utils/main.tftest.hcl b/registry/coder/modules/coder-utils/main.tftest.hcl index 0e3aa9ebb..9079b8658 100644 --- a/registry/coder/modules/coder-utils/main.tftest.hcl +++ b/registry/coder/modules/coder-utils/main.tftest.hcl @@ -391,3 +391,75 @@ run "test_start_syncs_with_post_install" { 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" + agent_name = "test-agent" + module_directory = ".test-module" + 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" + agent_name = "test-agent" + module_directory = ".test-module" + 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" + } +} From dda6063de32db178889d25c322a180ab867d3ef1 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Wed, 22 Apr 2026 08:36:51 +0000 Subject: [PATCH 14/17] test(coder-utils): assert optional scripts absent when unset Close a coverage gap: existing tests only set every script variable and verify presence. Nothing checked that pre-install, post-install, and start coder_script resources stay absent when their inputs are unset. Downstream consumers (e.g. claude-code without pre/post scripts) rely on this. --- .../coder/modules/coder-utils/main.tftest.hcl | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/registry/coder/modules/coder-utils/main.tftest.hcl b/registry/coder/modules/coder-utils/main.tftest.hcl index 9079b8658..29ef812e6 100644 --- a/registry/coder/modules/coder-utils/main.tftest.hcl +++ b/registry/coder/modules/coder-utils/main.tftest.hcl @@ -463,3 +463,30 @@ run "test_icon_applied" { 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" + agent_name = "test-agent" + module_directory = ".test-module" + 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" + } +} From 178c6090841f69c09a3922e56550e39b606fd7dd Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Wed, 22 Apr 2026 12:37:40 +0000 Subject: [PATCH 15/17] feat(coder-utils): add scripts output with filtered run-order list Expose a new `output "scripts"` that returns a run-ordered list of the `coder exp sync` names this module actually emits. Absent scripts (pre_install, post_install, or start when their inputs are null) are omitted from the list entirely, not padded with empty strings. This lets downstream modules consume the list directly with `coder exp sync want ` to serialize behind the install pipeline, instead of re-deriving the filter from `script_names` on every consumer. Adds 3 tftest cases covering all-scripts-present, install-only, and install+post configurations. --- registry/coder/modules/coder-utils/main.tf | 18 +++- .../coder/modules/coder-utils/main.tftest.hcl | 88 +++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/registry/coder/modules/coder-utils/main.tf b/registry/coder/modules/coder-utils/main.tf index 359294658..9384b56c8 100644 --- a/registry/coder/modules/coder-utils/main.tf +++ b/registry/coder/modules/coder-utils/main.tf @@ -198,4 +198,20 @@ output "script_names" { post_install = var.post_install_script != null ? local.post_install_script_name : "" start = var.start_script != null ? local.start_script_name : "" } -} \ No newline at end of file +} + +# Filtered, run-order list of the `coder exp sync` names for every +# coder_script this module actually creates. Absent scripts (pre/post/start +# when their inputs are null) are omitted entirely, not padded with empty +# strings. Downstream modules can use this with +# `coder exp sync want ` to serialize their own +# scripts behind the install pipeline. +output "scripts" { + description = "Ordered list of `coder exp sync` names for the coder_script resources this module creates, in the run order it enforces (pre_install, install, post_install, start). Scripts that were not configured are absent from the list." + value = concat( + var.pre_install_script != null ? [local.pre_install_script_name] : [], + [local.install_script_name], + var.post_install_script != null ? [local.post_install_script_name] : [], + var.start_script != null ? [local.start_script_name] : [], + ) +} diff --git a/registry/coder/modules/coder-utils/main.tftest.hcl b/registry/coder/modules/coder-utils/main.tftest.hcl index 29ef812e6..1a43f0a4d 100644 --- a/registry/coder/modules/coder-utils/main.tftest.hcl +++ b/registry/coder/modules/coder-utils/main.tftest.hcl @@ -490,3 +490,91 @@ run "test_optional_scripts_absent_by_default" { 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" + agent_name = "test-agent" + module_directory = ".test-module" + 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-agent-pre_install_script" + error_message = "scripts[0] must be the pre-install name" + } + + assert { + condition = output.scripts[1] == "test-agent-install_script" + error_message = "scripts[1] must be the install name" + } + + assert { + condition = output.scripts[2] == "test-agent-post_install_script" + error_message = "scripts[2] must be the post-install name" + } + + assert { + condition = output.scripts[3] == "test-agent-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" + agent_name = "test-agent" + module_directory = ".test-module" + 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-agent-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" + agent_name = "test-agent" + module_directory = ".test-module" + 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-agent-install_script" + error_message = "scripts[0] must be the install name" + } + + assert { + condition = output.scripts[1] == "test-agent-post_install_script" + error_message = "scripts[1] must be the post-install name" + } +} From af2931e6a641ab942b271d6db344773d8febdb16 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Wed, 22 Apr 2026 12:53:41 +0000 Subject: [PATCH 16/17] fix(coder-utils): tee script output to log file and stdout Each coder_script wrapper used to run the user's script with ${script_path} > ${log_path} 2>&1 which redirects combined output straight to the log file. Workspace users watching the coder_script output in the Coder UI during workspace start would see the script's display name but nothing else until the script finished, because every line the script emitted went to the file on disk and never to the agent's stdout. Switch to ${script_path} 2>&1 | tee ${log_path} so combined stdout and stderr fans out to both the log file (for after-the-fact inspection and debugging) and the agent's stdout (for live progress in the UI). pipefail is already set, so a failing inner script still propagates its exit code through the pipeline, the errexit trap still fires, and coder exp sync complete still runs. Applied uniformly to pre_install, install, post_install, and start. Adds a regex-based tftest that fails if any of the four wrappers stops teeing in the future. --- registry/coder/modules/coder-utils/main.tf | 8 ++-- .../coder/modules/coder-utils/main.tftest.hcl | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/registry/coder/modules/coder-utils/main.tf b/registry/coder/modules/coder-utils/main.tf index 9384b56c8..778d05be2 100644 --- a/registry/coder/modules/coder-utils/main.tf +++ b/registry/coder/modules/coder-utils/main.tf @@ -117,7 +117,7 @@ resource "coder_script" "pre_install_script" { echo -n '${local.encoded_pre_install_script}' | base64 -d > ${local.pre_install_path} chmod +x ${local.pre_install_path} - ${local.pre_install_path} > ${local.pre_install_log_path} 2>&1 + ${local.pre_install_path} 2>&1 | tee ${local.pre_install_log_path} EOT } @@ -141,7 +141,7 @@ resource "coder_script" "install_script" { echo -n '${local.encoded_install_script}' | base64 -d > ${local.install_path} chmod +x ${local.install_path} - ${local.install_path} > ${local.install_log_path} 2>&1 + ${local.install_path} 2>&1 | tee ${local.install_log_path} EOT } @@ -163,7 +163,7 @@ resource "coder_script" "post_install_script" { echo -n '${local.encoded_post_install_script}' | base64 -d > ${local.post_install_path} chmod +x ${local.post_install_path} - ${local.post_install_path} > ${local.post_install_log_path} 2>&1 + ${local.post_install_path} 2>&1 | tee ${local.post_install_log_path} EOT } @@ -186,7 +186,7 @@ resource "coder_script" "start_script" { echo -n '${local.encoded_start_script}' | base64 -d > ${local.start_path} chmod +x ${local.start_path} - ${local.start_path} > ${local.start_log_path} 2>&1 + ${local.start_path} 2>&1 | tee ${local.start_log_path} EOT } diff --git a/registry/coder/modules/coder-utils/main.tftest.hcl b/registry/coder/modules/coder-utils/main.tftest.hcl index 1a43f0a4d..aa5a41021 100644 --- a/registry/coder/modules/coder-utils/main.tftest.hcl +++ b/registry/coder/modules/coder-utils/main.tftest.hcl @@ -578,3 +578,41 @@ run "test_scripts_output_with_install_and_post" { 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" + agent_name = "test-agent" + module_directory = ".test-module" + 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 .*pre_install.log", coder_script.pre_install_script[0].script)) + error_message = "pre_install wrapper must tee combined output to the log file and stdout" + } + + assert { + condition = can(regex("install.sh 2>&1 \\| tee .*install.log", coder_script.install_script.script)) + error_message = "install wrapper must tee combined output to the log file and stdout" + } + + assert { + condition = can(regex("post_install.sh 2>&1 \\| tee .*post_install.log", coder_script.post_install_script[0].script)) + error_message = "post_install wrapper must tee combined output to the log file and stdout" + } + + assert { + condition = can(regex("start.sh 2>&1 \\| tee .*start.log", coder_script.start_script[0].script)) + error_message = "start wrapper must tee combined output to the log file and stdout" + } +} From ff51daeed884d0674f481997f7e7722431573b86 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Wed, 22 Apr 2026 17:36:10 +0000 Subject: [PATCH 17/17] refactor(coder-utils): drop script_names output The scripts output covers every use case (ordered list of the sync names this module actually creates). script_names duplicated that information as a map with empty-string placeholders for unconfigured scripts, which downstream modules never relied on. Removing it keeps the surface minimal. --- registry/coder/modules/coder-utils/main.tf | 10 --- .../coder/modules/coder-utils/main.tftest.hcl | 87 ------------------- 2 files changed, 97 deletions(-) diff --git a/registry/coder/modules/coder-utils/main.tf b/registry/coder/modules/coder-utils/main.tf index 778d05be2..126496fb6 100644 --- a/registry/coder/modules/coder-utils/main.tf +++ b/registry/coder/modules/coder-utils/main.tf @@ -190,16 +190,6 @@ resource "coder_script" "start_script" { EOT } -output "script_names" { - description = "The names of the scripts for sync." - value = { - pre_install = var.pre_install_script != null ? local.pre_install_script_name : "" - install = local.install_script_name - post_install = var.post_install_script != null ? local.post_install_script_name : "" - start = var.start_script != null ? local.start_script_name : "" - } -} - # Filtered, run-order list of the `coder exp sync` names for every # coder_script this module actually creates. Absent scripts (pre/post/start # when their inputs are null) are omitted entirely, not padded with empty diff --git a/registry/coder/modules/coder-utils/main.tftest.hcl b/registry/coder/modules/coder-utils/main.tftest.hcl index aa5a41021..40aeeb41f 100644 --- a/registry/coder/modules/coder-utils/main.tftest.hcl +++ b/registry/coder/modules/coder-utils/main.tftest.hcl @@ -98,27 +98,6 @@ run "test_with_all_scripts" { condition = coder_script.start_script[0].run_on_start == true error_message = "Start script should run on start" } - - # Verify outputs for script names - assert { - condition = output.script_names.pre_install == "test-agent-pre_install_script" - error_message = "Pre-install script name output should be correctly formatted" - } - - assert { - condition = output.script_names.install == "test-agent-install_script" - error_message = "Install script name output should be correctly formatted" - } - - assert { - condition = output.script_names.post_install == "test-agent-post_install_script" - error_message = "Post-install script name output should be correctly formatted" - } - - assert { - condition = output.script_names.start == "test-agent-start_script" - error_message = "Start script name output should be correctly formatted" - } } # Test with only install_script (minimum required input) @@ -153,27 +132,6 @@ run "test_install_only" { condition = coder_script.install_script.agent_id == "test-agent-id" error_message = "Install script should be created" } - - # Verify outputs - assert { - condition = output.script_names.pre_install == "" - error_message = "Pre-install script name output should be empty" - } - - assert { - condition = output.script_names.install == "test-agent-install_script" - error_message = "Install script name output should be correctly formatted" - } - - assert { - condition = output.script_names.post_install == "" - error_message = "Post-install script name output should be empty" - } - - assert { - condition = output.script_names.start == "" - error_message = "Start script name output should be empty" - } } # Test with install and start scripts (no pre/post install) @@ -218,26 +176,6 @@ run "test_install_and_start" { condition = can(regex("sync want test-agent-start_script test-agent-install_script", coder_script.start_script[0].script)) error_message = "Start script should sync-want install_script" } - - assert { - condition = output.script_names.pre_install == "" - error_message = "Pre-install script name output should be empty" - } - - assert { - condition = output.script_names.install == "test-agent-install_script" - error_message = "Install script name output should be correctly formatted" - } - - assert { - condition = output.script_names.post_install == "" - error_message = "Post-install script name output should be empty" - } - - assert { - condition = output.script_names.start == "test-agent-start_script" - error_message = "Start script name output should be correctly formatted" - } } # Test with mock data sources @@ -316,26 +254,6 @@ run "test_script_naming" { condition = can(regex("custom-name-start_script", coder_script.start_script[0].script)) error_message = "Start script should use custom agent_name in sync commands" } - - assert { - condition = output.script_names.pre_install == "" - error_message = "Pre-install script name output should be empty when not provided" - } - - assert { - condition = output.script_names.install == "custom-name-install_script" - error_message = "Install script name output should use custom agent_name" - } - - assert { - condition = output.script_names.post_install == "" - error_message = "Post-install script name output should be empty when not provided" - } - - assert { - condition = output.script_names.start == "custom-name-start_script" - error_message = "Start script name output should use custom agent_name" - } } # Test install syncs with pre_install when provided @@ -359,11 +277,6 @@ run "test_install_syncs_with_pre_install" { condition = can(regex("sync want test-agent-install_script test-agent-pre_install_script", coder_script.install_script.script)) error_message = "Install script should sync-want pre_install_script" } - - assert { - condition = output.script_names.pre_install == "test-agent-pre_install_script" - error_message = "Pre-install script name output should be set" - } } # Test start script sync deps with post_install present