Skip to content

Commit de3d8a4

Browse files
committed
refactor(registry/coder/modules/coder-utils): return scripts as a keyed map
Keying by phase lets callers write `module.coder_utils.scripts.install` instead of indexing a list whose length depends on which optional scripts were configured. Unconfigured phases are `null`. Callers that want every configured name can use `compact(values(scripts))`. README documents both patterns.
1 parent cfa7a24 commit de3d8a4

3 files changed

Lines changed: 51 additions & 37 deletions

File tree

registry/coder/modules/coder-utils/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,24 @@ The module materializes each script to `${module_directory}/scripts/` before run
9999
- `start.sh`
100100

101101
The pre-install and install `coder_script`s `mkdir -p` this subdirectory; post-install and start sync-depend on install, so the directory already exists by the time they run.
102+
103+
## Chaining downstream scripts
104+
105+
The `scripts` output exposes the `coder exp sync` name for each phase keyed by phase, with `null` for phases that were not configured. Downstream modules can sync their own `coder_script` resources behind this pipeline:
106+
107+
```tf
108+
resource "coder_script" "my_followup" {
109+
agent_id = coder_agent.main.id
110+
script = <<-EOT
111+
coder exp sync want my-followup ${module.coder_utils.scripts.install}
112+
coder exp sync start my-followup
113+
# ... work that depends on install completing ...
114+
EOT
115+
}
116+
```
117+
118+
To wait on every script this module created (regardless of which phases are configured), use `compact(values(...))`:
119+
120+
```tf
121+
coder exp sync want my-followup ${join(" ", compact(values(module.coder_utils.scripts)))}
122+
```

registry/coder/modules/coder-utils/main.tf

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -199,18 +199,16 @@ resource "coder_script" "start_script" {
199199
EOT
200200
}
201201

202-
# Filtered, run-order list of the `coder exp sync` names for every
203-
# coder_script this module actually creates. Absent scripts (pre/post/start
204-
# when their inputs are null) are omitted entirely, not padded with empty
205-
# strings. Downstream modules can use this with
206-
# `coder exp sync want <self> <each of these>` to serialize their own
207-
# scripts behind the install pipeline.
202+
# `coder exp sync` names keyed by phase, for downstream modules that want to
203+
# serialize their own `coder_script` resources behind this module's pipeline
204+
# with `coder exp sync want <self> <scripts.install>` or
205+
# `coder exp sync want <self> <values(compact(scripts))...>`.
208206
output "scripts" {
209-
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."
210-
value = concat(
211-
var.pre_install_script != null ? [local.pre_install_script_name] : [],
212-
[local.install_script_name],
213-
var.post_install_script != null ? [local.post_install_script_name] : [],
214-
var.start_script != null ? [local.start_script_name] : [],
215-
)
207+
description = "`coder exp sync` names for each phase this module can create. Unconfigured phases are null; `install` is always set."
208+
value = {
209+
pre_install = var.pre_install_script != null ? local.pre_install_script_name : null
210+
install = local.install_script_name
211+
post_install = var.post_install_script != null ? local.post_install_script_name : null
212+
start = var.start_script != null ? local.start_script_name : null
213+
}
216214
}

registry/coder/modules/coder-utils/main.tftest.hcl

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ run "test_optional_scripts_absent_by_default" {
408408
}
409409
}
410410

411-
# Verify `scripts` output is a filtered, run-order list
411+
# Verify `scripts` output is a keyed map of phase names
412412
run "test_scripts_output_with_all" {
413413
command = plan
414414

@@ -422,28 +422,23 @@ run "test_scripts_output_with_all" {
422422
}
423423

424424
assert {
425-
condition = length(output.scripts) == 4
426-
error_message = "scripts should have 4 entries when every script is set"
425+
condition = output.scripts.pre_install == "test-example-pre_install_script"
426+
error_message = "scripts.pre_install must be the pre-install name"
427427
}
428428

429429
assert {
430-
condition = output.scripts[0] == "test-example-pre_install_script"
431-
error_message = "scripts[0] must be the pre-install name"
430+
condition = output.scripts.install == "test-example-install_script"
431+
error_message = "scripts.install must be the install name"
432432
}
433433

434434
assert {
435-
condition = output.scripts[1] == "test-example-install_script"
436-
error_message = "scripts[1] must be the install name"
435+
condition = output.scripts.post_install == "test-example-post_install_script"
436+
error_message = "scripts.post_install must be the post-install name"
437437
}
438438

439439
assert {
440-
condition = output.scripts[2] == "test-example-post_install_script"
441-
error_message = "scripts[2] must be the post-install name"
442-
}
443-
444-
assert {
445-
condition = output.scripts[3] == "test-example-start_script"
446-
error_message = "scripts[3] must be the start name"
440+
condition = output.scripts.start == "test-example-start_script"
441+
error_message = "scripts.start must be the start name"
447442
}
448443
}
449444

@@ -457,13 +452,13 @@ run "test_scripts_output_with_install_only" {
457452
}
458453

459454
assert {
460-
condition = length(output.scripts) == 1
461-
error_message = "scripts should have exactly 1 entry (install) when pre/post/start are unset"
455+
condition = output.scripts.install == "test-example-install_script"
456+
error_message = "scripts.install must be the install name"
462457
}
463458

464459
assert {
465-
condition = output.scripts[0] == "test-example-install_script"
466-
error_message = "scripts[0] must be the install name"
460+
condition = output.scripts.pre_install == null && output.scripts.post_install == null && output.scripts.start == null
461+
error_message = "unconfigured phases must be null"
467462
}
468463
}
469464

@@ -478,18 +473,18 @@ run "test_scripts_output_with_install_and_post" {
478473
}
479474

480475
assert {
481-
condition = length(output.scripts) == 2
482-
error_message = "scripts should have 2 entries (install, post)"
476+
condition = output.scripts.install == "test-example-install_script"
477+
error_message = "scripts.install must be the install name"
483478
}
484479

485480
assert {
486-
condition = output.scripts[0] == "test-example-install_script"
487-
error_message = "scripts[0] must be the install name"
481+
condition = output.scripts.post_install == "test-example-post_install_script"
482+
error_message = "scripts.post_install must be the post-install name"
488483
}
489484

490485
assert {
491-
condition = output.scripts[1] == "test-example-post_install_script"
492-
error_message = "scripts[1] must be the post-install name"
486+
condition = output.scripts.pre_install == null && output.scripts.start == null
487+
error_message = "unconfigured phases must be null"
493488
}
494489
}
495490

0 commit comments

Comments
 (0)