diff --git a/.github/scripts/version-bump.sh b/.github/scripts/version-bump.sh index f84a7f894..ec078fccd 100755 --- a/.github/scripts/version-bump.sh +++ b/.github/scripts/version-bump.sh @@ -1,14 +1,18 @@ #!/bin/bash # Version Bump Script -# Usage: ./version-bump.sh [base_ref] +# Usage: ./version-bump.sh [--ci] [base_ref] +# --ci: CI mode - run bump, check for changes, exit 1 if changes needed # bump_type: patch, minor, or major # base_ref: base reference for diff (default: origin/main) set -euo pipefail +CI_MODE=false + usage() { - echo "Usage: $0 [base_ref]" + echo "Usage: $0 [--ci] [base_ref]" + echo " --ci: CI mode - validates versions are already bumped (exits 1 if not)" echo " bump_type: patch, minor, or major" echo " base_ref: base reference for diff (default: origin/main)" echo "" @@ -16,6 +20,7 @@ usage() { echo " $0 patch # Update versions with patch bump" echo " $0 minor # Update versions with minor bump" echo " $0 major # Update versions with major bump" + echo " $0 --ci patch # CI check: verify patch bump has been applied" exit 1 } @@ -85,7 +90,7 @@ update_readme_version() { in_module_block = 0 if (module_has_target_source) { num_lines = split(module_content, lines, "\n") - for (i = 1; i <= num_lines; i++) { + for (i = 1; i < num_lines; i++) { line = lines[i] if (line ~ /^[[:space:]]*version[[:space:]]*=/) { match(line, /^[[:space:]]*/) @@ -115,6 +120,11 @@ update_readme_version() { } main() { + if [ "${1:-}" = "--ci" ]; then + CI_MODE=true + shift + fi + if [ $# -lt 1 ] || [ $# -gt 2 ]; then usage fi @@ -152,6 +162,8 @@ main() { local untagged_modules="" local has_changes=false + declare -a modified_readme_files=() + while IFS= read -r module_path; do if [ -z "$module_path" ]; then continue; fi @@ -202,6 +214,7 @@ main() { if update_readme_version "$readme_path" "$namespace" "$module_name" "$new_version"; then updated_readmes="$updated_readmes\n- $namespace/$module_name" + modified_readme_files+=("$readme_path") has_changes=true fi @@ -210,19 +223,22 @@ main() { done <<< "$modules" - # Always run formatter to ensure consistent formatting - echo "🔧 Running formatter to ensure consistent formatting..." - if command -v bun > /dev/null 2>&1; then - bun fmt > /dev/null 2>&1 || echo "⚠️ Warning: bun fmt failed, but continuing..." - else - echo "⚠️ Warning: bun not found, skipping formatting" + if [ ${#modified_readme_files[@]} -gt 0 ]; then + echo "🔧 Formatting modified README files..." + if command -v bun > /dev/null 2>&1; then + for readme_file in "${modified_readme_files[@]}"; do + bun run prettier --write "$readme_file" 2> /dev/null || true + done + else + echo "⚠️ Warning: bun not found, skipping formatting" + fi + echo "" fi - echo "" echo "📋 Summary:" echo "Bump Type: $bump_type" echo "" - echo "Modules Updated:" + echo "Modules Processed:" echo -e "$bumped_modules" echo "" @@ -239,6 +255,19 @@ main() { echo "" fi + if [ "$CI_MODE" = true ]; then + echo "🔍 Comparing files to committed versions..." + if git diff --quiet; then + echo "✅ PASS: All versions match - no changes needed" + exit 0 + else + echo "❌ FAIL: Module versions need to be updated" + echo "" + echo "Run './.github/scripts/version-bump.sh $bump_type' locally and commit the changes" + exit 1 + fi + fi + if [ "$has_changes" = true ]; then echo "✅ Version bump completed successfully!" echo "📝 README files have been updated with new versions." diff --git a/.github/typos.toml b/.github/typos.toml index 7ebdacef4..d0b1f2863 100644 --- a/.github/typos.toml +++ b/.github/typos.toml @@ -3,6 +3,7 @@ muc = "muc" # For Munich location code tyo = "tyo" # For Tokyo location code Hashi = "Hashi" HashiCorp = "HashiCorp" +hel = "hel" # For Helsinki location code mavrickrishi = "mavrickrishi" # Username mavrick = "mavrick" # Username inh = "inh" # Option in setpriv command diff --git a/.github/workflows/version-bump.yaml b/.github/workflows/version-bump.yaml index 7c51d0ef0..aff9e0a14 100644 --- a/.github/workflows/version-bump.yaml +++ b/.github/workflows/version-bump.yaml @@ -55,62 +55,35 @@ jobs: ;; esac - - name: Check version bump requirements - id: version-check - run: | - output_file=$(mktemp) - if ./.github/scripts/version-bump.sh "${{ steps.bump-type.outputs.type }}" origin/main > "$output_file" 2>&1; then - echo "Script completed successfully" - else - echo "Script failed" - cat "$output_file" - exit 1 - fi - - { - echo "output<> $GITHUB_OUTPUT - - cat "$output_file" - - if git diff --quiet; then - echo "versions_up_to_date=true" >> $GITHUB_OUTPUT - echo "✅ All module versions are already up to date" - else - echo "versions_up_to_date=false" >> $GITHUB_OUTPUT - echo "❌ Module versions need to be updated" - echo "Files that would be changed:" - git diff --name-only - echo "" - echo "Diff preview:" - git diff - - git checkout . - git clean -fd - - exit 1 - fi + - name: Check version bump + run: ./.github/scripts/version-bump.sh --ci "${{ steps.bump-type.outputs.type }}" origin/main - - name: Comment on PR - Failure - if: failure() && steps.version-check.outputs.versions_up_to_date == 'false' + - name: Comment on PR - Version bump required + if: failure() uses: actions/github-script@v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const output = `${{ steps.version-check.outputs.output }}`; const bumpType = `${{ steps.bump-type.outputs.type }}`; - let comment = `## ❌ Version Bump Validation Failed\n\n`; - comment += `**Bump Type:** \`${bumpType}\`\n\n`; - comment += `Module versions need to be updated but haven't been bumped yet.\n\n`; - comment += `**Required Actions:**\n`; - comment += `1. Run the version bump script locally: \`./.github/scripts/version-bump.sh ${bumpType}\`\n`; - comment += `2. Commit the changes: \`git add . && git commit -m "chore: bump module versions (${bumpType})"\`\n`; - comment += `3. Push the changes: \`git push\`\n\n`; - comment += `### Script Output:\n\`\`\`\n${output}\n\`\`\`\n\n`; - comment += `> Please update the module versions and push the changes to continue.`; + const comment = [ + '## Version Bump Required', + '', + 'One or more modules in this PR need their versions updated.', + '', + '**To fix this:**', + '1. Run the version bump script locally:', + ' ```bash', + ` ./.github/scripts/version-bump.sh ${bumpType}`, + ' ```', + '2. Commit the changes:', + ' ```bash', + ` git add . && git commit -m "chore: bump module versions (${bumpType})"`, + ' ```', + '3. Push your changes', + '', + 'The CI will automatically re-run once you push the updated versions.' + ].join('\n'); github.rest.issues.createComment({ issue_number: context.issue.number, diff --git a/.icons/hetzner.svg b/.icons/hetzner.svg new file mode 100644 index 000000000..74bb87c1a --- /dev/null +++ b/.icons/hetzner.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/registry/AJ0070/.images/avatar.jpeg b/registry/AJ0070/.images/avatar.jpeg index d47bfb80f..568decd28 100644 Binary files a/registry/AJ0070/.images/avatar.jpeg and b/registry/AJ0070/.images/avatar.jpeg differ diff --git a/registry/AJ0070/.images/avatar.png b/registry/AJ0070/.images/avatar.png index 8f0ff9177..c62ffc9db 100644 Binary files a/registry/AJ0070/.images/avatar.png and b/registry/AJ0070/.images/avatar.png differ diff --git a/registry/Excellencedev/.images/avatar.png b/registry/Excellencedev/.images/avatar.png new file mode 100644 index 000000000..e883c2cf2 Binary files /dev/null and b/registry/Excellencedev/.images/avatar.png differ diff --git a/registry/Excellencedev/README.md b/registry/Excellencedev/README.md new file mode 100644 index 000000000..13200875a --- /dev/null +++ b/registry/Excellencedev/README.md @@ -0,0 +1,7 @@ +--- +display_name: "Excellencedev" +bio: "Love to contribute" +avatar: "./.images/avatar.png" +support_email: "ademiluyisuccessandexcellence@gmail.com" +status: "community" +--- diff --git a/registry/Excellencedev/templates/hetzner-linux/README.md b/registry/Excellencedev/templates/hetzner-linux/README.md new file mode 100644 index 000000000..60575e679 --- /dev/null +++ b/registry/Excellencedev/templates/hetzner-linux/README.md @@ -0,0 +1,32 @@ +--- +display_name: Hetzner Cloud Server +description: Provision Hetzner Cloud servers as Coder workspaces +icon: ../../../../.icons/hetzner.svg +tags: [vm, linux, hetzner] +--- + +# Remote Development on Hetzner Cloud (Linux) + +Provision Hetzner Cloud servers as [Coder workspaces](https://coder.com/docs/workspaces) with this example template. + +> [!WARNING] +> **Workspace Storage Persistence:** When a workspace is stopped, the Hetzner Cloud server instance is stopped but your home volume and stored data persist. This means your files and data remain intact when you resume the workspace. + +> [!IMPORTANT] +> **Volume Management & Costs:** Hetzner Cloud volumes persist even when workspaces are stopped and will continue to incur storage costs (€0.0476/GB/month). Volumes are only automatically deleted when the workspace is completely deleted. Monitor your volumes in the [Hetzner Cloud Console](https://console.hetzner.cloud/) to manage costs effectively. + +## Prerequisites + +To deploy workspaces as Hetzner Cloud servers, you'll need: + +- Hetzner Cloud [API token](https://console.hetzner.cloud/projects) (create under Security > API Tokens) + +### Authentication + +This template assumes that the Coder Provisioner is run in an environment that is authenticated with Hetzner Cloud. + +Obtain a Hetzner Cloud API token from your [Hetzner Cloud Console](https://console.hetzner.cloud/projects) and provide it as the `hcloud_token` variable when creating a workspace. +For more authentication options, see the [Terraform provider documentation](https://registry.terraform.io/providers/hetznercloud/hcloud/latest/docs#authentication). + +> [!NOTE] +> This template is designed to be a starting point. Edit the Terraform to extend the template to support your use case. diff --git a/registry/Excellencedev/templates/hetzner-linux/cloud-config.yaml.tftpl b/registry/Excellencedev/templates/hetzner-linux/cloud-config.yaml.tftpl new file mode 100644 index 000000000..73d4897ab --- /dev/null +++ b/registry/Excellencedev/templates/hetzner-linux/cloud-config.yaml.tftpl @@ -0,0 +1,62 @@ +#cloud-config +users: + - name: ${username} + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + groups: sudo + shell: /bin/bash +packages: + - git +%{ if home_volume_label != "" ~} +fs_setup: + - device: /dev/disk/by-id/scsi-0HC_Volume_${volume_id} + filesystem: ext4 + label: ${home_volume_label} + overwrite: false # This prevents reformatting the disk on every boot + +mounts: + - [ + "/dev/disk/by-id/scsi-0HC_Volume_${volume_id}", + "/home/${username}", + ext4, + "defaults,uid=1000,gid=1000", + ] +%{ endif ~} +write_files: + - path: /opt/coder/init + permissions: "0755" + encoding: b64 + content: ${init_script} + - path: /etc/systemd/system/coder-agent.service + permissions: "0644" + content: | + [Unit] + Description=Coder Agent + After=network-online.target + Wants=network-online.target + + [Service] + User=${username} + ExecStart=/opt/coder/init + Environment=CODER_AGENT_TOKEN=${coder_agent_token} + Restart=always + RestartSec=10 + TimeoutStopSec=90 + KillMode=process + + OOMScoreAdjust=-900 + SyslogIdentifier=coder-agent + + [Install] + WantedBy=multi-user.target +runcmd: +%{ if home_volume_label != "" ~} + - | + until [ -e /dev/disk/by-id/scsi-0HC_Volume_${volume_id} ]; do + echo "Waiting for volume device..." + sleep 2 + done +%{ endif ~} + - mount -a + - chown ${username}:${username} /home/${username} + - systemctl enable coder-agent + - systemctl start coder-agent \ No newline at end of file diff --git "a/registry/Excellencedev/templates/hetzner-linux/hetzner_server_types.json\342\200\216" "b/registry/Excellencedev/templates/hetzner-linux/hetzner_server_types.json\342\200\216" new file mode 100644 index 000000000..6be0938a7 --- /dev/null +++ "b/registry/Excellencedev/templates/hetzner-linux/hetzner_server_types.json\342\200\216" @@ -0,0 +1,27 @@ +{ + "type_meta": { + "cx22": { "cores": 2, "memory_gb": 4, "disk_gb": 40 }, + "cx32": { "cores": 4, "memory_gb": 8, "disk_gb": 80 }, + "cx42": { "cores": 8, "memory_gb": 16, "disk_gb": 160 }, + "cx52": { "cores": 16, "memory_gb": 32, "disk_gb": 320 }, + "cpx11": { "cores": 2, "memory_gb": 2, "disk_gb": 40 }, + "cpx21": { "cores": 3, "memory_gb": 4, "disk_gb": 80 }, + "cpx31": { "cores": 4, "memory_gb": 8, "disk_gb": 160 }, + "cpx41": { "cores": 8, "memory_gb": 16, "disk_gb": 240 }, + "cpx51": { "cores": 16, "memory_gb": 32, "disk_gb": 360 }, + "ccx13": { "cores": 2, "memory_gb": 8, "disk_gb": 80 }, + "ccx23": { "cores": 4, "memory_gb": 16, "disk_gb": 160 }, + "ccx33": { "cores": 8, "memory_gb": 32, "disk_gb": 240 }, + "ccx43": { "cores": 16, "memory_gb": 64, "disk_gb": 360 }, + "ccx53": { "cores": 32, "memory_gb": 128, "disk_gb": 600 }, + "ccx63": { "cores": 48, "memory_gb": 192, "disk_gb": 960 } + }, + "availability": { + "fsn1": ["cpx11", "cpx21", "cpx31", "cpx41", "cpx51", "ccx13", "ccx23", "ccx33"], + "ash": ["cpx11", "cpx21", "cpx31", "cpx41", "cpx51", "ccx13", "ccx23", "ccx33"], + "hel1": ["cx22", "cpx11", "cpx21", "cpx31", "cpx41", "cpx51", "ccx13", "ccx23", "ccx33"], + "hil": ["cpx11", "cpx21", "cpx31", "cpx41", "ccx13", "ccx23", "ccx33"], + "nbg1": ["cx22", "cx32", "cx42", "cx52", "cpx11", "cpx21", "cpx31", "cpx41", "cpx51", "ccx13", "ccx23", "ccx33"], + "sin": ["cpx11", "cpx21", "cpx31", "cpx41", "cpx51", "ccx13", "ccx23", "ccx33"] + } +} diff --git a/registry/Excellencedev/templates/hetzner-linux/main.tf b/registry/Excellencedev/templates/hetzner-linux/main.tf new file mode 100644 index 000000000..4b71e4561 --- /dev/null +++ b/registry/Excellencedev/templates/hetzner-linux/main.tf @@ -0,0 +1,183 @@ +terraform { + required_providers { + hcloud = { + source = "hetznercloud/hcloud" + } + coder = { + source = "coder/coder" + } + } +} + +variable "hcloud_token" { + sensitive = true +} + +provider "hcloud" { + token = var.hcloud_token +} + +# Available locations: https://docs.hetzner.com/cloud/general/locations/ +data "coder_parameter" "hcloud_location" { + name = "hcloud_location" + display_name = "Hetzner Location" + description = "Select the Hetzner Cloud location for your workspace." + type = "string" + default = "fsn1" + option { + name = "DE Falkenstein" + value = "fsn1" + } + option { + name = "US Ashburn, VA" + value = "ash" + } + option { + name = "US Hillsboro, OR" + value = "hil" + } + option { + name = "SG Singapore" + value = "sin" + } + option { + name = "DE Nuremberg" + value = "nbg1" + } + option { + name = "FI Helsinki" + value = "hel1" + } +} + +# Available server types: https://docs.hetzner.com/cloud/servers/overview/ +data "coder_parameter" "hcloud_server_type" { + name = "hcloud_server_type" + display_name = "Hetzner Server Type" + description = "Select the Hetzner Cloud server type for your workspace." + type = "string" + + dynamic "option" { + for_each = local.hcloud_server_type_options_for_selected_location + content { + name = option.value.name + value = option.value.value + } + } +} + +resource "hcloud_server" "dev" { + count = data.coder_workspace.me.start_count + name = "coder-${data.coder_workspace.me.name}-dev" + image = "ubuntu-24.04" + server_type = data.coder_parameter.hcloud_server_type.value + location = data.coder_parameter.hcloud_location.value + public_net { + ipv4_enabled = true + ipv6_enabled = true + } + user_data = templatefile("cloud-config.yaml.tftpl", { + username = lower(data.coder_workspace_owner.me.name) + home_volume_label = "coder-${data.coder_workspace.me.id}-home" + volume_id = hcloud_volume.home_volume.id + init_script = base64encode(coder_agent.main.init_script) + coder_agent_token = coder_agent.main.token + }) + labels = { + "coder_workspace_name" = data.coder_workspace.me.name, + "coder_workspace_owner" = data.coder_workspace_owner.me.name, + } +} + +resource "hcloud_volume" "home_volume" { + name = "coder-${data.coder_workspace.me.id}-home" + size = data.coder_parameter.home_volume_size.value + location = data.coder_parameter.hcloud_location.value + labels = { + "coder_workspace_name" = data.coder_workspace.me.name, + "coder_workspace_owner" = data.coder_workspace_owner.me.name, + } +} + +resource "hcloud_volume_attachment" "home_volume_attachment" { + count = data.coder_workspace.me.start_count + volume_id = hcloud_volume.home_volume.id + server_id = hcloud_server.dev[count.index].id + automount = false +} + +locals { + username = lower(data.coder_workspace_owner.me.name) + + # Data source: local JSON file under the module directory + # Check API for latest server types & availability: https://docs.hetzner.cloud/reference/cloud#server-types + hcloud_server_types_data = jsondecode(file("${path.module}/hetzner_server_types.json")) + hcloud_server_type_meta = local.hcloud_server_types_data.type_meta + hcloud_server_types_by_location = local.hcloud_server_types_data.availability + + hcloud_server_type_options_for_selected_location = [ + for type_name in lookup(local.hcloud_server_types_by_location, data.coder_parameter.hcloud_location.value, []) : { + name = format("%s (%d vCPU, %dGB RAM, %dGB)", upper(type_name), local.hcloud_server_type_meta[type_name].cores, local.hcloud_server_type_meta[type_name].memory_gb, local.hcloud_server_type_meta[type_name].disk_gb) + value = type_name + } + ] +} + +data "coder_provisioner" "me" {} + +provider "coder" {} + +data "coder_workspace" "me" {} + +data "coder_workspace_owner" "me" {} + +data "coder_parameter" "home_volume_size" { + name = "home_volume_size" + display_name = "Home volume size" + description = "How large would you like your home volume to be (in GB)?" + type = "number" + default = "20" + mutable = false + validation { + min = 1 + max = 100 # Adjust the max size as needed + } +} + +resource "coder_agent" "main" { + os = "linux" + arch = "amd64" + + metadata { + key = "cpu" + display_name = "CPU Usage" + interval = 5 + timeout = 5 + script = "coder stat cpu" + } + metadata { + key = "memory" + display_name = "Memory Usage" + interval = 5 + timeout = 5 + script = "coder stat mem" + } + metadata { + key = "home" + display_name = "Home Usage" + interval = 600 # every 10 minutes + timeout = 30 # df can take a while on large filesystems + script = "coder stat disk --path /home/${local.username}" + } +} + +module "code-server" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/code-server/coder" + + # This ensures that the latest non-breaking version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production. + version = "~> 1.0" + + agent_id = coder_agent.main.id + order = 1 +} \ No newline at end of file diff --git a/registry/anomaly/.images/avatar.jpeg b/registry/anomaly/.images/avatar.jpeg index ca1072d8d..f823960a6 100644 Binary files a/registry/anomaly/.images/avatar.jpeg and b/registry/anomaly/.images/avatar.jpeg differ diff --git a/registry/coder-labs/.images/openwebui.png b/registry/coder-labs/.images/openwebui.png index 709d3e5f3..5f2c3fce0 100644 Binary files a/registry/coder-labs/.images/openwebui.png and b/registry/coder-labs/.images/openwebui.png differ diff --git a/registry/coder-labs/.images/perplexica.png b/registry/coder-labs/.images/perplexica.png index 840849377..9bb6ea282 100644 Binary files a/registry/coder-labs/.images/perplexica.png and b/registry/coder-labs/.images/perplexica.png differ diff --git a/registry/coder-labs/modules/auggie/README.md b/registry/coder-labs/modules/auggie/README.md index fd1632fe6..562bcbd42 100644 --- a/registry/coder-labs/modules/auggie/README.md +++ b/registry/coder-labs/modules/auggie/README.md @@ -13,7 +13,7 @@ Run Auggie CLI in your workspace to access Augment's AI coding assistant with ad ```tf module "auggie" { source = "registry.coder.com/coder-labs/auggie/coder" - version = "0.2.2" + version = "0.3.0" agent_id = coder_agent.example.id folder = "/home/coder/project" } @@ -47,7 +47,7 @@ module "coder-login" { module "auggie" { source = "registry.coder.com/coder-labs/auggie/coder" - version = "0.2.2" + version = "0.3.0" agent_id = coder_agent.example.id folder = "/home/coder/project" @@ -103,7 +103,7 @@ EOF ```tf module "auggie" { source = "registry.coder.com/coder-labs/auggie/coder" - version = "0.2.2" + version = "0.3.0" agent_id = coder_agent.example.id folder = "/home/coder/project" diff --git a/registry/coder-labs/modules/auggie/main.tf b/registry/coder-labs/modules/auggie/main.tf index d75b1229b..8ecb3ba0a 100644 --- a/registry/coder-labs/modules/auggie/main.tf +++ b/registry/coder-labs/modules/auggie/main.tf @@ -4,7 +4,7 @@ terraform { required_providers { coder = { source = "coder/coder" - version = ">= 2.7" + version = ">= 2.12" } } } @@ -179,7 +179,7 @@ locals { module "agentapi" { source = "registry.coder.com/coder/agentapi/coder" - version = "1.2.0" + version = "2.0.0" agent_id = var.agent_id folder = local.folder @@ -229,4 +229,8 @@ module "agentapi" { ARG_MCP_CONFIG='${var.mcp != null ? base64encode(replace(var.mcp, "'", "'\\''")) : ""}' \ /tmp/install.sh EOT -} \ No newline at end of file +} + +output "task_app_id" { + value = module.agentapi.task_app_id +} diff --git a/registry/coder-labs/modules/copilot/README.md b/registry/coder-labs/modules/copilot/README.md index 4ef2f6bc9..76b8f025c 100644 --- a/registry/coder-labs/modules/copilot/README.md +++ b/registry/coder-labs/modules/copilot/README.md @@ -13,7 +13,7 @@ Run [GitHub Copilot CLI](https://docs.github.com/copilot/concepts/agents/about-c ```tf module "copilot" { source = "registry.coder.com/coder-labs/copilot/coder" - version = "0.2.3" + version = "0.3.0" agent_id = coder_agent.example.id workdir = "/home/coder/projects" } @@ -51,7 +51,7 @@ data "coder_parameter" "ai_prompt" { module "copilot" { source = "registry.coder.com/coder-labs/copilot/coder" - version = "0.2.3" + version = "0.3.0" agent_id = coder_agent.example.id workdir = "/home/coder/projects" @@ -71,7 +71,7 @@ Customize tool permissions, MCP servers, and Copilot settings: ```tf module "copilot" { source = "registry.coder.com/coder-labs/copilot/coder" - version = "0.2.3" + version = "0.3.0" agent_id = coder_agent.example.id workdir = "/home/coder/projects" @@ -142,7 +142,7 @@ variable "github_token" { module "copilot" { source = "registry.coder.com/coder-labs/copilot/coder" - version = "0.2.3" + version = "0.3.0" agent_id = coder_agent.example.id workdir = "/home/coder/projects" github_token = var.github_token @@ -156,7 +156,7 @@ Run Copilot as a command-line tool without task reporting or web interface. This ```tf module "copilot" { source = "registry.coder.com/coder-labs/copilot/coder" - version = "0.2.3" + version = "0.3.0" agent_id = coder_agent.example.id workdir = "/home/coder" report_tasks = false diff --git a/registry/coder-labs/modules/copilot/main.tf b/registry/coder-labs/modules/copilot/main.tf index 41a83d532..218184d75 100644 --- a/registry/coder-labs/modules/copilot/main.tf +++ b/registry/coder-labs/modules/copilot/main.tf @@ -3,7 +3,7 @@ terraform { required_providers { coder = { source = "coder/coder" - version = ">= 2.7" + version = ">= 2.12" } } } @@ -242,7 +242,7 @@ resource "coder_env" "github_token" { module "agentapi" { source = "registry.coder.com/coder/agentapi/coder" - version = "1.2.0" + version = "2.0.0" agent_id = var.agent_id folder = local.workdir @@ -268,7 +268,7 @@ module "agentapi" { set -o pipefail echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh chmod +x /tmp/start.sh - + ARG_WORKDIR='${local.workdir}' \ ARG_AI_PROMPT='${base64encode(var.ai_prompt)}' \ ARG_SYSTEM_PROMPT='${base64encode(local.final_system_prompt)}' \ @@ -288,7 +288,7 @@ module "agentapi" { set -o pipefail echo -n '${base64encode(local.install_script)}' | base64 -d > /tmp/install.sh chmod +x /tmp/install.sh - + ARG_MCP_APP_STATUS_SLUG='${local.app_slug}' \ ARG_REPORT_TASKS='${var.report_tasks}' \ ARG_WORKDIR='${local.workdir}' \ @@ -299,4 +299,8 @@ module "agentapi" { ARG_COPILOT_MODEL='${var.copilot_model}' \ /tmp/install.sh EOT -} \ No newline at end of file +} + +output "task_app_id" { + value = module.agentapi.task_app_id +} diff --git a/registry/coder-labs/modules/cursor-cli/README.md b/registry/coder-labs/modules/cursor-cli/README.md index 6aa5ada35..6b7105301 100644 --- a/registry/coder-labs/modules/cursor-cli/README.md +++ b/registry/coder-labs/modules/cursor-cli/README.md @@ -13,7 +13,7 @@ Run the Cursor Agent CLI in your workspace for interactive coding assistance and ```tf module "cursor_cli" { source = "registry.coder.com/coder-labs/cursor-cli/coder" - version = "0.2.2" + version = "0.3.0" agent_id = coder_agent.main.id folder = "/home/coder/project" } @@ -42,7 +42,7 @@ module "coder-login" { module "cursor_cli" { source = "registry.coder.com/coder-labs/cursor-cli/coder" - version = "0.2.2" + version = "0.3.0" agent_id = coder_agent.main.id folder = "/home/coder/project" diff --git a/registry/coder-labs/modules/cursor-cli/main.test.ts b/registry/coder-labs/modules/cursor-cli/main.test.ts index 4b37b71c2..b7f9947cd 100644 --- a/registry/coder-labs/modules/cursor-cli/main.test.ts +++ b/registry/coder-labs/modules/cursor-cli/main.test.ts @@ -159,7 +159,7 @@ describe("cursor-cli", async () => { "-c", "cat /home/coder/.cursor-cli-module/agentapi-start.log || cat /home/coder/.cursor-cli-module/start.log || true", ]); - expect(startLog.stdout).toContain(`-m ${model}`); + expect(startLog.stdout).toContain(`--model ${model}`); expect(startLog.stdout).toContain("-f"); expect(startLog.stdout).toContain("test prompt"); }); diff --git a/registry/coder-labs/modules/cursor-cli/main.tf b/registry/coder-labs/modules/cursor-cli/main.tf index a57ad65c6..4363f3585 100644 --- a/registry/coder-labs/modules/cursor-cli/main.tf +++ b/registry/coder-labs/modules/cursor-cli/main.tf @@ -4,7 +4,7 @@ terraform { required_providers { coder = { source = "coder/coder" - version = ">= 2.7" + version = ">= 2.12" } } } @@ -132,7 +132,7 @@ resource "coder_env" "cursor_api_key" { module "agentapi" { source = "registry.coder.com/coder/agentapi/coder" - version = "1.2.0" + version = "2.0.0" agent_id = var.agent_id folder = local.folder @@ -179,3 +179,7 @@ module "agentapi" { /tmp/install.sh EOT } + +output "task_app_id" { + value = module.agentapi.task_app_id +} diff --git a/registry/coder-labs/modules/cursor-cli/scripts/start.sh b/registry/coder-labs/modules/cursor-cli/scripts/start.sh index 8a200a2f7..1e9ab59d3 100644 --- a/registry/coder-labs/modules/cursor-cli/scripts/start.sh +++ b/registry/coder-labs/modules/cursor-cli/scripts/start.sh @@ -50,7 +50,7 @@ ARGS=() # global flags if [ -n "$ARG_MODEL" ]; then - ARGS+=("-m" "$ARG_MODEL") + ARGS+=("--model" "$ARG_MODEL") fi if [ "$ARG_FORCE" = "true" ]; then ARGS+=("-f") diff --git a/registry/coder-labs/modules/gemini/README.md b/registry/coder-labs/modules/gemini/README.md index 8e44c89b9..d0a113a02 100644 --- a/registry/coder-labs/modules/gemini/README.md +++ b/registry/coder-labs/modules/gemini/README.md @@ -13,7 +13,7 @@ Run [Gemini CLI](https://github.com/google-gemini/gemini-cli) in your workspace ```tf module "gemini" { source = "registry.coder.com/coder-labs/gemini/coder" - version = "2.1.2" + version = "3.0.0" agent_id = coder_agent.main.id folder = "/home/coder/project" } @@ -46,7 +46,7 @@ variable "gemini_api_key" { module "gemini" { source = "registry.coder.com/coder-labs/gemini/coder" - version = "2.1.2" + version = "3.0.0" agent_id = coder_agent.main.id gemini_api_key = var.gemini_api_key folder = "/home/coder/project" @@ -94,7 +94,7 @@ data "coder_parameter" "ai_prompt" { module "gemini" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder-labs/gemini/coder" - version = "2.1.2" + version = "3.0.0" agent_id = coder_agent.main.id gemini_api_key = var.gemini_api_key gemini_model = "gemini-2.5-flash" @@ -118,7 +118,7 @@ For enterprise users who prefer Google's Vertex AI platform: ```tf module "gemini" { source = "registry.coder.com/coder-labs/gemini/coder" - version = "2.1.2" + version = "3.0.0" agent_id = coder_agent.main.id gemini_api_key = var.gemini_api_key folder = "/home/coder/project" diff --git a/registry/coder-labs/modules/gemini/main.tf b/registry/coder-labs/modules/gemini/main.tf index 7cc8ac04a..dbc81bc79 100644 --- a/registry/coder-labs/modules/gemini/main.tf +++ b/registry/coder-labs/modules/gemini/main.tf @@ -4,7 +4,7 @@ terraform { required_providers { coder = { source = "coder/coder" - version = ">= 2.7" + version = ">= 2.12" } } } @@ -177,7 +177,7 @@ EOT module "agentapi" { source = "registry.coder.com/coder/agentapi/coder" - version = "1.2.0" + version = "2.0.0" agent_id = var.agent_id folder = local.folder @@ -225,4 +225,8 @@ module "agentapi" { GEMINI_TASK_PROMPT='${var.task_prompt}' \ /tmp/start.sh EOT -} \ No newline at end of file +} + +output "task_app_id" { + value = module.agentapi.task_app_id +} diff --git a/registry/coder-labs/modules/sourcegraph-amp/README.md b/registry/coder-labs/modules/sourcegraph-amp/README.md index 6ba0576fa..0fabafe8b 100644 --- a/registry/coder-labs/modules/sourcegraph-amp/README.md +++ b/registry/coder-labs/modules/sourcegraph-amp/README.md @@ -13,7 +13,7 @@ Run [Amp CLI](https://ampcode.com/) in your workspace to access Sourcegraph's AI ```tf module "amp-cli" { source = "registry.coder.com/coder-labs/sourcegraph-amp/coder" - version = "2.1.0" + version = "3.0.0" agent_id = coder_agent.example.id amp_api_key = var.amp_api_key install_amp = true @@ -48,7 +48,7 @@ variable "amp_api_key" { module "amp-cli" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder-labs/sourcegraph-amp/coder" - amp_version = "2.1.0" + amp_version = "3.0.0" agent_id = coder_agent.example.id amp_api_key = var.amp_api_key # recommended for tasks usage workdir = "/home/coder/project" diff --git a/registry/coder-labs/modules/sourcegraph-amp/main.test.ts b/registry/coder-labs/modules/sourcegraph-amp/main.test.ts index dc5328a30..105f73868 100644 --- a/registry/coder-labs/modules/sourcegraph-amp/main.test.ts +++ b/registry/coder-labs/modules/sourcegraph-amp/main.test.ts @@ -110,6 +110,7 @@ describe("amp", async () => { const { id } = await setup({ skipAmpMock: true, moduleVariables: { + install_via_npm: "true", amp_version: "0.0.1755964909-g31e083", }, }); diff --git a/registry/coder-labs/modules/sourcegraph-amp/main.tf b/registry/coder-labs/modules/sourcegraph-amp/main.tf index 31433795d..13b25ec08 100644 --- a/registry/coder-labs/modules/sourcegraph-amp/main.tf +++ b/registry/coder-labs/modules/sourcegraph-amp/main.tf @@ -4,7 +4,7 @@ terraform { required_providers { coder = { source = "coder/coder" - version = ">= 2.7" + version = ">= 2.12" } external = { source = "hashicorp/external" @@ -140,7 +140,7 @@ variable "base_amp_config" { type = string description = <<-EOT Base AMP configuration in JSON format. Can be overridden to customize AMP settings. - + If empty, defaults enable thinking and todos for autonomous operation. Additional options include: - "amp.permissions": [] (tool permissions) - "amp.tools.stopTimeout": 600 (extend timeout for long operations) @@ -148,7 +148,7 @@ variable "base_amp_config" { - "amp.tools.disable": ["builtin:open"] (disable tools for containers) - "amp.git.commit.ampThread.enabled": true (link commits to threads) - "amp.git.commit.coauthor.enabled": true (add Amp as co-author) - + Reference: https://ampcode.com/manual EOT default = "" @@ -220,7 +220,7 @@ locals { module "agentapi" { source = "registry.coder.com/coder/agentapi/coder" - version = "1.2.0" + version = "2.0.0" agent_id = var.agent_id folder = local.workdir @@ -268,4 +268,6 @@ module "agentapi" { EOT } - +output "task_app_id" { + value = module.agentapi.task_app_id +} diff --git a/registry/coder/.images/amazon-q.png b/registry/coder/.images/amazon-q.png index 1221cc8b9..eae79efc0 100644 Binary files a/registry/coder/.images/amazon-q.png and b/registry/coder/.images/amazon-q.png differ diff --git a/registry/coder/.images/rstudio-server.png b/registry/coder/.images/rstudio-server.png index 71ecc4648..a75e76c6f 100644 Binary files a/registry/coder/.images/rstudio-server.png and b/registry/coder/.images/rstudio-server.png differ diff --git a/registry/coder/modules/claude-code/README.md b/registry/coder/modules/claude-code/README.md index 11172010f..0256e0298 100644 --- a/registry/coder/modules/claude-code/README.md +++ b/registry/coder/modules/claude-code/README.md @@ -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 = "4.2.7" + version = "4.2.8" agent_id = coder_agent.main.id workdir = "/home/coder/project" claude_api_key = "xxxx-xxxxx-xxxx" @@ -45,7 +45,7 @@ This example shows how to configure the Claude Code module to run the agent behi ```tf module "claude-code" { source = "dev.registry.coder.com/coder/claude-code/coder" - version = "4.2.7" + version = "4.2.8" agent_id = coder_agent.main.id workdir = "/home/coder/project" enable_boundary = true @@ -72,7 +72,7 @@ data "coder_parameter" "ai_prompt" { module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "4.2.7" + version = "4.2.8" agent_id = coder_agent.main.id workdir = "/home/coder/project" @@ -108,7 +108,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 = "4.2.7" + version = "4.2.8" agent_id = coder_agent.main.id workdir = "/home/coder/project" install_claude_code = true @@ -130,7 +130,7 @@ variable "claude_code_oauth_token" { module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "4.2.7" + version = "4.2.8" agent_id = coder_agent.main.id workdir = "/home/coder/project" claude_code_oauth_token = var.claude_code_oauth_token @@ -203,7 +203,7 @@ resource "coder_env" "bedrock_api_key" { module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "4.2.7" + version = "4.2.8" agent_id = coder_agent.main.id workdir = "/home/coder/project" model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0" @@ -260,7 +260,7 @@ resource "coder_env" "google_application_credentials" { module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "4.2.7" + version = "4.2.8" agent_id = coder_agent.main.id workdir = "/home/coder/project" model = "claude-sonnet-4@20250514" diff --git a/registry/coder/modules/claude-code/main.tf b/registry/coder/modules/claude-code/main.tf index 8531a9f9a..bd160dede 100644 --- a/registry/coder/modules/claude-code/main.tf +++ b/registry/coder/modules/claude-code/main.tf @@ -86,7 +86,7 @@ variable "install_agentapi" { variable "agentapi_version" { type = string description = "The version of AgentAPI to install." - default = "v0.11.4" + default = "v0.11.6" } variable "ai_prompt" { diff --git a/registry/coder/modules/code-server/README.md b/registry/coder/modules/code-server/README.md index fca189092..3312f979d 100644 --- a/registry/coder/modules/code-server/README.md +++ b/registry/coder/modules/code-server/README.md @@ -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.1" + version = "1.4.2" agent_id = coder_agent.example.id } ``` @@ -29,9 +29,9 @@ module "code-server" { module "code-server" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/code-server/coder" - version = "1.4.1" + version = "1.4.2" agent_id = coder_agent.example.id - install_version = "1.4.1" + 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.1" + 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.1" + 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.1" + 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.1" + 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.1" + version = "1.4.2" agent_id = coder_agent.example.id use_cached = true extensions = ["dracula-theme.theme-dracula", "ms-azuretools.vscode-docker"] @@ -121,8 +121,10 @@ 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.1" + version = "1.4.2" agent_id = coder_agent.example.id offline = true } ``` + +Some of the key differences between code-server and [VS Code Web](https://registry.coder.com/modules/coder/vscode-web) are listed in [docs](https://coder.com/docs/user-guides/workspace-access/code-server#differences-between-code-server-and-vs-code-web). diff --git a/registry/coder/modules/zed/README.md b/registry/coder/modules/zed/README.md index 60e72f6c3..cffb71448 100644 --- a/registry/coder/modules/zed/README.md +++ b/registry/coder/modules/zed/README.md @@ -19,7 +19,7 @@ Zed is a high-performance, multiplayer code editor from the creators of Atom and module "zed" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/zed/coder" - version = "1.1.3" + version = "1.1.4" agent_id = coder_agent.main.id } ``` @@ -32,7 +32,7 @@ module "zed" { module "zed" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/zed/coder" - version = "1.1.3" + version = "1.1.4" agent_id = coder_agent.main.id folder = "/home/coder/project" } @@ -44,7 +44,7 @@ module "zed" { module "zed" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/zed/coder" - version = "1.1.3" + version = "1.1.4" agent_id = coder_agent.main.id display_name = "Zed Editor" order = 1 @@ -57,7 +57,7 @@ module "zed" { module "zed" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/zed/coder" - version = "1.1.3" + version = "1.1.4" agent_id = coder_agent.main.id agent_name = coder_agent.example.name } @@ -73,7 +73,7 @@ You can declaratively set/merge settings with the `settings` input. Provide a JS module "zed" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/zed/coder" - version = "1.1.3" + version = "1.1.4" agent_id = coder_agent.main.id settings = jsonencode({ @@ -85,6 +85,7 @@ module "zed" { env = {} } + } }) } diff --git a/registry/coder/modules/zed/main.test.ts b/registry/coder/modules/zed/main.test.ts index 124137505..9a39b47b1 100644 --- a/registry/coder/modules/zed/main.test.ts +++ b/registry/coder/modules/zed/main.test.ts @@ -1,5 +1,9 @@ import { describe, expect, it } from "bun:test"; import { + execContainer, + findResourceInstance, + removeContainer, + runContainer, runTerraformApply, runTerraformInit, testRequiredVariables, @@ -12,66 +16,114 @@ describe("zed", async () => { agent_id: "foo", }); - it("default output", async () => { + it("creates settings file with correct JSON", async () => { + const settings = { + theme: "One Dark", + buffer_font_size: 14, + vim_mode: true, + telemetry: { + diagnostics: false, + metrics: false, + }, + // Test special characters: single quotes, backslashes, URLs + message: "it's working", + path: "C:\\Users\\test", + api_url: "https://api.example.com/v1?token=abc&user=test", + }; + const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", + settings: JSON.stringify(settings), }); - expect(state.outputs.zed_url.value).toBe("zed://ssh/default.coder"); - const coder_app = state.resources.find( - (res) => res.type === "coder_app" && res.name === "zed", - ); + const instance = findResourceInstance(state, "coder_script"); + const id = await runContainer("alpine:latest"); - expect(coder_app).not.toBeNull(); - expect(coder_app?.instances.length).toBe(1); - expect(coder_app?.instances[0].attributes.order).toBeNull(); - }); + try { + const result = await execContainer(id, ["sh", "-c", instance.script]); + expect(result.exitCode).toBe(0); - it("adds folder", async () => { - const state = await runTerraformApply(import.meta.dir, { - agent_id: "foo", - folder: "/foo/bar", - }); - expect(state.outputs.zed_url.value).toBe("zed://ssh/default.coder/foo/bar"); - }); + const catResult = await execContainer(id, [ + "cat", + "/root/.config/zed/settings.json", + ]); + expect(catResult.exitCode).toBe(0); - it("expect order to be set", async () => { - const state = await runTerraformApply(import.meta.dir, { - agent_id: "foo", - order: "22", - }); + const written = JSON.parse(catResult.stdout.trim()); + expect(written).toEqual(settings); + } finally { + await removeContainer(id); + } + }, 30000); - const coder_app = state.resources.find( - (res) => res.type === "coder_app" && res.name === "zed", - ); + it("merges settings with existing file when jq available", async () => { + const existingSettings = { + theme: "Solarized Dark", + vim_mode: true, + }; - expect(coder_app).not.toBeNull(); - expect(coder_app?.instances.length).toBe(1); - expect(coder_app?.instances[0].attributes.order).toBe(22); - }); + const newSettings = { + theme: "One Dark", + buffer_font_size: 14, + }; - it("expect display_name to be set", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", - display_name: "Custom Zed", + settings: JSON.stringify(newSettings), }); - const coder_app = state.resources.find( - (res) => res.type === "coder_app" && res.name === "zed", - ); + const instance = findResourceInstance(state, "coder_script"); + const id = await runContainer("alpine:latest"); - expect(coder_app).not.toBeNull(); - expect(coder_app?.instances.length).toBe(1); - expect(coder_app?.instances[0].attributes.display_name).toBe("Custom Zed"); - }); + try { + // Install jq and create existing settings file + await execContainer(id, ["apk", "add", "--no-cache", "jq"]); + await execContainer(id, ["mkdir", "-p", "/root/.config/zed"]); + await execContainer(id, [ + "sh", + "-c", + `echo '${JSON.stringify(existingSettings)}' > /root/.config/zed/settings.json`, + ]); + + const result = await execContainer(id, ["sh", "-c", instance.script]); + expect(result.exitCode).toBe(0); + + const catResult = await execContainer(id, [ + "cat", + "/root/.config/zed/settings.json", + ]); + expect(catResult.exitCode).toBe(0); + + const merged = JSON.parse(catResult.stdout.trim()); + expect(merged.theme).toBe("One Dark"); // overwritten + expect(merged.buffer_font_size).toBe(14); // added + expect(merged.vim_mode).toBe(true); // preserved + } finally { + await removeContainer(id); + } + }, 30000); - it("adds agent_name to hostname", async () => { + it("exits early with empty settings", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", - agent_name: "myagent", + settings: "", }); - expect(state.outputs.zed_url.value).toBe( - "zed://ssh/myagent.default.default.coder", - ); - }); + + const instance = findResourceInstance(state, "coder_script"); + const id = await runContainer("alpine:latest"); + + try { + const result = await execContainer(id, ["sh", "-c", instance.script]); + expect(result.exitCode).toBe(0); + + // Settings file should not be created + const catResult = await execContainer(id, [ + "cat", + "/root/.config/zed/settings.json", + ]); + expect(catResult.exitCode).not.toBe(0); + } finally { + await removeContainer(id); + } + }, 30000); }); diff --git a/registry/coder/modules/zed/main.tf b/registry/coder/modules/zed/main.tf index 745254be0..414c3c0e9 100644 --- a/registry/coder/modules/zed/main.tf +++ b/registry/coder/modules/zed/main.tf @@ -65,6 +65,7 @@ locals { owner_name = lower(data.coder_workspace_owner.me.name) agent_name = lower(var.agent_name) hostname = var.agent_name != "" ? "${local.agent_name}.${local.workspace_name}.${local.owner_name}.coder" : "${local.workspace_name}.coder" + settings_b64 = var.settings != "" ? base64encode(var.settings) : "" } resource "coder_script" "zed_settings" { @@ -75,7 +76,11 @@ resource "coder_script" "zed_settings" { script = <<-EOT #!/usr/bin/env bash set -eu - SETTINGS_JSON='${replace(var.settings, "\"", "\\\"")}' + SETTINGS_B64='${local.settings_b64}' + if [ -z "$${SETTINGS_B64}" ]; then + exit 0 + fi + SETTINGS_JSON="$(echo -n "$${SETTINGS_B64}" | base64 -d)" if [ -z "$${SETTINGS_JSON}" ] || [ "$${SETTINGS_JSON}" = "{}" ]; then exit 0 fi diff --git a/registry/coder/modules/zed/zed.tftest.hcl b/registry/coder/modules/zed/zed.tftest.hcl index 508b65503..339f6876f 100644 --- a/registry/coder/modules/zed/zed.tftest.hcl +++ b/registry/coder/modules/zed/zed.tftest.hcl @@ -5,6 +5,20 @@ run "default_output" { agent_id = "foo" } + override_data { + target = data.coder_workspace.me + values = { + name = "default" + } + } + + override_data { + target = data.coder_workspace_owner.me + values = { + name = "default" + } + } + assert { condition = output.zed_url == "zed://ssh/default.coder" error_message = "zed_url did not match expected default URL" @@ -19,6 +33,20 @@ run "adds_folder" { folder = "/foo/bar" } + override_data { + target = data.coder_workspace.me + values = { + name = "default" + } + } + + override_data { + target = data.coder_workspace_owner.me + values = { + name = "default" + } + } + assert { condition = output.zed_url == "zed://ssh/default.coder/foo/bar" error_message = "zed_url did not include provided folder path" @@ -33,8 +61,54 @@ run "adds_agent_name" { agent_name = "myagent" } + override_data { + target = data.coder_workspace.me + values = { + name = "default" + } + } + + override_data { + target = data.coder_workspace_owner.me + values = { + name = "default" + } + } + assert { condition = output.zed_url == "zed://ssh/myagent.default.default.coder" error_message = "zed_url did not include agent_name in hostname" } } + +run "settings_base64_encoding" { + command = apply + + variables { + agent_id = "foo" + settings = jsonencode({ + theme = "dark" + fontSize = 14 + }) + } + + # Verify settings are base64 encoded (eyJ = base64 prefix for JSON starting with {") + assert { + condition = can(regex("SETTINGS_B64='eyJ", coder_script.zed_settings.script)) + error_message = "settings should be base64 encoded in the script" + } +} + +run "empty_settings" { + command = apply + + variables { + agent_id = "foo" + settings = "" + } + + assert { + condition = can(regex("SETTINGS_B64=''", coder_script.zed_settings.script)) + error_message = "empty settings should result in empty SETTINGS_B64" + } +} diff --git a/registry/coder/templates/kubernetes-devcontainer/main.tf b/registry/coder/templates/kubernetes-devcontainer/main.tf index d391c75a5..b7c6153d2 100644 --- a/registry/coder/templates/kubernetes-devcontainer/main.tf +++ b/registry/coder/templates/kubernetes-devcontainer/main.tf @@ -139,7 +139,7 @@ variable "cache_repo_secret_name" { type = string } -data "kubernetes_secret" "cache_repo_dockerconfig_secret" { +data "kubernetes_secret_v1" "cache_repo_dockerconfig_secret" { count = var.cache_repo_secret_name == "" ? 0 : 1 metadata { name = var.cache_repo_secret_name @@ -166,7 +166,7 @@ locals { # Use the docker gateway if the access URL is 127.0.0.1 "ENVBUILDER_INIT_SCRIPT" : replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal"), "ENVBUILDER_FALLBACK_IMAGE" : data.coder_parameter.fallback_image.value, - "ENVBUILDER_DOCKER_CONFIG_BASE64" : base64encode(try(data.kubernetes_secret.cache_repo_dockerconfig_secret[0].data[".dockerconfigjson"], "")), + "ENVBUILDER_DOCKER_CONFIG_BASE64" : base64encode(try(data.kubernetes_secret_v1.cache_repo_dockerconfig_secret[0].data[".dockerconfigjson"], "")), "ENVBUILDER_PUSH_IMAGE" : var.cache_repo == "" ? "" : "true" # You may need to adjust this if you get an error regarding deleting files when building the workspace. # For example, when testing in KinD, it was necessary to set `/product_name` and `/product_uuid` in @@ -186,7 +186,7 @@ resource "envbuilder_cached_image" "cached" { insecure = var.insecure_cache_repo } -resource "kubernetes_persistent_volume_claim" "workspaces" { +resource "kubernetes_persistent_volume_claim_v1" "workspaces" { metadata { name = "coder-${lower(data.coder_workspace.me.id)}-workspaces" namespace = var.namespace @@ -217,10 +217,10 @@ resource "kubernetes_persistent_volume_claim" "workspaces" { } } -resource "kubernetes_deployment" "main" { +resource "kubernetes_deployment_v1" "main" { count = data.coder_workspace.me.start_count depends_on = [ - kubernetes_persistent_volume_claim.workspaces + kubernetes_persistent_volume_claim_v1.workspaces ] wait_for_rollout = false metadata { @@ -300,7 +300,7 @@ resource "kubernetes_deployment" "main" { volume { name = "workspaces" persistent_volume_claim { - claim_name = kubernetes_persistent_volume_claim.workspaces.metadata.0.name + claim_name = kubernetes_persistent_volume_claim_v1.workspaces.metadata.0.name read_only = false } } diff --git a/registry/coder/templates/kubernetes-envbox/main.tf b/registry/coder/templates/kubernetes-envbox/main.tf index 98543d9c5..b7693cbf2 100644 --- a/registry/coder/templates/kubernetes-envbox/main.tf +++ b/registry/coder/templates/kubernetes-envbox/main.tf @@ -106,22 +106,20 @@ module "code-server" { # This ensures that the latest non-breaking version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production. version = "~> 1.0" - agent_id = coder_agent.main.id - agent_name = "main" - order = 1 + agent_id = coder_agent.main.id + order = 1 } # See https://registry.coder.com/modules/coder/jetbrains module "jetbrains" { - count = data.coder_workspace.me.start_count - source = "registry.coder.com/modules/coder/jetbrains/coder" - version = "~> 1.0" - agent_id = coder_agent.main.id - agent_name = "main" - folder = "/home/coder" + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/jetbrains/coder" + version = "~> 1.0" + agent_id = coder_agent.main.id + folder = "/home/coder" } -resource "kubernetes_persistent_volume_claim" "home" { +resource "kubernetes_persistent_volume_claim_v1" "home" { metadata { name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-home" namespace = var.namespace @@ -137,7 +135,7 @@ resource "kubernetes_persistent_volume_claim" "home" { } } -resource "kubernetes_pod" "main" { +resource "kubernetes_pod_v1" "main" { count = data.coder_workspace.me.start_count metadata { @@ -284,7 +282,7 @@ resource "kubernetes_pod" "main" { volume { name = "home" persistent_volume_claim { - claim_name = kubernetes_persistent_volume_claim.home.metadata.0.name + claim_name = kubernetes_persistent_volume_claim_v1.home.metadata.0.name read_only = false } } diff --git a/registry/coder/templates/kubernetes/main.tf b/registry/coder/templates/kubernetes/main.tf index 7d7c0aa87..c324331fe 100644 --- a/registry/coder/templates/kubernetes/main.tf +++ b/registry/coder/templates/kubernetes/main.tf @@ -192,7 +192,7 @@ resource "coder_app" "code-server" { } } -resource "kubernetes_persistent_volume_claim" "home" { +resource "kubernetes_persistent_volume_claim_v1" "home" { metadata { name = "coder-${data.coder_workspace.me.id}-home" namespace = var.namespace @@ -222,10 +222,10 @@ resource "kubernetes_persistent_volume_claim" "home" { } } -resource "kubernetes_deployment" "main" { +resource "kubernetes_deployment_v1" "main" { count = data.coder_workspace.me.start_count depends_on = [ - kubernetes_persistent_volume_claim.home + kubernetes_persistent_volume_claim_v1.home ] wait_for_rollout = false metadata { @@ -316,7 +316,7 @@ resource "kubernetes_deployment" "main" { volume { name = "home" persistent_volume_claim { - claim_name = kubernetes_persistent_volume_claim.home.metadata.0.name + claim_name = kubernetes_persistent_volume_claim_v1.home.metadata.0.name read_only = false } } diff --git a/registry/cytoshahar/.images/avatar.jpeg b/registry/cytoshahar/.images/avatar.jpeg index 3e82d7637..245aa94ab 100644 Binary files a/registry/cytoshahar/.images/avatar.jpeg and b/registry/cytoshahar/.images/avatar.jpeg differ diff --git a/registry/djarbz/.images/avatar.png b/registry/djarbz/.images/avatar.png index f60192032..a5f427f9e 100644 Binary files a/registry/djarbz/.images/avatar.png and b/registry/djarbz/.images/avatar.png differ diff --git a/registry/harleylrn/.images/avatar.png b/registry/harleylrn/.images/avatar.png index 08e6f54e1..f99ed6086 100644 Binary files a/registry/harleylrn/.images/avatar.png and b/registry/harleylrn/.images/avatar.png differ diff --git a/registry/harleylrn/.images/kiro-cli.png b/registry/harleylrn/.images/kiro-cli.png index d325299b1..3f4d5cf60 100644 Binary files a/registry/harleylrn/.images/kiro-cli.png and b/registry/harleylrn/.images/kiro-cli.png differ diff --git a/registry/umair/.images/avatar.jpeg b/registry/umair/.images/avatar.jpeg index 6495306b9..5afcd7065 100644 Binary files a/registry/umair/.images/avatar.jpeg and b/registry/umair/.images/avatar.jpeg differ