feat(registry/coder/modules/coder-utils): make install_script and start_script optional#842
feat(registry/coder/modules/coder-utils): make install_script and start_script optional#842
Conversation
…rt_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.
There was a problem hiding this comment.
Pull request overview
Makes install_script and start_script optional in the registry/coder/modules/coder-utils Terraform module, aligning behavior with the existing optional pre/post install scripts and updating tests accordingly.
Changes:
- Make
start_scriptoptional (addsdefault = null) and gateinstall_script/start_scriptresources behindcount. - Update sync dependency resolution to handle missing stages in the pre→install→post→start chain.
- Update Terraform/Bun tests to reflect count-based resources and nullable outputs, adding additional test scenarios.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
registry/coder/modules/coder-utils/main.tf |
Makes install/start scripts optional, adds sync-dependency locals, and makes outputs nullable. |
registry/coder/modules/coder-utils/main.tftest.hcl |
Updates tests for count-based resources and adds new test scenarios for missing scripts. |
registry/coder/modules/coder-utils/main.test.ts |
Updates required-variable test to no longer require start_script. |
Comments suppressed due to low confidence (1)
registry/coder/modules/coder-utils/main.tf:188
start_scriptis now optional and can be the only script provided, but this script doesn't create${local.module_dir_path}before writing${local.start_path}/${local.start_log_path}. In a start-only configuration (which is now explicitly supported by tests), the script will fail at runtime because the directory doesn't exist. Addmkdir -p ${local.module_dir_path}near the top of this script (or otherwise ensure the directory exists) so start-only works end-to-end.
script = <<-EOT
#!/bin/bash
set -o errexit
set -o pipefail
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}
chmod +x ${local.start_path}
…locks 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/<module_dir_name>.
…of null for unused outputs
51089bd to
9127a11
Compare
Deep ReviewThe core goal — making A few things to address before merging: 1. Undocumented breaking change:
|
| pre_install | install | post_install | start | post_install_sync_deps |
start_sync_deps |
|---|---|---|---|---|---|
| Y | Y | Y | Y | install | install + post_install |
| N | Y | N | Y | — | install |
| Y | N | Y | Y | pre_install | post_install |
| N | N | N | Y | — | null |
| Y | N | N | Y | — | pre_install |
All correct. The chain degrades gracefully when any middle step is absent.
6. mkdir -p added to post_install_script and start_script — good
With install_script now optional, the directory may not exist when later scripts run. The mkdir -p addition is the right fix.
7. Minor: test coverage gap
No test for post_install_only (just post_install_script, no other scripts). This would verify post_install_sync_deps resolves to null when both pre_install_script and install_script are absent.
| Area | Status |
|---|---|
| Core optionality logic | ✅ Good |
| Sync dependency chain | ✅ Correct |
| Test coverage | ✅ Good (minor gap) |
mkdir -p fix |
✅ Good |
module_dir_path change |
|
| PR description accuracy | null → "") |
| Version bump vs breaking flag | |
| Missing newline at EOF | 🔧 Trivial fix |
🤖 This response was generated by Coder Agents.
…uired, 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.
…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.
…post_install and start Both scripts sync-wait on install, which always creates the directory.
…_directory for clarity
…o single script_names object Replace four individual outputs with a single script_names output containing pre_install, install, post_install, and start fields.
DevelopmentCats
left a comment
There was a problem hiding this comment.
LGTM provided we have tested this, but if not I can finagle something together to test it all
Currently testing it in the new boundary module. Will merge this right before #840. |
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 '<prefix>: ' 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.
|
Pushed 6678b04 on top of this branch to add two optional variables so downstream modules (starting with
Existing tests unchanged (defaults preserve the old behavior). Two new cases in Also fixed a stale Let me know if you'd rather I split this into a follow-up PR once this one merges.
|
Pass display_name_prefix = "Claude Code" and icon = "/icon/claude.svg" through to the coder-utils module so the workspace shows "Claude Code: Install Script" with the Claude icon instead of the generic defaults. Relies on the two new variables added to coder-utils on PR #842's branch.
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.
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 <self> <each>` 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.
Replace the local `for name in [...] : name if name != ""` filter with a one-line passthrough of `module.coder-utils.scripts`, the new upstream output that returns the run-ordered, filtered list of `coder exp sync` names. The filtering logic is generic to any module that wraps coder-utils, so it lives better there than in every consumer. claude-code just forwards it under its own `output "scripts"` so downstream templates keep the same surface. Requires PR #842 on coder/registry (feat/coder-utils-optional-install-start).
|
One more addition on this branch ( This is generic to anything wrapping
|
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.
Description
Rework the
coder-utilsmodule to makestart_scriptoptional while keepinginstall_scriptrequired. Addsmkdir -pto every script block, updates the module directory path to a namespaced location, moves all sync dependency resolution into locals, and bumps Terraform to>= 1.9.Type of Change
Module Information
Path:
registry/coder/modules/coder-utilsNew version:
v1.1.0Breaking change: [x] Yes [ ] No
install_scriptis now required (previously haddefault = null). Callers that omitted it will need to provide a value.start_scriptnow defaults tonull(previously required). The module directory path changed from$HOME/<name>to$HOME/.coder-modules/coder/coder-utils/<name>.Testing & Validation
terraform test— 7 passed, 0 failed)Changes
main.tfinstall_script: now required (no default). Resource always created (nocount).start_script: addeddefault = null. Resource conditionally created withcount.mkdir -pto ensure the module directory exists.module_dir_pathupdated to$HOME/.coder-modules/coder/coder-utils/<module_dir_name>.install_sync_deps,start_sync_deps). Post-install always waits for install. Start waits for install (+ post_install when present). Install waits for pre_install when present.install_script_nameoutput is always set. Other outputs return""when variable is not provided.>= 1.9.main.tftest.hclinstall_scriptand optionalstart_script.test_install_syncs_with_pre_installandtest_start_syncs_with_post_installto verify sync dependency chains.main.test.tsinstall_scriptadded back to required variables.README.md1.1.0.Related Issues
Closes: #848