From 88384315a0d9a6a7ee1a2b4e70b0551c78641d45 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 23 Apr 2026 06:14:54 +0000 Subject: [PATCH 1/6] docs: standardize module data layout under ~/.coder-modules --- AGENTS.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 5623f13ca..fc3c4978e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,6 +21,30 @@ bun test main.test.ts # Run single TS test (from - **Templates**: `registry/[ns]/templates/[name]/` with `main.tf`, `README.md` - **Validation**: `cmd/readmevalidation/` (Go) validates structure/frontmatter; URLs must be relative, not absolute +## Module Data Layout + +All runtime data a module writes on the workspace MUST live under a single per-module root: + +``` +$HOME/.coder-modules/// +``` + +For a Coder-owned module named `claude-code`, the root is `$HOME/.coder-modules/coder/claude-code/`. + +Within that root, use these standard subdirectories: + +| Subdirectory | Purpose | Example | +| ------------ | ----------------------------------------- | ------------------------------------------------------------ | +| `logs/` | Output from install, start, or any script | `$HOME/.coder-modules/coder/claude-code/logs/install.log` | +| `scripts/` | Scripts materialized at runtime (if any) | `$HOME/.coder-modules/coder/claude-code/scripts/install.sh` | +| `config/` | Generated or user-supplied configuration | `$HOME/.coder-modules/coder/boundary/config/boundary.config` | + +- Name log files after the script that produced them (`install.sh` writes to `logs/install.log`, `start.sh` writes to `logs/start.log`). +- Always `mkdir -p` the target directory before writing; do not assume it exists. +- Do not write module runtime data to `$HOME` directly, to ad-hoc paths like `~/.-module/`, or to `/tmp/` for anything that must survive the session. +- READMEs and tests should reference paths under this root so troubleshooting has one place to look. +- New modules MUST follow this layout. Existing modules should migrate to it when they are next touched. + ## Code Style - Every module MUST have `.tftest.hcl` tests; optional `main.test.ts` for container/script tests From cdd12c0d7c853b450e331ddd5d3502ec29d03689 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 23 Apr 2026 06:29:12 +0000 Subject: [PATCH 2/6] docs: drop config/ example, recommend coder-utils for script orchestration --- AGENTS.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index fc3c4978e..180472e79 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,18 +33,26 @@ For a Coder-owned module named `claude-code`, the root is `$HOME/.coder-modules/ Within that root, use these standard subdirectories: -| Subdirectory | Purpose | Example | -| ------------ | ----------------------------------------- | ------------------------------------------------------------ | -| `logs/` | Output from install, start, or any script | `$HOME/.coder-modules/coder/claude-code/logs/install.log` | -| `scripts/` | Scripts materialized at runtime (if any) | `$HOME/.coder-modules/coder/claude-code/scripts/install.sh` | -| `config/` | Generated or user-supplied configuration | `$HOME/.coder-modules/coder/boundary/config/boundary.config` | +| Subdirectory | Purpose | Example | +| ------------ | ----------------------------------------- | ----------------------------------------------------------- | +| `logs/` | Output from install, start, or any script | `$HOME/.coder-modules/coder/claude-code/logs/install.log` | +| `scripts/` | Scripts materialized at runtime (if any) | `$HOME/.coder-modules/coder/claude-code/scripts/install.sh` | - Name log files after the script that produced them (`install.sh` writes to `logs/install.log`, `start.sh` writes to `logs/start.log`). - Always `mkdir -p` the target directory before writing; do not assume it exists. - Do not write module runtime data to `$HOME` directly, to ad-hoc paths like `~/.-module/`, or to `/tmp/` for anything that must survive the session. +- Tool-specific data (config files, caches, state, etc.) lives wherever the tool expects; only standardize paths the module itself controls. - READMEs and tests should reference paths under this root so troubleshooting has one place to look. - New modules MUST follow this layout. Existing modules should migrate to it when they are next touched. +## Use `coder-utils` for Script Orchestration + +For any new module that runs scripts (or when reworking an existing one), use the [`coder-utils`](registry/coder/modules/coder-utils) module to orchestrate `pre_install`, `install`, `post_install`, and `start` scripts instead of hand-rolling `coder_script` resources. + +- `coder-utils` handles script ordering via `coder exp sync`, materializes scripts under `module_directory`, and writes logs to `module_directory/logs/` automatically, which aligns with the Module Data Layout above. +- Set `module_directory = "$HOME/.coder-modules//"` so the standard root and `logs/` subdirectory fall out for free. +- See https://github.com/coder/registry/pull/870 for a reference migration (logs nested under `module_directory/logs`). + ## Code Style - Every module MUST have `.tftest.hcl` tests; optional `main.test.ts` for container/script tests From 595e7f884548d6465b12433798e2dec998a26b33 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 23 Apr 2026 06:31:11 +0000 Subject: [PATCH 3/6] docs: drop pr 870 reference link --- AGENTS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 180472e79..05cc3d497 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -51,7 +51,6 @@ For any new module that runs scripts (or when reworking an existing one), use th - `coder-utils` handles script ordering via `coder exp sync`, materializes scripts under `module_directory`, and writes logs to `module_directory/logs/` automatically, which aligns with the Module Data Layout above. - Set `module_directory = "$HOME/.coder-modules//"` so the standard root and `logs/` subdirectory fall out for free. -- See https://github.com/coder/registry/pull/870 for a reference migration (logs nested under `module_directory/logs`). ## Code Style From aa8e55105de3f2b5f371a69461ad30d09ce7900c Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 23 Apr 2026 16:33:57 +0000 Subject: [PATCH 4/6] docs: note coder-utils scripts/ layout and agent_name prefix --- AGENTS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 05cc3d497..66accecff 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -49,8 +49,8 @@ Within that root, use these standard subdirectories: For any new module that runs scripts (or when reworking an existing one), use the [`coder-utils`](registry/coder/modules/coder-utils) module to orchestrate `pre_install`, `install`, `post_install`, and `start` scripts instead of hand-rolling `coder_script` resources. -- `coder-utils` handles script ordering via `coder exp sync`, materializes scripts under `module_directory`, and writes logs to `module_directory/logs/` automatically, which aligns with the Module Data Layout above. -- Set `module_directory = "$HOME/.coder-modules//"` so the standard root and `logs/` subdirectory fall out for free. +- `coder-utils` handles script ordering via `coder exp sync`, materializes scripts under `module_directory/scripts/` (prefixed with `${agent_name}-utils-`), and writes logs to `module_directory/logs/` automatically, which aligns with the Module Data Layout above. +- Set `module_directory = "$HOME/.coder-modules//"` so the standard root, `scripts/`, and `logs/` subdirectories fall out for free. ## Code Style From d4c30509ccfa4b7bf4165768ca5b101673d6046c Mon Sep 17 00:00:00 2001 From: Muhammad Atif Ali Date: Fri, 24 Apr 2026 21:13:15 +0500 Subject: [PATCH 5/6] docs: expand coder-utils guidance with templatefile pattern and Terraform conventions - Remove agent_name-utils- prefix reference (dropped in PR 874) - Add templatefile() pattern with encoding rules table for .tftpl variables - Add start_script to coder-utils example (was missing) - Add scripts output pass-through pattern - Add variable/output ordering convention and sensitive = true guidance - Add .tftest.hcl command = plan vs apply guidance - Add count vs for_each rule for optional singletons --- AGENTS.md | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 66accecff..fedbf36b4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -49,9 +49,56 @@ Within that root, use these standard subdirectories: For any new module that runs scripts (or when reworking an existing one), use the [`coder-utils`](registry/coder/modules/coder-utils) module to orchestrate `pre_install`, `install`, `post_install`, and `start` scripts instead of hand-rolling `coder_script` resources. -- `coder-utils` handles script ordering via `coder exp sync`, materializes scripts under `module_directory/scripts/` (prefixed with `${agent_name}-utils-`), and writes logs to `module_directory/logs/` automatically, which aligns with the Module Data Layout above. +- `coder-utils` handles script ordering via `coder exp sync`, materializes scripts under `module_directory/scripts/` (e.g., `install.sh`, `start.sh`), and writes logs to `module_directory/logs/` automatically, which aligns with the Module Data Layout above. - Set `module_directory = "$HOME/.coder-modules//"` so the standard root, `scripts/`, and `logs/` subdirectories fall out for free. +### Passing scripts to `coder-utils` + +Store each script as a `.tftpl` file under `scripts/`. Render it at **plan time** in a `locals` block using `templatefile()`, then pass the rendered string directly to the `coder-utils` module. + +**Encoding rules for template variables:** + +| Value type | Terraform side | Template (`.tftpl`) side | +|---|---|---| +| String / path | pass as-is | `ARG_FOO='${ARG_FOO}'` | +| Boolean | `tostring(var.foo)` | `ARG_FOO='${ARG_FOO}'` | +| Free-form string (may contain quotes) | `base64encode(var.foo)` | `ARG_FOO=$(echo -n '${ARG_FOO}' \| base64 -d)` | +| Object / list (JSON) | `base64encode(jsonencode(var.foo))` | `ARG_FOO=$(echo -n '${ARG_FOO}' \| base64 -d)` | + +In `.tftpl` files, write literal bash `$` as `$$` (e.g., `$${HOME}`) so Terraform does not treat them as template interpolations. + +```tf +locals { + install_script = templatefile("${path.module}/scripts/install.sh.tftpl", { + ARG_FOO = var.foo + ARG_BAR = var.bar + }) +} + +module "coder_utils" { + source = "registry.coder.com/coder/coder-utils/coder" + version = "0.0.1" + + agent_id = var.agent_id + module_directory = "$HOME/.coder-modules//" + display_name_prefix = "My Module" + icon = var.icon + pre_install_script = var.pre_install_script + install_script = local.install_script + post_install_script = var.post_install_script + start_script = var.start_script # optional; omit if the module does not start a process +} +``` + +Always expose the `scripts` output as a pass-through so upstream modules can serialize their own `coder_script` resources behind this module's install pipeline: + +```tf +output "scripts" { + description = "Ordered list of coder exp sync names produced by this module, in run order." + value = module.coder_utils.scripts +} +``` + ## Code Style - Every module MUST have `.tftest.hcl` tests; optional `main.test.ts` for container/script tests @@ -62,6 +109,28 @@ For any new module that runs scripts (or when reworking an existing one), use th - **Do NOT include input/output variable tables in module or template READMEs.** The registry automatically generates these from the Terraform source (e.g., variable and output blocks in `main.tf`). Adding them to the README is redundant and creates maintenance drift. - Usage examples (e.g., a `module "..." { }` block) are encouraged, but not tables enumerating inputs/outputs. +### Variable and output conventions + +Order variable blocks: `description` → `type` → `default` → `validation` → `sensitive`. + +```tf +variable "api_key" { + description = "API key for the service." + type = string + default = "" + sensitive = true +} +``` + +- Mark variables and outputs that hold secrets or tokens `sensitive = true`. +- Every `output` block must have a `description`. +- Use `count = condition ? 1 : 0` for optional singleton resources. Reserve `for_each` for maps/sets where resource identity matters. + +### `.tftest.hcl` test commands + +- Use `command = plan` only for assertions on **input-derived values** (variables, locals computed from inputs). +- Use `command = apply` for **computed attributes** (resource IDs, anything the provider generates), and for nested blocks of set type (they cannot be indexed with `[0]` under `plan`). + ## PR Review Checklist - Version bumped via `.github/scripts/version-bump.sh` if module changed (patch=bugfix, minor=feature, major=breaking) From 65c55c7c1a074e127b041e09d03fb71e73c62b6b Mon Sep 17 00:00:00 2001 From: Muhammad Atif Ali Date: Fri, 24 Apr 2026 21:26:46 +0500 Subject: [PATCH 6/6] chore: fix prettier formatting in AGENTS.md --- AGENTS.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index fedbf36b4..5c135aa33 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -58,12 +58,12 @@ Store each script as a `.tftpl` file under `scripts/`. Render it at **plan time* **Encoding rules for template variables:** -| Value type | Terraform side | Template (`.tftpl`) side | -|---|---|---| -| String / path | pass as-is | `ARG_FOO='${ARG_FOO}'` | -| Boolean | `tostring(var.foo)` | `ARG_FOO='${ARG_FOO}'` | -| Free-form string (may contain quotes) | `base64encode(var.foo)` | `ARG_FOO=$(echo -n '${ARG_FOO}' \| base64 -d)` | -| Object / list (JSON) | `base64encode(jsonencode(var.foo))` | `ARG_FOO=$(echo -n '${ARG_FOO}' \| base64 -d)` | +| Value type | Terraform side | Template (`.tftpl`) side | +| ------------------------------------- | ----------------------------------- | ---------------------------------------------- | +| String / path | pass as-is | `ARG_FOO='${ARG_FOO}'` | +| Boolean | `tostring(var.foo)` | `ARG_FOO='${ARG_FOO}'` | +| Free-form string (may contain quotes) | `base64encode(var.foo)` | `ARG_FOO=$(echo -n '${ARG_FOO}' \| base64 -d)` | +| Object / list (JSON) | `base64encode(jsonencode(var.foo))` | `ARG_FOO=$(echo -n '${ARG_FOO}' \| base64 -d)` | In `.tftpl` files, write literal bash `$` as `$$` (e.g., `$${HOME}`) so Terraform does not treat them as template interpolations. @@ -86,7 +86,7 @@ module "coder_utils" { pre_install_script = var.pre_install_script install_script = local.install_script post_install_script = var.post_install_script - start_script = var.start_script # optional; omit if the module does not start a process + start_script = var.start_script # optional; omit if the module does not start a process } ```