Add arbitrary mux server command argument parsing (#738)
## Summary - add a new `additional_arguments` module variable to pass extra arguments to `mux server` - parse `additional_arguments` in `run.sh` with quoted-group support so values like paths with spaces are preserved - keep existing `add-project` behavior while allowing additional arbitrary flags - add Terraform and Bun tests covering `additional_arguments` behavior - document the new option in the module README and bump example version references to `1.2.0` ## Why The module previously only supported the `add-project` flag. This change lets users pass additional `mux server` arguments without waiting for new module variables. ## Validation - `shellcheck --severity=warning --format=gcc registry/coder/modules/mux/run.sh` - `terraform -chdir=registry/coder/modules/mux test -verbose` - `bun test registry/coder/modules/mux/main.test.ts` ## Breaking changes None. --- Generated with Mux (exec agent) using GPT-5.
This commit is contained in:
parent
480bf4b48c
commit
94e41d3780
@ -14,7 +14,7 @@ Automatically install and run [Mux](https://github.com/coder/mux) in a Coder wor
|
|||||||
module "mux" {
|
module "mux" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder/mux/coder"
|
source = "registry.coder.com/coder/mux/coder"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -37,7 +37,7 @@ module "mux" {
|
|||||||
module "mux" {
|
module "mux" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder/mux/coder"
|
source = "registry.coder.com/coder/mux/coder"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -48,7 +48,7 @@ module "mux" {
|
|||||||
module "mux" {
|
module "mux" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder/mux/coder"
|
source = "registry.coder.com/coder/mux/coder"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
# Default is "latest"; set to a specific version to pin
|
# Default is "latest"; set to a specific version to pin
|
||||||
install_version = "0.4.0"
|
install_version = "0.4.0"
|
||||||
@ -63,19 +63,34 @@ Start Mux with `mux server --add-project /path/to/project`:
|
|||||||
module "mux" {
|
module "mux" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder/mux/coder"
|
source = "registry.coder.com/coder/mux/coder"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
add-project = "/path/to/project"
|
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
|
### Custom Port
|
||||||
|
|
||||||
```tf
|
```tf
|
||||||
module "mux" {
|
module "mux" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder/mux/coder"
|
source = "registry.coder.com/coder/mux/coder"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
port = 8080
|
port = 8080
|
||||||
}
|
}
|
||||||
@ -89,7 +104,7 @@ Run an existing copy of Mux if found, otherwise install from npm:
|
|||||||
module "mux" {
|
module "mux" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder/mux/coder"
|
source = "registry.coder.com/coder/mux/coder"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
use_cached = true
|
use_cached = true
|
||||||
}
|
}
|
||||||
@ -103,7 +118,7 @@ Run without installing from the network (requires Mux to be pre-installed):
|
|||||||
module "mux" {
|
module "mux" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder/mux/coder"
|
source = "registry.coder.com/coder/mux/coder"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
agent_id = coder_agent.main.id
|
agent_id = coder_agent.main.id
|
||||||
install = false
|
install = false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
import { describe, expect, it } from "bun:test";
|
import { describe, expect, it } from "bun:test";
|
||||||
import {
|
import {
|
||||||
executeScriptInContainer,
|
executeScriptInContainer,
|
||||||
|
execContainer,
|
||||||
|
findResourceInstance,
|
||||||
|
readFileContainer,
|
||||||
|
removeContainer,
|
||||||
|
runContainer,
|
||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
@ -40,6 +45,57 @@ describe("mux", async () => {
|
|||||||
}
|
}
|
||||||
}, 60000);
|
}, 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 () => {
|
it("runs with npm present", async () => {
|
||||||
const state = await runTerraformApply(import.meta.dir, {
|
const state = await runTerraformApply(import.meta.dir, {
|
||||||
agent_id: "foo",
|
agent_id: "foo",
|
||||||
|
|||||||
@ -55,6 +55,12 @@ variable "add-project" {
|
|||||||
default = null
|
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" {
|
variable "install_version" {
|
||||||
type = string
|
type = string
|
||||||
description = "The version or dist-tag of Mux to install."
|
description = "The version or dist-tag of Mux to install."
|
||||||
@ -142,6 +148,7 @@ resource "coder_script" "mux" {
|
|||||||
PORT : var.port,
|
PORT : var.port,
|
||||||
LOG_PATH : var.log_path,
|
LOG_PATH : var.log_path,
|
||||||
ADD_PROJECT : var.add-project == null ? "" : var.add-project,
|
ADD_PROJECT : var.add-project == null ? "" : var.add-project,
|
||||||
|
ADDITIONAL_ARGUMENTS : var.additional_arguments,
|
||||||
INSTALL_PREFIX : var.install_prefix,
|
INSTALL_PREFIX : var.install_prefix,
|
||||||
OFFLINE : !var.install,
|
OFFLINE : !var.install,
|
||||||
USE_CACHED : var.use_cached,
|
USE_CACHED : var.use_cached,
|
||||||
|
|||||||
@ -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" {
|
run "custom_version" {
|
||||||
command = plan
|
command = plan
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,22 @@ function run_mux() {
|
|||||||
if [ -n "${ADD_PROJECT}" ]; then
|
if [ -n "${ADD_PROJECT}" ]; then
|
||||||
set -- "$@" --add-project "${ADD_PROJECT}"
|
set -- "$@" --add-project "${ADD_PROJECT}"
|
||||||
fi
|
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 "🚀 Starting mux server on port $port_value..."
|
||||||
echo "Check logs at ${LOG_PATH}!"
|
echo "Check logs at ${LOG_PATH}!"
|
||||||
MUX_SERVER_AUTH_TOKEN="$auth_token_value" PORT="$port_value" "$MUX_BINARY" "$@" > "${LOG_PATH}" 2>&1 &
|
MUX_SERVER_AUTH_TOKEN="$auth_token_value" PORT="$port_value" "$MUX_BINARY" "$@" > "${LOG_PATH}" 2>&1 &
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user