Compare commits

...

4 Commits

Author SHA1 Message Date
Danielle Maywood ff02249128 refactor(coder/claude-code): support terraform provider coder 2.12.0 (#488)
## Description

Updates the module to use the new version of the agentapi module for the
upcoming Coder 2.28 release

## Type of Change

- [ ] New module
- [ ] New template
- [ ] Bug fix
- [x] Feature/enhancement
- [ ] Documentation
- [ ] Other

## Module Information

**Path:** `registry/coder/modules/claude-code`  
**New version:** `v4.0.0`  
**Breaking change:** [x] Yes [ ] No

## Testing & Validation

- [x] Tests pass (`bun test`)
- [x] Code formatted (`bun fmt`)
- [ ] Changes tested locally

## Related Issues

- https://github.com/coder/internal/issues/1065

## Related PRs

- https://github.com/coder/registry/pull/485
- https://github.com/coder/registry/pull/497

Co-authored-by: Cian Johnston <cian@coder.com>
2025-11-03 16:26:08 +00:00
djarbz 4a11b06cba Fix/djarbz copyparty argcommas (#516)
## Description

I discovered that if we included a comma inside an argument that bash
would split it out as a separate argument.
I added a test to verify.
I also cleaned up some log formatting.

## Type of Change

- [ ] New module
- [ ] New template
- [x] Bug fix
- [ ] Feature/enhancement
- [ ] Documentation
- [ ] Other

## Module Information

<!-- Delete this section if not applicable -->

**Path:** `registry/djarbz/modules/copyparty`  
**New version:** `v1.0.1`  
**Breaking change:** [ ] Yes [x] No

## Testing & Validation

- [N/A] Tests pass (`bun test`)
- [x] Code formatted (`bun fmt`)
- [x] Changes tested locally

## Related Issues

None

---------

Co-authored-by: DevCats <christofer@coder.com>
2025-10-31 10:04:07 -05:00
DevCats 925c71e641 fix: improve version extraction logic to prevent false positives (#511)
## Description

Makes the version extract and replace logic more specific so it wont
replace any field that does is not specifically `field` under the
detected modules.
<!-- Briefly describe what this PR does and why -->

## Type of Change

- [ ] New module
- [ ] New template
- [X] Bug fix
- [ ] Feature/enhancement
- [ ] Documentation
- [ ] Other

## Testing & Validation

- [X] Tests pass (`bun test`)
- [X] Code formatted (`bun fmt`)
- [X] Changes tested locally

## Related Issues

#510 
<!-- Link related issues or write "None" if not applicable -->

---------

Co-authored-by: Atif Ali <atif@coder.com>
2025-10-31 07:45:52 -05:00
Atif Ali 5450113939 fix(coder/modules/claude-code): move set -euo pipefail after sourcing .bashrc (#520)
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
2025-10-30 20:17:33 +05:00
9 changed files with 75 additions and 44 deletions
+8 -5
View File
@@ -77,16 +77,19 @@ update_readme_version() {
in_target_module = 0
}
}
/version.*=.*"/ {
/^[[:space:]]*version[[:space:]]*=/ {
if (in_target_module) {
gsub(/version[[:space:]]*=[[:space:]]*"[^"]*"/, "version = \"" new_version "\"")
match($0, /^[[:space]]*/
indent = substr($0, 1, RLENGTH)
print indent "version = \"" new_version "\""
in_target_module = 0
next
}
}
{ print }
' "$readme_path" > "${readme_path}.tmp" && mv "${readme_path}.tmp" "$readme_path"
return 0
elif grep -q 'version\s*=\s*"' "$readme_path"; then
elif grep -q '^[[:space:]]*version[[:space:]]*=' "$readme_path"; then
echo "⚠️ Found version references but no module source match for $namespace/$module_name"
return 1
fi
@@ -148,9 +151,9 @@ main() {
local current_version
if [ -z "$latest_tag" ]; then
if [ -f "$readme_path" ] && grep -q 'version\s*=\s*"' "$readme_path"; then
if [ -f "$readme_path" ] && grep -q '^[[:space:]]*version[[:space:]]*=' "$readme_path"; then
local readme_version
readme_version=$(grep 'version\s*=\s*"' "$readme_path" | head -1 | sed 's/.*version\s*=\s*"\([^"]*\)".*/\1/')
readme_version=$(awk '/^[[:space:]]*version[[:space:]]*=/ { match($0, /"[^"]*"/); print substr($0, RSTART+1, RLENGTH-2); exit }' "$readme_path")
echo "No git tag found, but README shows version: $readme_version"
if ! validate_version "$readme_version"; then
+6 -6
View File
@@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "3.4.3"
version = "4.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder/project"
claude_api_key = "xxxx-xxxxx-xxxx"
@@ -70,7 +70,7 @@ data "coder_parameter" "ai_prompt" {
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "3.4.3"
version = "4.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder/project"
@@ -106,7 +106,7 @@ Run and configure Claude Code as a standalone CLI in your workspace.
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "3.4.3"
version = "4.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
install_claude_code = true
@@ -129,7 +129,7 @@ variable "claude_code_oauth_token" {
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "3.4.3"
version = "4.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder/project"
claude_code_oauth_token = var.claude_code_oauth_token
@@ -202,7 +202,7 @@ resource "coder_env" "bedrock_api_key" {
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "3.4.3"
version = "4.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder/project"
model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
@@ -259,7 +259,7 @@ resource "coder_env" "google_application_credentials" {
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "3.4.3"
version = "4.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder/project"
model = "claude-sonnet-4@20250514"
+7 -4
View File
@@ -4,7 +4,7 @@ terraform {
required_providers {
coder = {
source = "coder/coder"
version = ">= 2.7"
version = ">= 2.12"
}
}
}
@@ -270,7 +270,7 @@ resource "coder_env" "claude_api_key" {
locals {
# we have to trim the slash because otherwise coder exp mcp will
# set up an invalid claude config
# set up an invalid claude config
workdir = trimsuffix(var.workdir, "/")
app_slug = "ccw"
install_script = file("${path.module}/scripts/install.sh")
@@ -313,9 +313,8 @@ locals {
}
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "1.2.0"
version = "2.0.0"
agent_id = var.agent_id
web_app_slug = local.app_slug
@@ -379,3 +378,7 @@ module "agentapi" {
/tmp/install.sh
EOT
}
output "task_app_id" {
value = module.agentapi.task_app_id
}
@@ -1,10 +1,12 @@
#!/bin/bash
set -euo pipefail
if [ -f "$HOME/.bashrc" ]; then
source "$HOME"/.bashrc
fi
# Set strict error handling AFTER sourcing bashrc to avoid unbound variable errors from user dotfiles
set -euo pipefail
BOLD='\033[0;1m'
command_exists() {
@@ -1,9 +1,12 @@
#!/bin/bash
set -euo pipefail
if [ -f "$HOME/.bashrc" ]; then
source "$HOME"/.bashrc
fi
# Set strict error handling AFTER sourcing bashrc to avoid unbound variable errors from user dotfiles
set -euo pipefail
export PATH="$HOME/.local/bin:$PATH"
command_exists() {
+4 -4
View File
@@ -17,7 +17,7 @@ This module installs Copyparty, an alternative to Filebrowser.
module "copyparty" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/djarbz/copyparty/coder"
version = "1.0.0"
version = "1.0.1"
}
```
@@ -35,7 +35,7 @@ Some basic command line options:
module "copyparty" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/djarbz/copyparty/coder"
version = "1.0.0"
version = "1.0.1"
agent_id = coder_agent.example.id
arguments = [
"-v", "/home/coder/:/home:r", # Share home directory (read-only)
@@ -51,14 +51,14 @@ module "copyparty" {
module "copyparty" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/djarbz/copyparty/coder"
version = "1.0.0"
version = "1.0.1"
agent_id = coder_agent.example.id
subdomain = true
arguments = [
"-v", "/tmp:/tmp:r", # Share tmp directory (read-only)
"-v", "/home/coder/:/home:rw", # Share home directory (read-write)
"-v", "${local.root_dir}:/work:A:c,dotsrch", # Share work directory (All Perms)
"-e2dsa", # Enables general file indexing"
"-e2dsa", # Enables general file indexing
"--re-maxage", "900", # Rescan filesystem for changes every SEC
"--see-dots", # Show dotfiles by default if user has correct permissions on volume
"--xff-src=lan", # List of trusted reverse-proxy CIDRs (comma-separated) or `lan` for private IPs.
@@ -80,7 +80,7 @@ run "test_defaults" {
}
assert {
condition = strcontains(coder_script.copyparty.script, "IFS=',' read -r -a ARGUMENTS \u003c\u003c\u003c \"\"")
condition = strcontains(coder_script.copyparty.script, "ARGUMENTS=()")
error_message = "Script content does not reflect default empty arguments"
}
}
@@ -138,7 +138,7 @@ run "test_custom_values" {
}
assert {
condition = strcontains(coder_script.copyparty.script, "IFS=',' read -r -a ARGUMENTS \u003c\u003c\u003c \"--verbose,-v\"")
condition = strcontains(coder_script.copyparty.script, "ARGUMENTS=(\"--verbose\" \"-v\")")
error_message = "Script content does not reflect custom arguments"
}
@@ -179,3 +179,26 @@ run "test_invalid_share" {
var.share,
]
}
# --- Test Case 7: Comma in Arguments [Readme Example 2] ---
run "test_comma_args" {
# Arguments containing commas
variables {
agent_id = "example-agent-id"
arguments = [
"-v", "/tmp:/tmp:r", # Share tmp directory (read-only)
"-v", "/home/coder/:/home:rw", # Share home directory (read-write)
"-v", "/work:/work:A:c,dotsrch", # Share work directory (All Perms)
"-e2dsa", # Enables general file indexing
"--re-maxage", "900", # Rescan filesystem for changes every SEC
"--see-dots", # Show dotfiles by default if user has correct permissions on volume
"--xff-src=lan", # List of trusted reverse-proxy CIDRs (comma-separated) or `lan` for private IPs.
"--rproxy", "1", # Which ip to associate clients with, index of X-FWD IP.
]
}
assert {
condition = strcontains(coder_script.copyparty.script, "ARGUMENTS=(\"-v\" \"/tmp:/tmp:r\" \"-v\" \"/home/coder/:/home:rw\" \"-v\" \"/work:/work:A:c,dotsrch\" \"-e2dsa\" \"--re-maxage\" \"900\" \"--see-dots\" \"--xff-src=lan\" \"--rproxy\" \"1\")")
error_message = "Script content does not reflect Readme Example #2 arguments with commas"
}
}
+1 -1
View File
@@ -129,7 +129,7 @@ resource "coder_script" "copyparty" {
LOG_PATH : var.log_path,
PORT : var.port,
PINNED_VERSION : var.pinned_version,
ARGUMENTS : join(",", var.arguments),
ARGUMENTS : join(" ", formatlist("\"%s\"", var.arguments)),
})
run_on_start = true
run_on_stop = false
+17 -20
View File
@@ -9,19 +9,16 @@ LOG_PATH="${LOG_PATH}"
PORT="${PORT}"
# Pinned version (e.g., v1.19.16); overrides latest release discovery if set
PINNED_VERSION="${PINNED_VERSION}"
# Custom CLI Arguments# The variable from Terraform is a single, comma-separated string.
# We need to split it into a proper bash array using the comma (,) as the delimiter.
IFS=',' read -r -a ARGUMENTS <<< "${ARGUMENTS}"
# Custom CLI Arguments
# The variable from Terraform is a series of quoted and space separated strings.
# We need to parse it into a proper bash array.
ARGUMENTS=(${ARGUMENTS})
# VARIABLE appears unused. Verify use (or export if used externally).
# shellcheck disable=SC2034
MODULE_NAME="Copyparty"
# VARIABLE appears unused. Verify use (or export if used externally).
# shellcheck disable=SC2034
BOLD='\033[0;1m'
printf '%sInstalling %s ...\n\n' "$${BOLD}" "$${MODULE_NAME}"
printf '\e[1mInstalling %s ...\e[0m\n' "$${MODULE_NAME}"
# Add code here
# Use variables from the templatefile function in main.tf
@@ -32,7 +29,7 @@ if ! command -v python3 &> /dev/null; then
printf "❌ Python3 could not be found. Please install it to continue.\n"
exit 1
fi
printf "✅ Python3 is installed.\n\n"
printf "✅ Python3 is installed.\n"
RELEASE_TO_INSTALL=""
# Install provided version to pin, otherwise discover latest github release from `https://github.com/9001/copyparty`.
@@ -44,7 +41,7 @@ if [[ -n "$${PINNED_VERSION}" ]]; then
exit 1
fi
RELEASE_TO_INSTALL="$${PINNED_VERSION}"
printf "✅ Using pinned version %s.\n\n" "$${RELEASE_TO_INSTALL}"
printf "✅ Using pinned version %s.\n" "$${RELEASE_TO_INSTALL}"
else
printf "🔎 Discovering latest release from GitHub...\n"
# Use curl to get the latest release tag from the GitHub API and sed to parse it
@@ -54,11 +51,11 @@ else
exit 1
fi
RELEASE_TO_INSTALL="$${LATEST_RELEASE}"
printf "🏷️ Latest release is %s.\n\n" "$${RELEASE_TO_INSTALL}"
printf "🏷️ Latest release is %s.\n" "$${RELEASE_TO_INSTALL}"
fi
# Download appropriate release version assets: `copyparty-sfx.py` and `helptext.html`.
printf "🚀 Downloading copyparty v%s...\n" "$${RELEASE_TO_INSTALL}"
printf "🚀 Downloading copyparty %s...\n" "$${RELEASE_TO_INSTALL}"
DOWNLOAD_URL="https://github.com/9001/copyparty/releases/download/$${RELEASE_TO_INSTALL}"
printf "⏬ Downloading copyparty-sfx.py...\n"
@@ -74,9 +71,9 @@ if ! curl -fsSL -o /tmp/helptext.html "$${DOWNLOAD_URL}/helptext.html"; then
fi
chmod +x /tmp/copyparty-sfx.py
printf "✅ Download complete.\n\n"
printf "✅ Download complete.\n"
printf "🥳 Installation complete!\n\n"
printf "🥳 Installation complete!\n"
# Build a clean, quoted string of the command for logging purposes only.
log_command="python3 /tmp/copyparty-sfx.py -p '$${PORT}'"
@@ -85,16 +82,16 @@ for arg in "$${ARGUMENTS[@]}"; do
log_command+=" '$${arg}'"
done
# Clear the log file and write the header and command string using printf.
# Dump the executing command to a tmp file for diagnostic review.
{
printf "=== Starting copyparty at %s ===\n" "$(date)"
printf "EXECUTING: %s\n" "$${log_command}"
} > "$${LOG_PATH}"
} > "/tmp/copyparty.cmd"
printf "👷 Starting %s in background...\n\n" "$${MODULE_NAME}"
printf "👷 Starting %s in background...\n" "$${MODULE_NAME}"
# Execute the actual command using the robust array expansion.
# Then, append its output (stdout and stderr) to the log file.
python3 /tmp/copyparty-sfx.py -p "$${PORT}" "$${ARGUMENTS[@]}" >> "$${LOG_PATH}" 2>&1 &
# Then, capture its output (stdout and stderr) to the log file.
python3 /tmp/copyparty-sfx.py -p "$${PORT}" "$${ARGUMENTS[@]}" > "$${LOG_PATH}" 2>&1 &
printf "✅ Service started. Check logs at %s\n\n" "$${LOG_PATH}"
printf "✅ Service started. Check logs at %s\n" "$${LOG_PATH}"