mirror of
https://github.com/coder/registry.git
synced 2026-06-03 04:58:15 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 83696cf201 | |||
| 4722df0169 | |||
| 45265a4504 | |||
| aa110281fe | |||
| d20d182cf1 | |||
| cbcb5b702b | |||
| d60546d4c4 | |||
| d401399c9f | |||
| ef2727f392 | |||
| 252d08ccec |
@@ -14,7 +14,7 @@ Automatically install [code-server](https://github.com/coder/code-server) in a w
|
||||
module "code-server" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/code-server/coder"
|
||||
version = "1.4.3"
|
||||
version = "1.4.2"
|
||||
agent_id = coder_agent.example.id
|
||||
}
|
||||
```
|
||||
@@ -29,7 +29,7 @@ module "code-server" {
|
||||
module "code-server" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/code-server/coder"
|
||||
version = "1.4.3"
|
||||
version = "1.4.2"
|
||||
agent_id = coder_agent.example.id
|
||||
install_version = "4.106.3"
|
||||
}
|
||||
@@ -43,7 +43,7 @@ Install the Dracula theme from [OpenVSX](https://open-vsx.org/):
|
||||
module "code-server" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/code-server/coder"
|
||||
version = "1.4.3"
|
||||
version = "1.4.2"
|
||||
agent_id = coder_agent.example.id
|
||||
extensions = [
|
||||
"dracula-theme.theme-dracula"
|
||||
@@ -61,7 +61,7 @@ Configure VS Code's [settings.json](https://code.visualstudio.com/docs/getstarte
|
||||
module "code-server" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/code-server/coder"
|
||||
version = "1.4.3"
|
||||
version = "1.4.2"
|
||||
agent_id = coder_agent.example.id
|
||||
extensions = ["dracula-theme.theme-dracula"]
|
||||
settings = {
|
||||
@@ -78,7 +78,7 @@ Just run code-server in the background, don't fetch it from GitHub:
|
||||
module "code-server" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/code-server/coder"
|
||||
version = "1.4.3"
|
||||
version = "1.4.2"
|
||||
agent_id = coder_agent.example.id
|
||||
extensions = ["dracula-theme.theme-dracula", "ms-azuretools.vscode-docker"]
|
||||
}
|
||||
@@ -92,7 +92,7 @@ You can pass additional command-line arguments to code-server using the `additio
|
||||
module "code-server" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/code-server/coder"
|
||||
version = "1.4.3"
|
||||
version = "1.4.2"
|
||||
agent_id = coder_agent.example.id
|
||||
additional_args = "--disable-workspace-trust"
|
||||
}
|
||||
@@ -108,7 +108,7 @@ Run an existing copy of code-server if found, otherwise download from GitHub:
|
||||
module "code-server" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/code-server/coder"
|
||||
version = "1.4.3"
|
||||
version = "1.4.2"
|
||||
agent_id = coder_agent.example.id
|
||||
use_cached = true
|
||||
extensions = ["dracula-theme.theme-dracula", "ms-azuretools.vscode-docker"]
|
||||
@@ -121,7 +121,7 @@ Just run code-server in the background, don't fetch it from GitHub:
|
||||
module "code-server" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/code-server/coder"
|
||||
version = "1.4.3"
|
||||
version = "1.4.2"
|
||||
agent_id = coder_agent.example.id
|
||||
offline = true
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ variable "settings" {
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "machine_settings" {
|
||||
variable "machine-settings" {
|
||||
type = any
|
||||
description = "A map of template level machine settings to apply to code-server. This will be overwritten at each container start."
|
||||
default = {}
|
||||
@@ -167,7 +167,7 @@ resource "coder_script" "code-server" {
|
||||
INSTALL_PREFIX : var.install_prefix,
|
||||
// This is necessary otherwise the quotes are stripped!
|
||||
SETTINGS : replace(jsonencode(var.settings), "\"", "\\\""),
|
||||
MACHINE_SETTINGS : replace(jsonencode(var.machine_settings), "\"", "\\\""),
|
||||
MACHINE_SETTINGS : replace(jsonencode(var.machine-settings), "\"", "\\\""),
|
||||
OFFLINE : var.offline,
|
||||
USE_CACHED : var.use_cached,
|
||||
USE_CACHED_EXTENSIONS : var.use_cached_extensions,
|
||||
|
||||
@@ -26,7 +26,6 @@ describe("dotfiles", async () => {
|
||||
"git@github.com:coder/dotfiles.git",
|
||||
"git://github.com/coder/dotfiles.git",
|
||||
"ssh://git@github.com/coder/dotfiles.git",
|
||||
"ssh://git@bitbucket.example.org:7999/~myusername/dotfiles.git",
|
||||
];
|
||||
for (const url of validUrls) {
|
||||
const state = await runTerraformApply(import.meta.dir, {
|
||||
@@ -56,6 +55,21 @@ describe("dotfiles", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("command uses bash for fish shell compatibility", async () => {
|
||||
const state = await runTerraformApply(import.meta.dir, {
|
||||
agent_id: "foo",
|
||||
manual_update: "true",
|
||||
dotfiles_uri: "https://github.com/test/dotfiles",
|
||||
});
|
||||
|
||||
const app = state.resources.find(
|
||||
(r) => r.type === "coder_app" && r.name === "dotfiles",
|
||||
);
|
||||
|
||||
expect(app).toBeDefined();
|
||||
expect(app?.instances[0]?.attributes?.command).toContain("/bin/bash -c");
|
||||
});
|
||||
|
||||
it("set custom order for coder_parameter", async () => {
|
||||
const order = 99;
|
||||
const state = await runTerraformApply(import.meta.dir, {
|
||||
|
||||
@@ -40,7 +40,7 @@ variable "default_dotfiles_uri" {
|
||||
validation {
|
||||
condition = (
|
||||
var.default_dotfiles_uri == "" ||
|
||||
can(regex("^(https?://|ssh://|git@|git://)[a-zA-Z0-9._/:@~-]+$", var.default_dotfiles_uri))
|
||||
can(regex("^(https?://|ssh://|git@|git://)[a-zA-Z0-9._/:@-]+$", var.default_dotfiles_uri))
|
||||
)
|
||||
error_message = "Must be a valid dotfiles repository URL (https, git@, or git://) without special characters."
|
||||
}
|
||||
@@ -55,7 +55,7 @@ variable "dotfiles_uri" {
|
||||
condition = (
|
||||
var.dotfiles_uri == null ||
|
||||
var.dotfiles_uri == "" ||
|
||||
can(regex("^(https?://|ssh://|git@|git://)[a-zA-Z0-9._/:@~-]+$", var.dotfiles_uri))
|
||||
can(regex("^(https?://|ssh://|git@|git://)[a-zA-Z0-9._/:@-]+$", var.dotfiles_uri))
|
||||
)
|
||||
error_message = "Must be a valid dotfiles repository URL (https, git@, or git://) without special characters."
|
||||
}
|
||||
@@ -102,7 +102,7 @@ data "coder_parameter" "dotfiles_uri" {
|
||||
icon = "/icon/dotfiles.svg"
|
||||
|
||||
validation {
|
||||
regex = "^$|^(https?://|ssh://|git@|git://)[a-zA-Z0-9._/:@~-]+$"
|
||||
regex = "^$|^(https?://|ssh://|git@|git://)[a-zA-Z0-9._/:@-]+$"
|
||||
error = "Must be a valid dotfiles repository URL (https, git@, or git://) without special characters."
|
||||
}
|
||||
}
|
||||
@@ -133,11 +133,11 @@ resource "coder_app" "dotfiles" {
|
||||
icon = "/icon/dotfiles.svg"
|
||||
order = var.order
|
||||
group = var.group
|
||||
command = templatefile("${path.module}/run.sh", {
|
||||
command = "/bin/bash -c \"$(echo ${base64encode(templatefile("${path.module}/run.sh", {
|
||||
DOTFILES_URI : local.dotfiles_uri,
|
||||
DOTFILES_USER : local.user,
|
||||
POST_CLONE_SCRIPT : local.encoded_post_clone_script
|
||||
})
|
||||
}))} | base64 -d)\""
|
||||
}
|
||||
|
||||
output "dotfiles_uri" {
|
||||
|
||||
@@ -8,13 +8,13 @@ tags: [ai, agents, development, multiplexer]
|
||||
|
||||
# Mux
|
||||
|
||||
Automatically install and run [Mux](https://github.com/coder/mux) in a Coder workspace. By default, the module auto-detects an available package manager (`npm`, `pnpm`, or `bun`) to install `mux@next` (with a fallback to downloading the npm tarball if none is found). You can also force a specific package manager via `package_manager` and point to a custom registry with `registry_url`. Mux is a desktop application for parallel agentic development that enables developers to run multiple AI agents simultaneously across isolated workspaces.
|
||||
Automatically install and run [Mux](https://github.com/coder/mux) in a Coder workspace. By default, the module installs `mux@next` from npm (with a fallback to downloading the npm tarball if npm is unavailable). Mux is a desktop application for parallel agentic development that enables developers to run multiple AI agents simultaneously across isolated workspaces.
|
||||
|
||||
```tf
|
||||
module "mux" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/mux/coder"
|
||||
version = "1.3.1"
|
||||
version = "1.1.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.3.1"
|
||||
version = "1.1.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.3.1"
|
||||
version = "1.1.0"
|
||||
agent_id = coder_agent.main.id
|
||||
# Default is "latest"; set to a specific version to pin
|
||||
install_version = "0.4.0"
|
||||
@@ -63,24 +63,9 @@ 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.3.1"
|
||||
version = "1.1.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.3.1"
|
||||
agent_id = coder_agent.main.id
|
||||
additional_arguments = "--open-mode pinned --add-project '/workspaces/my repo'"
|
||||
add-project = "/path/to/project"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -90,40 +75,12 @@ module "mux" {
|
||||
module "mux" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/mux/coder"
|
||||
version = "1.3.1"
|
||||
version = "1.1.0"
|
||||
agent_id = coder_agent.main.id
|
||||
port = 8080
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Package Manager
|
||||
|
||||
Force a specific package manager instead of auto-detection:
|
||||
|
||||
```tf
|
||||
module "mux" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/mux/coder"
|
||||
version = "1.3.1"
|
||||
agent_id = coder_agent.main.id
|
||||
package_manager = "pnpm" # or "npm", "bun"
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Registry
|
||||
|
||||
Use a private or mirrored npm registry:
|
||||
|
||||
```tf
|
||||
module "mux" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/mux/coder"
|
||||
version = "1.3.1"
|
||||
agent_id = coder_agent.main.id
|
||||
registry_url = "https://npm.pkg.github.com"
|
||||
}
|
||||
```
|
||||
|
||||
### Use Cached Installation
|
||||
|
||||
Run an existing copy of Mux if found, otherwise install from npm:
|
||||
@@ -132,7 +89,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.3.1"
|
||||
version = "1.1.0"
|
||||
agent_id = coder_agent.main.id
|
||||
use_cached = true
|
||||
}
|
||||
@@ -146,7 +103,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.3.1"
|
||||
version = "1.1.0"
|
||||
agent_id = coder_agent.main.id
|
||||
install = false
|
||||
}
|
||||
@@ -160,6 +117,4 @@ module "mux" {
|
||||
|
||||
- Mux is currently in preview and you may encounter bugs
|
||||
- Requires internet connectivity for agent operations (unless `install` is set to false)
|
||||
- Auto-detects `npm`, `pnpm`, or `bun` by default; set `package_manager` to force a specific one
|
||||
- Installs `mux@next` from the npm registry by default; set `registry_url` to use a private or mirrored registry
|
||||
- Falls back to a direct tarball download when no package manager is found
|
||||
- Installs `mux@next` from npm by default (falls back to the npm tarball if npm is unavailable)
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import {
|
||||
executeScriptInContainer,
|
||||
execContainer,
|
||||
findResourceInstance,
|
||||
readFileContainer,
|
||||
removeContainer,
|
||||
runContainer,
|
||||
runTerraformApply,
|
||||
runTerraformInit,
|
||||
testRequiredVariables,
|
||||
@@ -35,7 +30,7 @@ describe("mux", async () => {
|
||||
}
|
||||
expect(output.exitCode).toBe(0);
|
||||
const expectedLines = [
|
||||
"📥 No package manager found; downloading tarball from registry...",
|
||||
"📥 npm not found; downloading tarball from npm registry...",
|
||||
"🥳 mux has been installed in /tmp/mux",
|
||||
"🚀 Starting mux server on port 4000...",
|
||||
"Check logs at /tmp/mux.log!",
|
||||
@@ -45,57 +40,6 @@ 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",
|
||||
@@ -111,7 +55,7 @@ chmod +x /tmp/mux/mux`,
|
||||
expect(output.exitCode).toBe(0);
|
||||
const expectedLines = [
|
||||
"📦 Installing mux via npm into /tmp/mux...",
|
||||
"⏭️ Skipping lifecycle scripts with --ignore-scripts",
|
||||
"⏭️ Skipping npm lifecycle scripts with --ignore-scripts",
|
||||
"🥳 mux has been installed in /tmp/mux",
|
||||
"🚀 Starting mux server on port 4000...",
|
||||
"Check logs at /tmp/mux.log!",
|
||||
|
||||
@@ -49,41 +49,18 @@ variable "log_path" {
|
||||
default = "/tmp/mux.log"
|
||||
}
|
||||
|
||||
variable "add_project" {
|
||||
variable "add-project" {
|
||||
type = string
|
||||
description = "Optional path to add/open as a project in Mux on startup."
|
||||
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."
|
||||
default = "next"
|
||||
}
|
||||
|
||||
variable "package_manager" {
|
||||
type = string
|
||||
description = "Package manager to install Mux. 'auto' detects npm, pnpm, or bun (falling back to tarball download). Set to 'npm', 'pnpm', or 'bun' to force a specific one."
|
||||
default = "auto"
|
||||
validation {
|
||||
condition = contains(["auto", "npm", "pnpm", "bun"], var.package_manager)
|
||||
error_message = "The 'package_manager' variable must be one of: 'auto', 'npm', 'pnpm', 'bun'."
|
||||
}
|
||||
}
|
||||
|
||||
variable "registry_url" {
|
||||
type = string
|
||||
description = "The npm-compatible registry URL to install Mux from. Override this for private registries or mirrors."
|
||||
default = "https://registry.npmjs.org"
|
||||
}
|
||||
|
||||
|
||||
variable "share" {
|
||||
type = string
|
||||
default = "owner"
|
||||
@@ -154,7 +131,6 @@ resource "random_password" "mux_auth_token" {
|
||||
|
||||
locals {
|
||||
mux_auth_token = random_password.mux_auth_token.result
|
||||
registry_url = trimsuffix(var.registry_url, "/")
|
||||
}
|
||||
|
||||
resource "coder_script" "mux" {
|
||||
@@ -165,14 +141,11 @@ resource "coder_script" "mux" {
|
||||
VERSION : var.install_version,
|
||||
PORT : var.port,
|
||||
LOG_PATH : var.log_path,
|
||||
ADD_PROJECT : var.add_project == null ? "" : var.add_project,
|
||||
ADDITIONAL_ARGUMENTS : var.additional_arguments,
|
||||
ADD_PROJECT : var.add-project == null ? "" : var.add-project,
|
||||
INSTALL_PREFIX : var.install_prefix,
|
||||
OFFLINE : !var.install,
|
||||
USE_CACHED : var.use_cached,
|
||||
AUTH_TOKEN : local.mux_auth_token,
|
||||
PACKAGE_MANAGER : var.package_manager,
|
||||
REGISTRY_URL : local.registry_url,
|
||||
})
|
||||
run_on_start = true
|
||||
|
||||
|
||||
@@ -79,20 +79,6 @@ 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
|
||||
|
||||
@@ -121,96 +107,3 @@ run "use_cached_only_success" {
|
||||
use_cached = true
|
||||
}
|
||||
}
|
||||
|
||||
# Custom package_manager should appear in generated script
|
||||
run "custom_package_manager_npm" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "foo"
|
||||
package_manager = "npm"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = strcontains(resource.coder_script.mux.script, "PM_CMD=\"npm\"")
|
||||
error_message = "mux script must set PM_CMD to the configured package manager"
|
||||
}
|
||||
}
|
||||
|
||||
run "custom_package_manager_pnpm" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "foo"
|
||||
package_manager = "pnpm"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = strcontains(resource.coder_script.mux.script, "PM_CMD=\"pnpm\"")
|
||||
error_message = "mux script must set PM_CMD to the configured package manager"
|
||||
}
|
||||
}
|
||||
|
||||
run "custom_package_manager_bun" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "foo"
|
||||
package_manager = "bun"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = strcontains(resource.coder_script.mux.script, "PM_CMD=\"bun\"")
|
||||
error_message = "mux script must set PM_CMD to the configured package manager"
|
||||
}
|
||||
}
|
||||
|
||||
# Invalid package_manager should fail validation
|
||||
run "invalid_package_manager" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "foo"
|
||||
package_manager = "yarn"
|
||||
}
|
||||
|
||||
expect_failures = [
|
||||
var.package_manager
|
||||
]
|
||||
}
|
||||
|
||||
# Custom registry_url should appear in generated script
|
||||
run "custom_registry_url" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "foo"
|
||||
registry_url = "https://npm.example.com"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = strcontains(resource.coder_script.mux.script, "https://npm.example.com")
|
||||
error_message = "mux script must use the configured registry URL"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = !strcontains(resource.coder_script.mux.script, "registry.npmjs.org")
|
||||
error_message = "mux script must not contain hardcoded registry.npmjs.org when custom registry is set"
|
||||
}
|
||||
}
|
||||
|
||||
# registry_url trailing slash should be stripped
|
||||
run "registry_url_trailing_slash" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "foo"
|
||||
registry_url = "https://npm.example.com/"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = strcontains(resource.coder_script.mux.script, "https://npm.example.com/mux/")
|
||||
error_message = "registry URL trailing slash must be stripped to avoid double slashes"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,22 +20,6 @@ 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 &
|
||||
@@ -54,7 +38,7 @@ fi
|
||||
|
||||
# If there is no cached install OR we don't want to use a cached install
|
||||
if [ ! -f "$MUX_BINARY" ] || [ "${USE_CACHED}" != true ]; then
|
||||
printf "$${BOLD}Installing mux...\n"
|
||||
printf "$${BOLD}Installing mux from npm...\n"
|
||||
|
||||
# Clean up from other install (in case install prefix changed).
|
||||
if [ -n "$CODER_SCRIPT_BIN_DIR" ] && [ -e "$CODER_SCRIPT_BIN_DIR/mux" ]; then
|
||||
@@ -63,76 +47,41 @@ if [ ! -f "$MUX_BINARY" ] || [ "${USE_CACHED}" != true ]; then
|
||||
|
||||
mkdir -p "$(dirname "$MUX_BINARY")"
|
||||
|
||||
# Determine which package manager to use
|
||||
PM_CMD=""
|
||||
if [ "${PACKAGE_MANAGER}" = "auto" ]; then
|
||||
for pm in npm pnpm bun; do
|
||||
if command -v "$pm" > /dev/null 2>&1; then
|
||||
PM_CMD="$pm"
|
||||
break
|
||||
fi
|
||||
done
|
||||
else
|
||||
PM_CMD="${PACKAGE_MANAGER}"
|
||||
if ! command -v "$PM_CMD" > /dev/null 2>&1; then
|
||||
echo "❌ Configured package manager '${PACKAGE_MANAGER}' not found on PATH"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$PM_CMD" ]; then
|
||||
echo "📦 Installing mux via $PM_CMD into ${INSTALL_PREFIX}..."
|
||||
if command -v npm > /dev/null 2>&1; then
|
||||
echo "📦 Installing mux via npm into ${INSTALL_PREFIX}..."
|
||||
NPM_WORKDIR="${INSTALL_PREFIX}/npm"
|
||||
mkdir -p "$NPM_WORKDIR"
|
||||
cd "$NPM_WORKDIR" || exit 1
|
||||
if [ ! -f package.json ]; then
|
||||
echo '{}' > package.json
|
||||
fi
|
||||
echo "⏭️ Skipping lifecycle scripts with --ignore-scripts"
|
||||
echo "⏭️ Skipping npm lifecycle scripts with --ignore-scripts"
|
||||
PKG="mux"
|
||||
if [ -z "${VERSION}" ] || [ "${VERSION}" = "latest" ]; then
|
||||
PKG_SPEC="$PKG@latest"
|
||||
else
|
||||
PKG_SPEC="$PKG@${VERSION}"
|
||||
fi
|
||||
INSTALL_OK=true
|
||||
case "$PM_CMD" in
|
||||
npm)
|
||||
if ! npm install --no-audit --no-fund --omit=dev --ignore-scripts --registry "${REGISTRY_URL}" "$PKG_SPEC"; then
|
||||
INSTALL_OK=false
|
||||
fi
|
||||
;;
|
||||
pnpm)
|
||||
if ! pnpm add --ignore-scripts --registry "${REGISTRY_URL}" "$PKG_SPEC"; then
|
||||
INSTALL_OK=false
|
||||
fi
|
||||
;;
|
||||
bun)
|
||||
if ! bun add --ignore-scripts --registry "${REGISTRY_URL}" "$PKG_SPEC"; then
|
||||
INSTALL_OK=false
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
if [ "$INSTALL_OK" != true ]; then
|
||||
echo "❌ Failed to install mux via $PM_CMD"
|
||||
if ! npm install --no-audit --no-fund --omit=dev --ignore-scripts "$PKG_SPEC"; then
|
||||
echo "❌ Failed to install mux via npm"
|
||||
exit 1
|
||||
fi
|
||||
# Determine the installed binary path
|
||||
BIN_DIR="$NPM_WORKDIR/node_modules/.bin"
|
||||
CANDIDATE="$BIN_DIR/mux"
|
||||
if [ ! -f "$CANDIDATE" ]; then
|
||||
echo "❌ Could not locate mux binary after $PM_CMD install"
|
||||
echo "❌ Could not locate mux binary after npm install"
|
||||
exit 1
|
||||
fi
|
||||
chmod +x "$CANDIDATE" || true
|
||||
ln -sf "$CANDIDATE" "$MUX_BINARY"
|
||||
else
|
||||
echo "📥 No package manager found; downloading tarball from registry..."
|
||||
echo "📥 npm not found; downloading tarball from npm registry..."
|
||||
VERSION_TO_USE="${VERSION}"
|
||||
if [ -z "$VERSION_TO_USE" ]; then
|
||||
VERSION_TO_USE="next"
|
||||
fi
|
||||
META_URL="${REGISTRY_URL}/mux/$VERSION_TO_USE"
|
||||
META_URL="https://registry.npmjs.org/mux/$VERSION_TO_USE"
|
||||
META_JSON="$(curl -fsSL "$META_URL" || true)"
|
||||
if [ -z "$META_JSON" ]; then
|
||||
echo "❌ Failed to fetch npm metadata: $META_URL"
|
||||
@@ -171,7 +120,7 @@ if [ ! -f "$MUX_BINARY" ] || [ "${USE_CACHED}" != true ]; then
|
||||
echo "❌ Could not determine version for mux"
|
||||
exit 1
|
||||
fi
|
||||
TARBALL_URL="${REGISTRY_URL}/mux/-/mux-$VERSION_TO_USE.tgz"
|
||||
TARBALL_URL="https://registry.npmjs.org/mux/-/mux-$VERSION_TO_USE.tgz"
|
||||
fi
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
TAR_PATH="$TMP_DIR/mux.tgz"
|
||||
|
||||
@@ -11,34 +11,6 @@ set -euo pipefail
|
||||
#
|
||||
# This script only validates changed modules. Documentation and template changes are ignored.
|
||||
|
||||
# Validates that Terraform variable names use underscores (snake_case) instead
|
||||
# of hyphens. Hyphens are technically valid but deprecated and non-idiomatic.
|
||||
# See: https://developer.hashicorp.com/terraform/language/values/variables
|
||||
validate_variable_names() {
|
||||
local dir="$1"
|
||||
local found_issues=0
|
||||
|
||||
while IFS= read -r tf_file; do
|
||||
while IFS= read -r match; do
|
||||
local line_num
|
||||
line_num=$(echo "$match" | cut -d: -f1)
|
||||
local line_content
|
||||
line_content=$(echo "$match" | cut -d: -f2-)
|
||||
local var_name
|
||||
var_name=$(echo "$line_content" | sed -n 's/.*variable "\([^"]*\)".*/\1/p')
|
||||
|
||||
if [[ -n "$var_name" ]]; then
|
||||
echo " ERROR: $tf_file:$line_num"
|
||||
echo " Variable \"$var_name\" contains a hyphen."
|
||||
echo " Rename to \"${var_name//-/_}\" (use underscores instead of hyphens)."
|
||||
found_issues=$((found_issues + 1))
|
||||
fi
|
||||
done < <(grep -n 'variable "[^"]*-[^"]*"' "$tf_file" 2> /dev/null || true)
|
||||
done < <(find "$dir" -name '*.tf' -type f | sort)
|
||||
|
||||
return "$found_issues"
|
||||
}
|
||||
|
||||
validate_terraform_directory() {
|
||||
local dir="$1"
|
||||
echo "Running \`terraform validate\` in $dir"
|
||||
@@ -119,16 +91,6 @@ main() {
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "==> Validating Terraform variable names use snake_case..."
|
||||
for dir in $subdirs; do
|
||||
if test -f "$dir/main.tf"; then
|
||||
if ! validate_variable_names "$dir"; then
|
||||
status=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
exit $status
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user