Skip to content

Commit 39f332f

Browse files
35C4n0rJay Kumarmatifali
authored
feat(registry/coder/modules/coder-utils): make install_script and start_script optional (#842)
Co-authored-by: Jay Kumar <jay.kumar@coder.com> Co-authored-by: Atif Ali <atif@coder.com>
1 parent b108185 commit 39f332f

4 files changed

Lines changed: 425 additions & 128 deletions

File tree

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

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ The Coder Utils module is a building block for modules that need to run multiple
2020
```tf
2121
module "coder_utils" {
2222
source = "registry.coder.com/coder/coder-utils/coder"
23-
version = "1.0.1"
23+
version = "1.1.0"
2424
25-
agent_id = coder_agent.main.id
26-
agent_name = "myagent"
27-
module_dir_name = ".my-module"
25+
agent_id = coder_agent.main.id
26+
agent_name = "myagent"
27+
module_directory = ".my-module"
2828
2929
pre_install_script = <<-EOT
3030
#!/bin/bash
@@ -56,10 +56,30 @@ module "coder_utils" {
5656

5757
The module orchestrates scripts in the following order:
5858

59-
1. **Log File Creation** - Creates module directory and log files
60-
2. **Pre-Install Script** (optional) - Runs before installation
61-
3. **Install Script** - Main installation
62-
4. **Post-Install Script** (optional) - Runs after installation
63-
5. **Start Script** - Starts the application
59+
1. **Pre-Install Script** (optional) - Runs before installation
60+
2. **Install Script** (required) - Main installation
61+
3. **Post-Install Script** (optional) - Runs after installation
62+
4. **Start Script** (optional) - Starts the application
6463

6564
Each script waits for its prerequisites to complete before running using `coder exp sync` dependency management.
65+
66+
## Customizing Script Display
67+
68+
By default each `coder_script` renders in the Coder UI as plain "Install Script", "Pre-Install Script", etc. Downstream modules can brand them:
69+
70+
```tf
71+
module "coder_utils" {
72+
source = "registry.coder.com/coder/coder-utils/coder"
73+
version = "1.1.0"
74+
75+
agent_id = coder_agent.main.id
76+
agent_name = "myagent"
77+
module_directory = ".my-module"
78+
install_script = "echo installing"
79+
80+
display_name_prefix = "Claude Code" # yields "Claude Code: Install Script", etc.
81+
icon = "/icon/claude.svg"
82+
}
83+
```
84+
85+
Both variables are optional. `display_name_prefix` defaults to `""` (no prefix), and `icon` defaults to `null` (use the Coder provider's default).

registry/coder/modules/coder-utils/main.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ describe("coder-utils", async () => {
77
testRequiredVariables(import.meta.dir, {
88
agent_id: "test-agent-id",
99
agent_name: "test-agent",
10-
module_dir_name: ".test-module",
11-
start_script: "echo 'start'",
10+
module_directory: ".test-module",
11+
install_script: "echo 'install'",
1212
});
1313
});

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

Lines changed: 66 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ variable "pre_install_script" {
2929
variable "install_script" {
3030
type = string
3131
description = "Script to install the agent used by AgentAPI."
32-
default = null
3332
}
3433

3534
variable "post_install_script" {
@@ -41,6 +40,7 @@ variable "post_install_script" {
4140
variable "start_script" {
4241
type = string
4342
description = "Script that starts AgentAPI."
43+
default = null
4444
}
4545

4646
variable "agent_name" {
@@ -49,84 +49,107 @@ variable "agent_name" {
4949

5050
}
5151

52-
variable "module_dir_name" {
52+
variable "module_directory" {
53+
type = string
54+
description = "The module's working directory for scripts and logs."
55+
}
56+
57+
variable "display_name_prefix" {
5358
type = string
54-
description = "The name of the module directory."
59+
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'."
60+
default = ""
61+
}
62+
63+
variable "icon" {
64+
type = string
65+
description = "Icon shown in the Coder UI for every coder_script this module creates. Falls back to the Coder provider's default when unset."
66+
default = null
5567
}
5668

5769
locals {
5870
encoded_pre_install_script = var.pre_install_script != null ? base64encode(var.pre_install_script) : ""
59-
encoded_install_script = var.install_script != null ? base64encode(var.install_script) : ""
71+
encoded_install_script = base64encode(var.install_script)
6072
encoded_post_install_script = var.post_install_script != null ? base64encode(var.post_install_script) : ""
61-
encoded_start_script = base64encode(var.start_script)
73+
encoded_start_script = var.start_script != null ? base64encode(var.start_script) : ""
6274

6375
pre_install_script_name = "${var.agent_name}-pre_install_script"
6476
install_script_name = "${var.agent_name}-install_script"
6577
post_install_script_name = "${var.agent_name}-post_install_script"
6678
start_script_name = "${var.agent_name}-start_script"
6779

68-
module_dir_path = "$HOME/${var.module_dir_name}"
80+
pre_install_path = "${var.module_directory}/pre_install.sh"
81+
install_path = "${var.module_directory}/install.sh"
82+
post_install_path = "${var.module_directory}/post_install.sh"
83+
start_path = "${var.module_directory}/start.sh"
6984

70-
pre_install_path = "${local.module_dir_path}/pre_install.sh"
71-
install_path = "${local.module_dir_path}/install.sh"
72-
post_install_path = "${local.module_dir_path}/post_install.sh"
73-
start_path = "${local.module_dir_path}/start.sh"
85+
pre_install_log_path = "${var.module_directory}/pre_install.log"
86+
install_log_path = "${var.module_directory}/install.log"
87+
post_install_log_path = "${var.module_directory}/post_install.log"
88+
start_log_path = "${var.module_directory}/start.log"
7489

75-
pre_install_log_path = "${local.module_dir_path}/pre_install.log"
76-
install_log_path = "${local.module_dir_path}/install.log"
77-
post_install_log_path = "${local.module_dir_path}/post_install.log"
78-
start_log_path = "${local.module_dir_path}/start.log"
90+
install_sync_deps = var.pre_install_script != null ? local.pre_install_script_name : null
91+
92+
start_sync_deps = (
93+
var.post_install_script != null
94+
? "${local.install_script_name} ${local.post_install_script_name}"
95+
: local.install_script_name
96+
)
97+
98+
display_name_prefix = var.display_name_prefix != "" ? "${var.display_name_prefix}: " : ""
7999
}
80100

81101
resource "coder_script" "pre_install_script" {
82102
count = var.pre_install_script == null ? 0 : 1
83103
agent_id = var.agent_id
84-
display_name = "Pre-Install Script"
104+
display_name = "${local.display_name_prefix}Pre-Install Script"
105+
icon = var.icon
85106
run_on_start = true
86107
script = <<-EOT
87108
#!/bin/bash
88109
set -o errexit
89110
set -o pipefail
90111
91-
mkdir -p ${local.module_dir_path}
112+
mkdir -p ${var.module_directory}
92113
93114
trap 'coder exp sync complete ${local.pre_install_script_name}' EXIT
94115
coder exp sync start ${local.pre_install_script_name}
95116
96117
echo -n '${local.encoded_pre_install_script}' | base64 -d > ${local.pre_install_path}
97118
chmod +x ${local.pre_install_path}
98119
99-
${local.pre_install_path} > ${local.pre_install_log_path} 2>&1
120+
${local.pre_install_path} 2>&1 | tee ${local.pre_install_log_path}
100121
EOT
101122
}
102123

103124
resource "coder_script" "install_script" {
104125
agent_id = var.agent_id
105-
display_name = "Install Script"
126+
display_name = "${local.display_name_prefix}Install Script"
127+
icon = var.icon
106128
run_on_start = true
107129
script = <<-EOT
108130
#!/bin/bash
109131
set -o errexit
110132
set -o pipefail
111133
112-
mkdir -p ${local.module_dir_path}
134+
mkdir -p ${var.module_directory}
113135
114136
trap 'coder exp sync complete ${local.install_script_name}' EXIT
115-
%{if var.pre_install_script != null~}
116-
coder exp sync want ${local.install_script_name} ${local.pre_install_script_name}
137+
%{if local.install_sync_deps != null~}
138+
coder exp sync want ${local.install_script_name} ${local.install_sync_deps}
117139
%{endif~}
118140
coder exp sync start ${local.install_script_name}
119141
echo -n '${local.encoded_install_script}' | base64 -d > ${local.install_path}
120142
chmod +x ${local.install_path}
121143
122-
${local.install_path} > ${local.install_log_path} 2>&1
144+
${local.install_path} 2>&1 | tee ${local.install_log_path}
123145
EOT
124146
}
125147

126148
resource "coder_script" "post_install_script" {
127149
count = var.post_install_script != null ? 1 : 0
128150
agent_id = var.agent_id
129-
display_name = "Post-Install Script"
151+
display_name = "${local.display_name_prefix}Post-Install Script"
152+
icon = var.icon
130153
run_on_start = true
131154
script = <<-EOT
132155
#!/bin/bash
@@ -140,13 +163,15 @@ resource "coder_script" "post_install_script" {
140163
echo -n '${local.encoded_post_install_script}' | base64 -d > ${local.post_install_path}
141164
chmod +x ${local.post_install_path}
142165
143-
${local.post_install_path} > ${local.post_install_log_path} 2>&1
166+
${local.post_install_path} 2>&1 | tee ${local.post_install_log_path}
144167
EOT
145168
}
146169

147170
resource "coder_script" "start_script" {
171+
count = var.start_script != null ? 1 : 0
148172
agent_id = var.agent_id
149-
display_name = "Start Script"
173+
display_name = "${local.display_name_prefix}Start Script"
174+
icon = var.icon
150175
run_on_start = true
151176
script = <<-EOT
152177
#!/bin/bash
@@ -155,36 +180,28 @@ resource "coder_script" "start_script" {
155180
156181
trap 'coder exp sync complete ${local.start_script_name}' EXIT
157182
158-
%{if var.post_install_script != null~}
159-
coder exp sync want ${local.start_script_name} ${local.install_script_name} ${local.post_install_script_name}
160-
%{else~}
161-
coder exp sync want ${local.start_script_name} ${local.install_script_name}
162-
%{endif~}
183+
coder exp sync want ${local.start_script_name} ${local.start_sync_deps}
163184
coder exp sync start ${local.start_script_name}
164185
165186
echo -n '${local.encoded_start_script}' | base64 -d > ${local.start_path}
166187
chmod +x ${local.start_path}
167188
168-
${local.start_path} > ${local.start_log_path} 2>&1
189+
${local.start_path} 2>&1 | tee ${local.start_log_path}
169190
EOT
170191
}
171192

172-
output "pre_install_script_name" {
173-
description = "The name of the pre-install script for sync."
174-
value = local.pre_install_script_name
175-
}
176-
177-
output "install_script_name" {
178-
description = "The name of the install script for sync."
179-
value = local.install_script_name
180-
}
181-
182-
output "post_install_script_name" {
183-
description = "The name of the post-install script for sync."
184-
value = local.post_install_script_name
193+
# Filtered, run-order list of the `coder exp sync` names for every
194+
# coder_script this module actually creates. Absent scripts (pre/post/start
195+
# when their inputs are null) are omitted entirely, not padded with empty
196+
# strings. Downstream modules can use this with
197+
# `coder exp sync want <self> <each of these>` to serialize their own
198+
# scripts behind the install pipeline.
199+
output "scripts" {
200+
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."
201+
value = concat(
202+
var.pre_install_script != null ? [local.pre_install_script_name] : [],
203+
[local.install_script_name],
204+
var.post_install_script != null ? [local.post_install_script_name] : [],
205+
var.start_script != null ? [local.start_script_name] : [],
206+
)
185207
}
186-
187-
output "start_script_name" {
188-
description = "The name of the start script for sync."
189-
value = local.start_script_name
190-
}

0 commit comments

Comments
 (0)