Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 9 additions & 15 deletions registry/coder/modules/coder-utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,13 @@ tags: [internal, library]

The Coder Utils module is a building block for modules that need to run multiple scripts in a specific order. It uses `coder exp sync` for dependency management and is designed for orchestrating pre-install, install, post-install, and start scripts.

> [!NOTE]
>
> - The `agent_name` should be the same as that of the agentapi module's `agent_name` if used together.

```tf
module "coder_utils" {
source = "registry.coder.com/coder/coder-utils/coder"
version = "1.3.0"
version = "0.0.1"

agent_id = coder_agent.main.id
agent_name = "myagent"
module_directory = ".my-module"
module_directory = "$HOME/.coder-modules/coder/claude-code"

pre_install_script = <<-EOT
#!/bin/bash
Expand Down Expand Up @@ -70,11 +65,10 @@ By default each `coder_script` renders in the Coder UI as plain "Install Script"
```tf
module "coder_utils" {
source = "registry.coder.com/coder/coder-utils/coder"
version = "1.3.0"
version = "0.0.1"

agent_id = coder_agent.main.id
agent_name = "myagent"
module_directory = ".my-module"
module_directory = "$HOME/.coder-modules/coder/claude-code"
install_script = "echo installing"

display_name_prefix = "Claude Code" # yields "Claude Code: Install Script", etc.
Expand All @@ -99,9 +93,9 @@ Each `coder_script` `mkdir -p`s this subdirectory before its `tee` runs, so the

The module materializes each script to `${module_directory}/scripts/` before running it:

- `${agent_name}-utils-pre_install.sh`
- `${agent_name}-utils-install.sh`
- `${agent_name}-utils-post_install.sh`
- `${agent_name}-utils-start.sh`
- `pre_install.sh`
- `install.sh`
- `post_install.sh`
- `start.sh`

The `${agent_name}-utils-` prefix namespaces files per-agent so multiple `coder-utils` instances can safely share a `module_directory`. 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.
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.
33 changes: 29 additions & 4 deletions registry/coder/modules/coder-utils/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
import { describe } from "bun:test";
import { runTerraformInit, testRequiredVariables } from "~test";
import { describe, expect, it } from "bun:test";
import {
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "~test";

describe("coder-utils", async () => {
await runTerraformInit(import.meta.dir);

testRequiredVariables(import.meta.dir, {
agent_id: "test-agent-id",
agent_name: "test-agent",
module_directory: ".test-module",
module_directory: "$HOME/.coder-modules/test/example",
install_script: "echo 'install'",
});

it("rejects invalid module_directory", async () => {
try {
await runTerraformApply(import.meta.dir, {
agent_id: "test-agent-id",
module_directory: "$HOME/.coder-modules/test",
install_script: "echo 'install'",
});
} catch (ex) {
if (!(ex instanceof Error)) {
throw new Error("Unknown error generated");
}

expect(ex.message).toContain("module_directory must match the pattern");
expect(ex.message).toContain(
"'$HOME/.coder-modules/<namespace>/<module-name>'",
);
return;
}

throw new Error("module_directory validation should have failed");
});
});
32 changes: 17 additions & 15 deletions registry/coder/modules/coder-utils/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,14 @@ variable "start_script" {
default = null
}

variable "agent_name" {
type = string
description = "The name of the agent. This is used to construct unique script names for the experiment sync."

}

variable "module_directory" {
type = string
description = "The module's working directory. Scripts this module writes land under `scripts/` and their logs under `logs/` in this path."
description = "The calling module's working directory. Must follow the pattern '$HOME/.coder-modules/<namespace>/<module-name>'."

validation {
condition = can(regex("^\\$HOME/\\.coder-modules/[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$", var.module_directory))
error_message = "module_directory must match the pattern '$HOME/.coder-modules/<namespace>/<module-name>' (e.g. '$HOME/.coder-modules/coder/claude-code')."
}
}

variable "display_name_prefix" {
Expand All @@ -67,20 +66,23 @@ variable "icon" {
}

locals {
path_parts = split("/", var.module_directory)
caller_name = "${local.path_parts[length(local.path_parts) - 2]}-${local.path_parts[length(local.path_parts) - 1]}"

encoded_pre_install_script = var.pre_install_script != null ? base64encode(var.pre_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) : ""

pre_install_script_name = "${var.agent_name}-pre_install_script"
install_script_name = "${var.agent_name}-install_script"
post_install_script_name = "${var.agent_name}-post_install_script"
start_script_name = "${var.agent_name}-start_script"
pre_install_script_name = "${local.caller_name}-pre_install_script"
install_script_name = "${local.caller_name}-install_script"
post_install_script_name = "${local.caller_name}-post_install_script"
start_script_name = "${local.caller_name}-start_script"

pre_install_path = "${local.scripts_directory}/${var.agent_name}-utils-pre_install.sh"
install_path = "${local.scripts_directory}/${var.agent_name}-utils-install.sh"
post_install_path = "${local.scripts_directory}/${var.agent_name}-utils-post_install.sh"
start_path = "${local.scripts_directory}/${var.agent_name}-utils-start.sh"
pre_install_path = "${local.scripts_directory}/pre_install.sh"
install_path = "${local.scripts_directory}/install.sh"
post_install_path = "${local.scripts_directory}/post_install.sh"
start_path = "${local.scripts_directory}/start.sh"

pre_install_log_path = "${local.log_directory}/pre_install.log"
install_log_path = "${local.log_directory}/install.log"
Expand Down
Loading
Loading