diff --git a/registry/coder/modules/mux/README.md b/registry/coder/modules/mux/README.md index b9cfafc0..69073926 100644 --- a/registry/coder/modules/mux/README.md +++ b/registry/coder/modules/mux/README.md @@ -14,7 +14,7 @@ Automatically install and run [Mux](https://github.com/coder/mux) in a Coder wor module "mux" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/mux/coder" - version = "1.1.0" + version = "1.2.0" agent_id = coder_agent.main.id } ``` @@ -37,7 +37,7 @@ module "mux" { module "mux" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/mux/coder" - version = "1.1.0" + version = "1.2.0" agent_id = coder_agent.main.id } ``` @@ -48,7 +48,7 @@ module "mux" { module "mux" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/mux/coder" - version = "1.1.0" + version = "1.2.0" agent_id = coder_agent.main.id # Default is "latest"; set to a specific version to pin install_version = "0.4.0" @@ -63,19 +63,34 @@ Start Mux with `mux server --add-project /path/to/project`: module "mux" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/mux/coder" - version = "1.1.0" + version = "1.2.0" agent_id = coder_agent.main.id add-project = "/path/to/project" } ``` +### Pass Arbitrary `mux server` Arguments + +Use `additional_arguments` to append additional arguments to `mux server`. +The module parses quoted values, so grouped arguments remain intact. + +```tf +module "mux" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/mux/coder" + version = "1.2.0" + agent_id = coder_agent.main.id + additional_arguments = "--open-mode pinned --add-project '/workspaces/my repo'" +} +``` + ### Custom Port ```tf module "mux" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/mux/coder" - version = "1.1.0" + version = "1.2.0" agent_id = coder_agent.main.id port = 8080 } @@ -89,7 +104,7 @@ Run an existing copy of Mux if found, otherwise install from npm: module "mux" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/mux/coder" - version = "1.1.0" + version = "1.2.0" agent_id = coder_agent.main.id use_cached = true } @@ -103,7 +118,7 @@ Run without installing from the network (requires Mux to be pre-installed): module "mux" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/mux/coder" - version = "1.1.0" + version = "1.2.0" agent_id = coder_agent.main.id install = false } diff --git a/registry/coder/modules/mux/main.test.ts b/registry/coder/modules/mux/main.test.ts index 96fae5e4..c1b6e238 100644 --- a/registry/coder/modules/mux/main.test.ts +++ b/registry/coder/modules/mux/main.test.ts @@ -1,6 +1,11 @@ import { describe, expect, it } from "bun:test"; import { executeScriptInContainer, + execContainer, + findResourceInstance, + readFileContainer, + removeContainer, + runContainer, runTerraformApply, runTerraformInit, testRequiredVariables, @@ -40,6 +45,57 @@ describe("mux", async () => { } }, 60000); + it("parses custom additional_arguments", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + install: false, + log_path: "/tmp/mux.log", + additional_arguments: + "--open-mode pinned --add-project '/workspaces/my repo'", + }); + + const instance = findResourceInstance(state, "coder_script"); + const id = await runContainer("alpine/curl"); + + try { + const setup = await execContainer(id, [ + "sh", + "-c", + `apk add --no-cache bash >/dev/null +mkdir -p /tmp/mux +cat <<'EOF' > /tmp/mux/mux +#!/usr/bin/env sh +i=1 +for arg in "$@"; do + echo "arg$i=$arg" + i=$((i + 1)) +done +EOF +chmod +x /tmp/mux/mux`, + ]); + expect(setup.exitCode).toBe(0); + + const output = await execContainer(id, ["sh", "-c", instance.script]); + if (output.exitCode !== 0) { + console.log("STDOUT:\n" + output.stdout); + console.log("STDERR:\n" + output.stderr); + } + expect(output.exitCode).toBe(0); + + await execContainer(id, ["sh", "-c", "sleep 1"]); + const log = await readFileContainer(id, "/tmp/mux.log"); + expect(log).toContain("arg1=server"); + expect(log).toContain("arg2=--port"); + expect(log).toContain("arg3=4000"); + expect(log).toContain("arg4=--open-mode"); + expect(log).toContain("arg5=pinned"); + expect(log).toContain("arg6=--add-project"); + expect(log).toContain("arg7=/workspaces/my repo"); + } finally { + await removeContainer(id); + } + }, 60000); + it("runs with npm present", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", diff --git a/registry/coder/modules/mux/main.tf b/registry/coder/modules/mux/main.tf index 1eeddecf..dbc585d8 100644 --- a/registry/coder/modules/mux/main.tf +++ b/registry/coder/modules/mux/main.tf @@ -55,6 +55,12 @@ variable "add-project" { default = null } +variable "additional_arguments" { + type = string + description = "Additional command-line arguments to pass to `mux server` (for example: `--add-project /path --open-mode pinned`)." + default = "" +} + variable "install_version" { type = string description = "The version or dist-tag of Mux to install." @@ -142,6 +148,7 @@ resource "coder_script" "mux" { PORT : var.port, LOG_PATH : var.log_path, ADD_PROJECT : var.add-project == null ? "" : var.add-project, + ADDITIONAL_ARGUMENTS : var.additional_arguments, INSTALL_PREFIX : var.install_prefix, OFFLINE : !var.install, USE_CACHED : var.use_cached, diff --git a/registry/coder/modules/mux/mux.tftest.hcl b/registry/coder/modules/mux/mux.tftest.hcl index af103ae2..688ddedb 100644 --- a/registry/coder/modules/mux/mux.tftest.hcl +++ b/registry/coder/modules/mux/mux.tftest.hcl @@ -79,6 +79,20 @@ run "auth_token_in_url" { } } +run "custom_additional_arguments" { + command = plan + + variables { + agent_id = "foo" + additional_arguments = "--open-mode pinned --add-project '/workspaces/my repo'" + } + + assert { + condition = strcontains(resource.coder_script.mux.script, "--open-mode pinned --add-project '/workspaces/my repo'") + error_message = "mux launch script must include the configured additional arguments" + } +} + run "custom_version" { command = plan diff --git a/registry/coder/modules/mux/run.sh b/registry/coder/modules/mux/run.sh index 0d0c6520..099448fb 100644 --- a/registry/coder/modules/mux/run.sh +++ b/registry/coder/modules/mux/run.sh @@ -20,6 +20,22 @@ function run_mux() { if [ -n "${ADD_PROJECT}" ]; then set -- "$@" --add-project "${ADD_PROJECT}" fi + + # Parse additional user-supplied server arguments while preserving quoted groups. + if [ -n "${ADDITIONAL_ARGUMENTS}" ]; then + local parsed_additional_arguments + if ! parsed_additional_arguments="$(printf "%s\n" "${ADDITIONAL_ARGUMENTS}" | xargs -n1 printf "%s\n" 2> /dev/null)"; then + echo "❌ Failed to parse additional_arguments. Ensure quotes are balanced." + exit 1 + fi + while IFS= read -r parsed_arg; do + [ -n "$parsed_arg" ] || continue + set -- "$@" "$parsed_arg" + done << EOF +$${parsed_additional_arguments} +EOF + fi + echo "🚀 Starting mux server on port $port_value..." echo "Check logs at ${LOG_PATH}!" MUX_SERVER_AUTH_TOKEN="$auth_token_value" PORT="$port_value" "$MUX_BINARY" "$@" > "${LOG_PATH}" 2>&1 &