From 6387957bbf8b83131bb813e2408d2fab499b5b42 Mon Sep 17 00:00:00 2001 From: Pranjal6955 Date: Sat, 12 Jul 2025 11:52:23 +0530 Subject: [PATCH 1/4] Proxmox VE Virtual Machine Template for Coder Workspaces --- registry/coder/templates/proxmox-vm/README.md | 102 +++++ registry/coder/templates/proxmox-vm/main.tf | 398 ++++++++++++++++++ 2 files changed, 500 insertions(+) create mode 100644 registry/coder/templates/proxmox-vm/README.md create mode 100644 registry/coder/templates/proxmox-vm/main.tf diff --git a/registry/coder/templates/proxmox-vm/README.md b/registry/coder/templates/proxmox-vm/README.md new file mode 100644 index 00000000..aba3b1d0 --- /dev/null +++ b/registry/coder/templates/proxmox-vm/README.md @@ -0,0 +1,102 @@ +--- +display_name: Proxmox VE (Virtual Machine) +description: Provision Proxmox VE VMs as Coder workspaces +icon: ../../../../.icons/proxmox.svg +maintainer_github: coder +verified: true +tags: [vm, linux, proxmox, qemu, kvm] +--- + +# Remote Development on Proxmox VE VMs + +Provision Proxmox VE virtual machines as [Coder workspaces](https://coder.com/docs/workspaces) with this example template. + +## Prerequisites + +### Infrastructure + +**Proxmox VE Cluster**: This template requires an existing Proxmox VE cluster (version 7.0 or higher recommended). + +**VM Template**: This template uses cloud-init enabled VM templates. You'll need to create a template with: +- Ubuntu 22.04 LTS (or your preferred Linux distribution) +- Cloud-init package installed +- Qemu Guest Agent installed + +**Network**: Ensure your Proxmox VE cluster has proper network configuration with DHCP or static IP assignment. + +### Authentication + +This template authenticates to Proxmox VE using API tokens. You'll need to: + +1. Create a user in Proxmox VE for Coder: + ```bash + pveum user add coder@pve + ``` + +2. Create an API token: + ```bash + pveum user token add coder@pve coder-token --privsep=0 + ``` + +3. Assign appropriate permissions: + ```bash + pveum role add CoderRole -privs "VM.Allocate,VM.Audit,VM.Clone,VM.Config.CDROM,VM.Config.CPU,VM.Config.Cloudinit,VM.Config.Disk,VM.Config.HWType,VM.Config.Memory,VM.Config.Network,VM.Config.Options,VM.Console,VM.Monitor,VM.PowerMgmt,VM.Snapshot,VM.Snapshot.Rollback,Datastore.Allocate,Datastore.AllocateSpace,Datastore.Audit,SDN.Use" + pveum aclmod / -user coder@pve -role CoderRole + ``` + +4. Set the following environment variables when running Coder: + ```bash + export PM_API_URL="https://your-proxmox-host:8006/api2/json" + export PM_API_TOKEN_ID="coder@pve!coder-token" + export PM_API_TOKEN_SECRET="your-api-token-secret" + ``` + +### Creating a VM Template + +1. Create a new VM in Proxmox VE +2. Install Ubuntu 22.04 LTS with cloud-init and qemu-guest-agent +3. Install development tools and configure the system +4. Shut down the VM and convert it to a template: + ```bash + qm template + ``` + +## Architecture + +This template provisions the following resources: + +- Proxmox VE VM (persistent, based on template) +- Cloud-init configuration for automated setup +- Network interface with DHCP or static IP +- Virtual disk storage + +The VM is persistent, meaning the full filesystem is preserved when the workspace restarts. The template uses cloud-init for initial configuration and the Qemu Guest Agent for better integration. + +> **Note** +> This template is designed to be a starting point! Edit the Terraform to extend the template to support your use case. + +## Features + +- **Resource Configuration**: Configurable CPU cores, memory, and disk size +- **Template-based**: Fast deployment using Proxmox VE VM templates +- **Cloud-init**: Automated initial configuration +- **Network Integration**: Automatic network configuration +- **Storage Flexibility**: Support for different storage backends +- **Agent Integration**: Full Coder agent support with metadata + +## Customization + +You can customize this template by: + +- Modifying VM resource parameters +- Changing the base template +- Adjusting network configuration +- Adding additional storage devices +- Configuring backup schedules + +## Troubleshooting + +- Ensure the Proxmox VE API is accessible from your Coder deployment +- Verify API token permissions are correctly configured +- Check that the VM template exists and has cloud-init enabled +- Ensure sufficient resources are available on the target node diff --git a/registry/coder/templates/proxmox-vm/main.tf b/registry/coder/templates/proxmox-vm/main.tf new file mode 100644 index 00000000..d2d65c40 --- /dev/null +++ b/registry/coder/templates/proxmox-vm/main.tf @@ -0,0 +1,398 @@ + +terraform { + required_providers { + coder = { + source = "coder/coder" + } + proxmox = { + source = "telmate/proxmox" + version = "~> 3.0" + } + cloudinit = { + source = "hashicorp/cloudinit" + } + } +} + +provider "coder" {} + +# Variables for Proxmox configuration +variable "proxmox_api_url" { + description = "Proxmox API URL (e.g., https://proxmox-server:8006/api2/json)" + type = string + default = "" +} + +variable "proxmox_node" { + description = "Proxmox VE node name where VMs will be created" + type = string + default = "pve" +} + +variable "vm_template" { + description = "VM template name to clone from" + type = string + default = "ubuntu-22.04-cloud" +} + +variable "storage" { + description = "Storage backend for VM disks" + type = string + default = "local-lvm" +} + +variable "network_bridge" { + description = "Network bridge to connect VMs to" + type = string + default = "vmbr0" +} + +# Proxmox provider configuration following official documentation +provider "proxmox" { + # API URL can be set via PM_API_URL environment variable or variable + pm_api_url = var.proxmox_api_url != "" ? var.proxmox_api_url : null + + # Authentication via API token (recommended): + # Set these environment variables: + # PM_API_TOKEN_ID='terraform-prov@pve!mytoken' + # PM_API_TOKEN_SECRET="your-token-secret" + # + # Or via username/password: + # PM_USER="terraform-prov@pve" + # PM_PASS="password" + + # TLS settings + pm_tls_insecure = true # Allow self-signed certificates (common in Proxmox) + + # Performance settings + pm_parallel = 2 # Allow 2 simultaneous operations + pm_timeout = 600 # 10 minute timeout for API calls + + # Optional: Enable debugging + pm_debug = false + + # Optional: Enable logging for troubleshooting + pm_log_enable = false + pm_log_file = "terraform-plugin-proxmox.log" + + # Minimum permission check + pm_minimum_permission_check = true +} + +data "coder_workspace" "me" {} +data "coder_workspace_owner" "me" {} + +# User parameters for VM configuration +data "coder_parameter" "cpu_cores" { + name = "cpu_cores" + display_name = "CPU Cores" + description = "Number of CPU cores for the VM" + type = "number" + default = 2 + icon = "/icon/memory.svg" + mutable = true + validation { + min = 1 + max = 16 + } +} + +data "coder_parameter" "memory" { + name = "memory" + display_name = "Memory (MB)" + description = "Amount of memory in MB" + type = "number" + default = 2048 + icon = "/icon/memory.svg" + mutable = true + validation { + min = 512 + max = 32768 + } +} + +data "coder_parameter" "disk_size" { + name = "disk_size" + display_name = "Disk Size (GB)" + description = "Size of the VM disk in GB" + type = "number" + default = 20 + icon = "/emojis/1f4be.png" + mutable = false + validation { + min = 10 + max = 500 + } +} + +data "coder_parameter" "proxmox_node" { + name = "proxmox_node" + display_name = "Proxmox Node" + description = "Which Proxmox node should host the VM?" + type = "string" + default = var.proxmox_node + mutable = false +} + +resource "coder_agent" "main" { + arch = "amd64" + os = "linux" + startup_script = <<-EOT + set -e + + # Wait for cloud-init to complete + cloud-init status --wait + + # Install development tools + sudo apt-get update + sudo apt-get install -y curl wget git build-essential + + # Add any commands that should be executed at workspace startup here + EOT + + 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 = "disk" + display_name = "Disk Usage" + interval = 600 + timeout = 30 + script = "coder stat disk --path /home/coder" + } +} + +# See https://registry.coder.com/modules/coder/code-server +module "code-server" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/code-server/coder" + + version = "~> 1.0" + agent_id = coder_agent.main.id + order = 1 +} + +# See https://registry.coder.com/modules/coder/jetbrains-gateway +module "jetbrains_gateway" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/jetbrains-gateway/coder" + + jetbrains_ides = ["IU", "PY", "WS", "PS", "RD", "CL", "GO", "RM"] + default = "IU" + folder = "/home/coder" + + version = "~> 1.0" + agent_id = coder_agent.main.id + agent_name = "main" + order = 2 +} + +locals { + vm_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}" + linux_user = "coder" + hostname = lower(data.coder_workspace.me.name) +} + +# Cloud-init configuration +data "cloudinit_config" "user_data" { + gzip = false + base64_encode = false + + part { + content_type = "text/cloud-config" + content = yamlencode({ + hostname = local.hostname + users = [{ + name = local.linux_user + groups = ["sudo", "docker"] + sudo = "ALL=(ALL) NOPASSWD:ALL" + shell = "/bin/bash" + lock_passwd = true + ssh_authorized_keys = [] + }] + packages = [ + "curl", + "wget", + "git", + "build-essential", + "qemu-guest-agent" + ] + runcmd = [ + "systemctl enable qemu-guest-agent", + "systemctl start qemu-guest-agent", + "usermod -aG docker ${local.linux_user}", + # Run Coder agent init script + "su - ${local.linux_user} -c '${coder_agent.main.init_script}'" + ] + write_files = [{ + path = "/etc/systemd/system/coder-agent.service" + content = <<-EOT + [Unit] + Description=Coder Agent + After=network-online.target + Wants=network-online.target + + [Service] + Type=notify + User=${local.linux_user} + WorkingDirectory=/home/${local.linux_user} + Environment=CODER_AGENT_TOKEN=${coder_agent.main.token} + ExecStart=${coder_agent.main.init_script} + Restart=always + RestartSec=10 + + [Install] + WantedBy=multi-user.target + EOT + }] + }) + } +} + +# Create cloud-init user data file +resource "proxmox_file" "cloud_init_user_data" { + content_type = "snippets" + datastore_id = "local" + node_name = data.coder_parameter.proxmox_node.value + + source_raw { + data = data.cloudinit_config.user_data.rendered + file_name = "coder-${data.coder_workspace.me.id}-user.yml" + } +} + +# Create the VM using proxmox_vm_qemu resource +resource "proxmox_vm_qemu" "workspace" { + count = data.coder_workspace.me.start_count + name = local.vm_name + target_node = data.coder_parameter.proxmox_node.value + + # Clone from template + clone = var.vm_template + full_clone = true + + # VM Configuration + cores = data.coder_parameter.cpu_cores.value + memory = data.coder_parameter.memory.value + sockets = 1 + + # Enable Qemu Guest Agent for better integration + agent = 1 + + # Operating system type + os_type = "cloud-init" + + # Cloud-init configuration + cloudinit_cdrom_storage = var.storage + cicustom = "user=local:snippets/coder-${data.coder_workspace.me.id}-user.yml" + + # Network configuration + network { + bridge = var.network_bridge + model = "virtio" + } + + # Disk configuration following provider documentation format + disks { + scsi { + scsi0 { + disk { + size = data.coder_parameter.disk_size.value + storage = var.storage + format = "raw" # Explicitly set format + } + } + } + } + + # Boot settings + boot = "order=scsi0" + onboot = false + startup = "" + + # VM lifecycle management + lifecycle { + ignore_changes = [ + network, + desc, + numa, + hotplug, + disk, # Prevent disk recreation + ] + } + + # Tags for identification (supported in newer Proxmox versions) + tags = "coder,workspace,${data.coder_workspace_owner.me.name}" + + # Depends on cloud-init file being created first + depends_on = [proxmox_file.cloud_init_user_data] +} + +# VM power management based on workspace state +resource "null_resource" "vm_power_management" { + count = data.coder_workspace.me.start_count + + # Trigger on workspace state changes + triggers = { + workspace_transition = data.coder_workspace.me.transition + vm_id = proxmox_vm_qemu.workspace[0].vmid + node = data.coder_parameter.proxmox_node.value + } + + # Start VM on workspace start + provisioner "local-exec" { + when = create + command = data.coder_workspace.me.transition == "start" ? "echo 'VM should be started'" : "echo 'VM created'" + } + + # Note: Actual VM power management would require additional tooling + # This is a placeholder for proper power management implementation + + depends_on = [proxmox_vm_qemu.workspace] +} + +# Metadata for workspace information +resource "coder_metadata" "workspace_info" { + count = data.coder_workspace.me.start_count + resource_id = proxmox_vm_qemu.workspace[0].id + + item { + key = "node" + value = data.coder_parameter.proxmox_node.value + } + item { + key = "vm_id" + value = proxmox_vm_qemu.workspace[0].vmid + } + item { + key = "cores" + value = data.coder_parameter.cpu_cores.value + } + item { + key = "memory" + value = "${data.coder_parameter.memory.value} MB" + } + item { + key = "disk_size" + value = "${data.coder_parameter.disk_size.value} GB" + } + item { + key = "template" + value = var.vm_template + } + item { + key = "ip_address" + value = proxmox_vm_qemu.workspace[0].default_ipv4_address + } +} \ No newline at end of file From 54fda65b3aa0e70d98830f6c85c747f1613ac9ee Mon Sep 17 00:00:00 2001 From: Pranjal6955 Date: Sat, 12 Jul 2025 11:56:14 +0530 Subject: [PATCH 2/4] Fixes --- registry/coder/templates/proxmox-vm/main.tf | 1 - 1 file changed, 1 deletion(-) diff --git a/registry/coder/templates/proxmox-vm/main.tf b/registry/coder/templates/proxmox-vm/main.tf index d2d65c40..892dc931 100644 --- a/registry/coder/templates/proxmox-vm/main.tf +++ b/registry/coder/templates/proxmox-vm/main.tf @@ -1,4 +1,3 @@ - terraform { required_providers { coder = { From 94ad6eaeabb5e4980e7ea06b0de1f538ff88614a Mon Sep 17 00:00:00 2001 From: Pranjal6955 Date: Sat, 12 Jul 2025 22:10:10 +0530 Subject: [PATCH 3/4] Fixes --- registry/coder/templates/proxmox-vm/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/registry/coder/templates/proxmox-vm/README.md b/registry/coder/templates/proxmox-vm/README.md index aba3b1d0..a0367971 100644 --- a/registry/coder/templates/proxmox-vm/README.md +++ b/registry/coder/templates/proxmox-vm/README.md @@ -18,6 +18,7 @@ Provision Proxmox VE virtual machines as [Coder workspaces](https://coder.com/do **Proxmox VE Cluster**: This template requires an existing Proxmox VE cluster (version 7.0 or higher recommended). **VM Template**: This template uses cloud-init enabled VM templates. You'll need to create a template with: + - Ubuntu 22.04 LTS (or your preferred Linux distribution) - Cloud-init package installed - Qemu Guest Agent installed @@ -29,16 +30,19 @@ Provision Proxmox VE virtual machines as [Coder workspaces](https://coder.com/do This template authenticates to Proxmox VE using API tokens. You'll need to: 1. Create a user in Proxmox VE for Coder: + ```bash pveum user add coder@pve ``` 2. Create an API token: + ```bash pveum user token add coder@pve coder-token --privsep=0 ``` 3. Assign appropriate permissions: + ```bash pveum role add CoderRole -privs "VM.Allocate,VM.Audit,VM.Clone,VM.Config.CDROM,VM.Config.CPU,VM.Config.Cloudinit,VM.Config.Disk,VM.Config.HWType,VM.Config.Memory,VM.Config.Network,VM.Config.Options,VM.Console,VM.Monitor,VM.PowerMgmt,VM.Snapshot,VM.Snapshot.Rollback,Datastore.Allocate,Datastore.AllocateSpace,Datastore.Audit,SDN.Use" pveum aclmod / -user coder@pve -role CoderRole From ecf99a3b84963f77a1b775360bb123089e5c3ea6 Mon Sep 17 00:00:00 2001 From: Pranjal6955 Date: Tue, 15 Jul 2025 21:40:49 +0530 Subject: [PATCH 4/4] Fixes --- .github/scripts/version-bump.sh | 4 +- .../scripts/agentapi-wait-for-start.sh | 26 ++--- .../coder/modules/agentapi/scripts/main.sh | 94 +++++++++---------- .../agentapi/testdata/agentapi-start.sh | 8 +- .../claude-code/scripts/agentapi-start.sh | 30 +++--- .../scripts/agentapi-wait-for-start.sh | 26 ++--- .../coder/modules/devcontainers-cli/run.sh | 66 ++++++------- registry/coder/modules/filebrowser/run.sh | 4 +- .../coder/modules/goose/scripts/install.sh | 46 ++++----- registry/coder/modules/goose/scripts/start.sh | 26 ++--- .../modules/goose/testdata/goose-mock.sh | 4 +- .../modules/jetbrains-fleet/main.test.ts | 2 +- registry/coder/modules/jupyterlab/run.sh | 28 +++--- registry/coder/modules/kasmvnc/run.sh | 45 ++++----- registry/coder/modules/vault-jwt/run.sh | 14 +-- registry/coder/modules/vscode-web/run.sh | 2 +- registry/coder/templates/proxmox-vm/main.tf | 74 +++++++-------- 17 files changed, 251 insertions(+), 248 deletions(-) diff --git a/.github/scripts/version-bump.sh b/.github/scripts/version-bump.sh index 095d1279..fc316619 100755 --- a/.github/scripts/version-bump.sh +++ b/.github/scripts/version-bump.sh @@ -192,8 +192,8 @@ main() { # 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..." + 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" fi diff --git a/registry/coder/modules/agentapi/scripts/agentapi-wait-for-start.sh b/registry/coder/modules/agentapi/scripts/agentapi-wait-for-start.sh index 7430e9ec..6e18332f 100644 --- a/registry/coder/modules/agentapi/scripts/agentapi-wait-for-start.sh +++ b/registry/coder/modules/agentapi/scripts/agentapi-wait-for-start.sh @@ -11,22 +11,22 @@ agentapi_started=false echo "Waiting for agentapi server to start on port $port..." for i in $(seq 1 150); do - for j in $(seq 1 3); do - sleep 0.1 - if curl -fs -o /dev/null "http://localhost:$port/status"; then - echo "agentapi response received ($j/3)" - else - echo "agentapi server not responding ($i/15)" - continue 2 - fi - done - agentapi_started=true - break + for j in $(seq 1 3); do + sleep 0.1 + if curl -fs -o /dev/null "http://localhost:$port/status"; then + echo "agentapi response received ($j/3)" + else + echo "agentapi server not responding ($i/15)" + continue 2 + fi + done + agentapi_started=true + break done if [ "$agentapi_started" != "true" ]; then - echo "Error: agentapi server did not start on port $port after 15 seconds." - exit 1 + echo "Error: agentapi server did not start on port $port after 15 seconds." + exit 1 fi echo "agentapi server started on port $port." diff --git a/registry/coder/modules/agentapi/scripts/main.sh b/registry/coder/modules/agentapi/scripts/main.sh index f7a5caab..3b485d27 100644 --- a/registry/coder/modules/agentapi/scripts/main.sh +++ b/registry/coder/modules/agentapi/scripts/main.sh @@ -16,81 +16,81 @@ AGENTAPI_PORT="$ARG_AGENTAPI_PORT" set +o nounset command_exists() { - command -v "$1" >/dev/null 2>&1 + command -v "$1" > /dev/null 2>&1 } module_path="$HOME/${MODULE_DIR_NAME}" mkdir -p "$module_path/scripts" if [ ! -d "${WORKDIR}" ]; then - echo "Warning: The specified folder '${WORKDIR}' does not exist." - echo "Creating the folder..." - mkdir -p "${WORKDIR}" - echo "Folder created successfully." + echo "Warning: The specified folder '${WORKDIR}' does not exist." + echo "Creating the folder..." + mkdir -p "${WORKDIR}" + echo "Folder created successfully." fi if [ -n "${PRE_INSTALL_SCRIPT}" ]; then - echo "Running pre-install script..." - echo -n "${PRE_INSTALL_SCRIPT}" >"$module_path/pre_install.sh" - chmod +x "$module_path/pre_install.sh" - "$module_path/pre_install.sh" 2>&1 | tee "$module_path/pre_install.log" + echo "Running pre-install script..." + echo -n "${PRE_INSTALL_SCRIPT}" > "$module_path/pre_install.sh" + chmod +x "$module_path/pre_install.sh" + "$module_path/pre_install.sh" 2>&1 | tee "$module_path/pre_install.log" fi echo "Running install script..." -echo -n "${INSTALL_SCRIPT}" >"$module_path/install.sh" +echo -n "${INSTALL_SCRIPT}" > "$module_path/install.sh" chmod +x "$module_path/install.sh" "$module_path/install.sh" 2>&1 | tee "$module_path/install.log" # Install AgentAPI if enabled if [ "${INSTALL_AGENTAPI}" = "true" ]; then - echo "Installing AgentAPI..." - arch=$(uname -m) - if [ "$arch" = "x86_64" ]; then - binary_name="agentapi-linux-amd64" - elif [ "$arch" = "aarch64" ]; then - binary_name="agentapi-linux-arm64" - else - echo "Error: Unsupported architecture: $arch" - exit 1 - fi - if [ "${AGENTAPI_VERSION}" = "latest" ]; then - # for the latest release the download URL pattern is different than for tagged releases - # https://docs.github.com/en/repositories/releasing-projects-on-github/linking-to-releases - download_url="https://github.com/coder/agentapi/releases/latest/download/$binary_name" - else - download_url="https://github.com/coder/agentapi/releases/download/${AGENTAPI_VERSION}/$binary_name" - fi - curl \ - --retry 5 \ - --retry-delay 5 \ - --fail \ - --retry-all-errors \ - -L \ - -C - \ - -o agentapi \ - "$download_url" - chmod +x agentapi - sudo mv agentapi /usr/local/bin/agentapi + echo "Installing AgentAPI..." + arch=$(uname -m) + if [ "$arch" = "x86_64" ]; then + binary_name="agentapi-linux-amd64" + elif [ "$arch" = "aarch64" ]; then + binary_name="agentapi-linux-arm64" + else + echo "Error: Unsupported architecture: $arch" + exit 1 + fi + if [ "${AGENTAPI_VERSION}" = "latest" ]; then + # for the latest release the download URL pattern is different than for tagged releases + # https://docs.github.com/en/repositories/releasing-projects-on-github/linking-to-releases + download_url="https://github.com/coder/agentapi/releases/latest/download/$binary_name" + else + download_url="https://github.com/coder/agentapi/releases/download/${AGENTAPI_VERSION}/$binary_name" + fi + curl \ + --retry 5 \ + --retry-delay 5 \ + --fail \ + --retry-all-errors \ + -L \ + -C - \ + -o agentapi \ + "$download_url" + chmod +x agentapi + sudo mv agentapi /usr/local/bin/agentapi fi if ! command_exists agentapi; then - echo "Error: AgentAPI is not installed. Please enable install_agentapi or install it manually." - exit 1 + echo "Error: AgentAPI is not installed. Please enable install_agentapi or install it manually." + exit 1 fi -echo -n "${START_SCRIPT}" >"$module_path/scripts/agentapi-start.sh" -echo -n "${WAIT_FOR_START_SCRIPT}" >"$module_path/scripts/agentapi-wait-for-start.sh" +echo -n "${START_SCRIPT}" > "$module_path/scripts/agentapi-start.sh" +echo -n "${WAIT_FOR_START_SCRIPT}" > "$module_path/scripts/agentapi-wait-for-start.sh" chmod +x "$module_path/scripts/agentapi-start.sh" chmod +x "$module_path/scripts/agentapi-wait-for-start.sh" if [ -n "${POST_INSTALL_SCRIPT}" ]; then - echo "Running post-install script..." - echo -n "${POST_INSTALL_SCRIPT}" >"$module_path/post_install.sh" - chmod +x "$module_path/post_install.sh" - "$module_path/post_install.sh" 2>&1 | tee "$module_path/post_install.log" + echo "Running post-install script..." + echo -n "${POST_INSTALL_SCRIPT}" > "$module_path/post_install.sh" + chmod +x "$module_path/post_install.sh" + "$module_path/post_install.sh" 2>&1 | tee "$module_path/post_install.log" fi export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 cd "${WORKDIR}" -nohup "$module_path/scripts/agentapi-start.sh" true "${AGENTAPI_PORT}" &>"$module_path/agentapi-start.log" & +nohup "$module_path/scripts/agentapi-start.sh" true "${AGENTAPI_PORT}" &> "$module_path/agentapi-start.log" & "$module_path/scripts/agentapi-wait-for-start.sh" "${AGENTAPI_PORT}" diff --git a/registry/coder/modules/agentapi/testdata/agentapi-start.sh b/registry/coder/modules/agentapi/testdata/agentapi-start.sh index 1564fe03..cf55e7a1 100644 --- a/registry/coder/modules/agentapi/testdata/agentapi-start.sh +++ b/registry/coder/modules/agentapi/testdata/agentapi-start.sh @@ -8,9 +8,9 @@ port=${2:-3284} module_path="$HOME/.agentapi-module" log_file_path="$module_path/agentapi.log" -echo "using prompt: $use_prompt" >>/home/coder/test-agentapi-start.log -echo "using port: $port" >>/home/coder/test-agentapi-start.log +echo "using prompt: $use_prompt" >> /home/coder/test-agentapi-start.log +echo "using port: $port" >> /home/coder/test-agentapi-start.log agentapi server --port "$port" --term-width 67 --term-height 1190 -- \ - bash -c aiagent \ - >"$log_file_path" 2>&1 + bash -c aiagent \ + > "$log_file_path" 2>&1 diff --git a/registry/coder/modules/claude-code/scripts/agentapi-start.sh b/registry/coder/modules/claude-code/scripts/agentapi-start.sh index c66b7f35..4f447c33 100644 --- a/registry/coder/modules/claude-code/scripts/agentapi-start.sh +++ b/registry/coder/modules/claude-code/scripts/agentapi-start.sh @@ -9,14 +9,14 @@ log_file_path="$module_path/agentapi.log" # if the first argument is not empty, start claude with the prompt if [ -n "$1" ]; then - cp "$module_path/prompt.txt" /tmp/claude-code-prompt + cp "$module_path/prompt.txt" /tmp/claude-code-prompt else - rm -f /tmp/claude-code-prompt + rm -f /tmp/claude-code-prompt fi # if the log file already exists, archive it if [ -f "$log_file_path" ]; then - mv "$log_file_path" "$log_file_path"".$(date +%s)" + mv "$log_file_path" "$log_file_path"".$(date +%s)" fi # see the remove-last-session-id.js script for details @@ -28,14 +28,14 @@ node "$scripts_dir/remove-last-session-id.js" "$(pwd)" || true set +o errexit function start_agentapi() { - local continue_flag="$1" - local prompt_subshell='"$(cat /tmp/claude-code-prompt)"' - - # use low width to fit in the tasks UI sidebar. height is adjusted so that width x height ~= 80x1000 characters - # visible in the terminal screen by default. - agentapi server --term-width 67 --term-height 1190 -- \ - bash -c "claude $continue_flag --dangerously-skip-permissions $prompt_subshell" \ - > "$log_file_path" 2>&1 + local continue_flag="$1" + local prompt_subshell='"$(cat /tmp/claude-code-prompt)"' + + # use low width to fit in the tasks UI sidebar. height is adjusted so that width x height ~= 80x1000 characters + # visible in the terminal screen by default. + agentapi server --term-width 67 --term-height 1190 -- \ + bash -c "claude $continue_flag --dangerously-skip-permissions $prompt_subshell" \ + > "$log_file_path" 2>&1 } echo "Starting AgentAPI..." @@ -47,15 +47,15 @@ exit_code=$? echo "First AgentAPI exit code: $exit_code" if [ $exit_code -eq 0 ]; then - exit 0 + exit 0 fi # if there was no conversation to continue, claude exited with an error. # start claude without the --continue flag. if grep -q "No conversation found to continue" "$log_file_path"; then - echo "AgentAPI with --continue flag failed, starting claude without it." - start_agentapi - exit_code=$? + echo "AgentAPI with --continue flag failed, starting claude without it." + start_agentapi + exit_code=$? fi echo "Second AgentAPI exit code: $exit_code" diff --git a/registry/coder/modules/claude-code/scripts/agentapi-wait-for-start.sh b/registry/coder/modules/claude-code/scripts/agentapi-wait-for-start.sh index 2eb84975..b9e76d36 100644 --- a/registry/coder/modules/claude-code/scripts/agentapi-wait-for-start.sh +++ b/registry/coder/modules/claude-code/scripts/agentapi-wait-for-start.sh @@ -9,22 +9,22 @@ agentapi_started=false echo "Waiting for agentapi server to start on port 3284..." for i in $(seq 1 150); do - for j in $(seq 1 3); do - sleep 0.1 - if curl -fs -o /dev/null "http://localhost:3284/status"; then - echo "agentapi response received ($j/3)" - else - echo "agentapi server not responding ($i/15)" - continue 2 - fi - done - agentapi_started=true - break + for j in $(seq 1 3); do + sleep 0.1 + if curl -fs -o /dev/null "http://localhost:3284/status"; then + echo "agentapi response received ($j/3)" + else + echo "agentapi server not responding ($i/15)" + continue 2 + fi + done + agentapi_started=true + break done if [ "$agentapi_started" != "true" ]; then - echo "Error: agentapi server did not start on port 3284 after 15 seconds." - exit 1 + echo "Error: agentapi server did not start on port 3284 after 15 seconds." + exit 1 fi echo "agentapi server started on port 3284." diff --git a/registry/coder/modules/devcontainers-cli/run.sh b/registry/coder/modules/devcontainers-cli/run.sh index f7bf852c..bd3c1b1d 100644 --- a/registry/coder/modules/devcontainers-cli/run.sh +++ b/registry/coder/modules/devcontainers-cli/run.sh @@ -1,55 +1,55 @@ #!/usr/bin/env sh # If @devcontainers/cli is already installed, we can skip -if command -v devcontainer >/dev/null 2>&1; then - echo "🥳 @devcontainers/cli is already installed into $(which devcontainer)!" - exit 0 +if command -v devcontainer > /dev/null 2>&1; then + echo "🥳 @devcontainers/cli is already installed into $(which devcontainer)!" + exit 0 fi # Check if docker is installed -if ! command -v docker >/dev/null 2>&1; then - echo "WARNING: Docker was not found but is required to use @devcontainers/cli, please make sure it is available." +if ! command -v docker > /dev/null 2>&1; then + echo "WARNING: Docker was not found but is required to use @devcontainers/cli, please make sure it is available." fi # Determine the package manager to use: npm, pnpm, or yarn -if command -v yarn >/dev/null 2>&1; then - PACKAGE_MANAGER="yarn" -elif command -v npm >/dev/null 2>&1; then - PACKAGE_MANAGER="npm" -elif command -v pnpm >/dev/null 2>&1; then - PACKAGE_MANAGER="pnpm" +if command -v yarn > /dev/null 2>&1; then + PACKAGE_MANAGER="yarn" +elif command -v npm > /dev/null 2>&1; then + PACKAGE_MANAGER="npm" +elif command -v pnpm > /dev/null 2>&1; then + PACKAGE_MANAGER="pnpm" else - echo "ERROR: No supported package manager (npm, pnpm, yarn) is installed. Please install one first." 1>&2 - exit 1 + echo "ERROR: No supported package manager (npm, pnpm, yarn) is installed. Please install one first." 1>&2 + exit 1 fi install() { - echo "Installing @devcontainers/cli using $PACKAGE_MANAGER..." - if [ "$PACKAGE_MANAGER" = "npm" ]; then - npm install -g @devcontainers/cli - elif [ "$PACKAGE_MANAGER" = "pnpm" ]; then - # Check if PNPM_HOME is set, if not, set it to the script's bin directory - # pnpm needs this to be set to install binaries - # coder agent ensures this part is part of the PATH - # so that the devcontainer command is available - if [ -z "$PNPM_HOME" ]; then - PNPM_HOME="$CODER_SCRIPT_BIN_DIR" - export M_HOME - fi - pnpm add -g @devcontainers/cli - elif [ "$PACKAGE_MANAGER" = "yarn" ]; then - yarn global add @devcontainers/cli --prefix "$(dirname "$CODER_SCRIPT_BIN_DIR")" + echo "Installing @devcontainers/cli using $PACKAGE_MANAGER..." + if [ "$PACKAGE_MANAGER" = "npm" ]; then + npm install -g @devcontainers/cli + elif [ "$PACKAGE_MANAGER" = "pnpm" ]; then + # Check if PNPM_HOME is set, if not, set it to the script's bin directory + # pnpm needs this to be set to install binaries + # coder agent ensures this part is part of the PATH + # so that the devcontainer command is available + if [ -z "$PNPM_HOME" ]; then + PNPM_HOME="$CODER_SCRIPT_BIN_DIR" + export M_HOME fi + pnpm add -g @devcontainers/cli + elif [ "$PACKAGE_MANAGER" = "yarn" ]; then + yarn global add @devcontainers/cli --prefix "$(dirname "$CODER_SCRIPT_BIN_DIR")" + fi } if ! install; then - echo "Failed to install @devcontainers/cli" >&2 - exit 1 + echo "Failed to install @devcontainers/cli" >&2 + exit 1 fi -if ! command -v devcontainer >/dev/null 2>&1; then - echo "Installation completed but 'devcontainer' command not found in PATH" >&2 - exit 1 +if ! command -v devcontainer > /dev/null 2>&1; then + echo "Installation completed but 'devcontainer' command not found in PATH" >&2 + exit 1 fi echo "🥳 @devcontainers/cli has been installed into $(which devcontainer)!" diff --git a/registry/coder/modules/filebrowser/run.sh b/registry/coder/modules/filebrowser/run.sh index ea4b857a..ed34e2a2 100644 --- a/registry/coder/modules/filebrowser/run.sh +++ b/registry/coder/modules/filebrowser/run.sh @@ -7,7 +7,7 @@ BOLD='\033[[0;1m' printf "$${BOLD}Installing filebrowser \n\n" # Check if filebrowser is installed -if ! command -v filebrowser &>/dev/null; then +if ! command -v filebrowser &> /dev/null; then curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash fi @@ -34,6 +34,6 @@ printf "👷 Starting filebrowser in background... \n\n" printf "📂 Serving $${ROOT_DIR} at http://localhost:${PORT} \n\n" -filebrowser >>${LOG_PATH} 2>&1 & +filebrowser >> ${LOG_PATH} 2>&1 & printf "📝 Logs at ${LOG_PATH} \n\n" diff --git a/registry/coder/modules/goose/scripts/install.sh b/registry/coder/modules/goose/scripts/install.sh index 28fc923a..9bca8086 100644 --- a/registry/coder/modules/goose/scripts/install.sh +++ b/registry/coder/modules/goose/scripts/install.sh @@ -2,7 +2,7 @@ # Function to check if a command exists command_exists() { - command -v "$1" >/dev/null 2>&1 + command -v "$1" > /dev/null 2>&1 } set -o nounset @@ -18,40 +18,40 @@ echo "--------------------------------" set +o nounset if [ "${ARG_INSTALL}" = "true" ]; then - echo "Installing Goose..." - parsed_version="${ARG_GOOSE_VERSION}" - if [ "${ARG_GOOSE_VERSION}" = "stable" ]; then - parsed_version="" - fi - curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh | GOOSE_VERSION="${parsed_version}" CONFIGURE=false bash - echo "Goose installed" + echo "Installing Goose..." + parsed_version="${ARG_GOOSE_VERSION}" + if [ "${ARG_GOOSE_VERSION}" = "stable" ]; then + parsed_version="" + fi + curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh | GOOSE_VERSION="${parsed_version}" CONFIGURE=false bash + echo "Goose installed" else - echo "Skipping Goose installation" + echo "Skipping Goose installation" fi if [ "${ARG_GOOSE_CONFIG}" != "" ]; then - echo "Configuring Goose..." - mkdir -p "$HOME/.config/goose" - echo "GOOSE_PROVIDER: $ARG_PROVIDER" >"$HOME/.config/goose/config.yaml" - echo "GOOSE_MODEL: $ARG_MODEL" >>"$HOME/.config/goose/config.yaml" - echo "$ARG_GOOSE_CONFIG" >>"$HOME/.config/goose/config.yaml" + echo "Configuring Goose..." + mkdir -p "$HOME/.config/goose" + echo "GOOSE_PROVIDER: $ARG_PROVIDER" > "$HOME/.config/goose/config.yaml" + echo "GOOSE_MODEL: $ARG_MODEL" >> "$HOME/.config/goose/config.yaml" + echo "$ARG_GOOSE_CONFIG" >> "$HOME/.config/goose/config.yaml" else - echo "Skipping Goose configuration" + echo "Skipping Goose configuration" fi if [ "${GOOSE_SYSTEM_PROMPT}" != "" ]; then - echo "Setting Goose system prompt..." - mkdir -p "$HOME/.config/goose" - echo "$GOOSE_SYSTEM_PROMPT" >"$HOME/.config/goose/.goosehints" + echo "Setting Goose system prompt..." + mkdir -p "$HOME/.config/goose" + echo "$GOOSE_SYSTEM_PROMPT" > "$HOME/.config/goose/.goosehints" else - echo "Goose system prompt not set. use the GOOSE_SYSTEM_PROMPT environment variable to set it." + echo "Goose system prompt not set. use the GOOSE_SYSTEM_PROMPT environment variable to set it." fi if command_exists goose; then - GOOSE_CMD=goose + GOOSE_CMD=goose elif [ -f "$HOME/.local/bin/goose" ]; then - GOOSE_CMD="$HOME/.local/bin/goose" + GOOSE_CMD="$HOME/.local/bin/goose" else - echo "Error: Goose is not installed. Please enable install_goose or install it manually." - exit 1 + echo "Error: Goose is not installed. Please enable install_goose or install it manually." + exit 1 fi diff --git a/registry/coder/modules/goose/scripts/start.sh b/registry/coder/modules/goose/scripts/start.sh index 314a41d0..737138ba 100644 --- a/registry/coder/modules/goose/scripts/start.sh +++ b/registry/coder/modules/goose/scripts/start.sh @@ -4,16 +4,16 @@ set -o errexit set -o pipefail command_exists() { - command -v "$1" >/dev/null 2>&1 + command -v "$1" > /dev/null 2>&1 } if command_exists goose; then - GOOSE_CMD=goose + GOOSE_CMD=goose elif [ -f "$HOME/.local/bin/goose" ]; then - GOOSE_CMD="$HOME/.local/bin/goose" + GOOSE_CMD="$HOME/.local/bin/goose" else - echo "Error: Goose is not installed. Please enable install_goose or install it manually." - exit 1 + echo "Error: Goose is not installed. Please enable install_goose or install it manually." + exit 1 fi # this must be kept up to date with main.tf @@ -21,15 +21,15 @@ MODULE_DIR="$HOME/.goose-module" mkdir -p "$MODULE_DIR" if [ ! -z "$GOOSE_TASK_PROMPT" ]; then - echo "Starting with a prompt" - PROMPT="Review your goosehints. Every step of the way, report tasks to Coder with proper descriptions and statuses. Your task at hand: $GOOSE_TASK_PROMPT" - PROMPT_FILE="$MODULE_DIR/prompt.txt" - echo -n "$PROMPT" >"$PROMPT_FILE" - GOOSE_ARGS=(run --interactive --instructions "$PROMPT_FILE") + echo "Starting with a prompt" + PROMPT="Review your goosehints. Every step of the way, report tasks to Coder with proper descriptions and statuses. Your task at hand: $GOOSE_TASK_PROMPT" + PROMPT_FILE="$MODULE_DIR/prompt.txt" + echo -n "$PROMPT" > "$PROMPT_FILE" + GOOSE_ARGS=(run --interactive --instructions "$PROMPT_FILE") else - echo "Starting without a prompt" - GOOSE_ARGS=() + echo "Starting without a prompt" + GOOSE_ARGS=() fi agentapi server --term-width 67 --term-height 1190 -- \ - bash -c "$(printf '%q ' "$GOOSE_CMD" "${GOOSE_ARGS[@]}")" + bash -c "$(printf '%q ' "$GOOSE_CMD" "${GOOSE_ARGS[@]}")" diff --git a/registry/coder/modules/goose/testdata/goose-mock.sh b/registry/coder/modules/goose/testdata/goose-mock.sh index 4d7d3931..b6b3e38c 100644 --- a/registry/coder/modules/goose/testdata/goose-mock.sh +++ b/registry/coder/modules/goose/testdata/goose-mock.sh @@ -3,6 +3,6 @@ set -e while true; do - echo "$(date) - goose-mock" - sleep 15 + echo "$(date) - goose-mock" + sleep 15 done diff --git a/registry/coder/modules/jetbrains-fleet/main.test.ts b/registry/coder/modules/jetbrains-fleet/main.test.ts index b9463e81..e266985a 100644 --- a/registry/coder/modules/jetbrains-fleet/main.test.ts +++ b/registry/coder/modules/jetbrains-fleet/main.test.ts @@ -97,4 +97,4 @@ describe("jetbrains-fleet", async () => { expect(coder_app?.instances.length).toBe(1); expect(coder_app?.instances[0].attributes.group).toBe("JetBrains IDEs"); }); -}); \ No newline at end of file +}); diff --git a/registry/coder/modules/jupyterlab/run.sh b/registry/coder/modules/jupyterlab/run.sh index e9a45b5a..be686e55 100644 --- a/registry/coder/modules/jupyterlab/run.sh +++ b/registry/coder/modules/jupyterlab/run.sh @@ -3,13 +3,13 @@ INSTALLER="" check_available_installer() { # check if pipx is installed echo "Checking for a supported installer" - if command -v pipx >/dev/null 2>&1; then + if command -v pipx > /dev/null 2>&1; then echo "pipx is installed" INSTALLER="pipx" return fi # check if uv is installed - if command -v uv >/dev/null 2>&1; then + if command -v uv > /dev/null 2>&1; then echo "uv is installed" INSTALLER="uv" return @@ -26,21 +26,21 @@ fi BOLD='\033[0;1m' # check if jupyterlab is installed -if ! command -v jupyter-lab >/dev/null 2>&1; then +if ! command -v jupyter-lab > /dev/null 2>&1; then # install jupyterlab check_available_installer printf "$${BOLD}Installing jupyterlab!\n" case $INSTALLER in - uv) - uv pip install -q jupyterlab && - printf "%s\n" "🥳 jupyterlab has been installed" - JUPYTER="$HOME/.venv/bin/jupyter-lab" - ;; - pipx) - pipx install jupyterlab && - printf "%s\n" "🥳 jupyterlab has been installed" - JUPYTER="$HOME/.local/bin/jupyter-lab" - ;; + uv) + uv pip install -q jupyterlab \ + && printf "%s\n" "🥳 jupyterlab has been installed" + JUPYTER="$HOME/.venv/bin/jupyter-lab" + ;; + pipx) + pipx install jupyterlab \ + && printf "%s\n" "🥳 jupyterlab has been installed" + JUPYTER="$HOME/.local/bin/jupyter-lab" + ;; esac else printf "%s\n\n" "🥳 jupyterlab is already installed" @@ -55,4 +55,4 @@ $JUPYTER --no-browser \ --ServerApp.port="${PORT}" \ --ServerApp.token='' \ --ServerApp.password='' \ - >"${LOG_PATH}" 2>&1 & + > "${LOG_PATH}" 2>&1 & diff --git a/registry/coder/modules/kasmvnc/run.sh b/registry/coder/modules/kasmvnc/run.sh index 67a8a310..9ba7d207 100644 --- a/registry/coder/modules/kasmvnc/run.sh +++ b/registry/coder/modules/kasmvnc/run.sh @@ -3,7 +3,10 @@ # Exit on error, undefined variables, and pipe failures set -euo pipefail -error() { printf "💀 ERROR: %s\n" "$@"; exit 1; } +error() { + printf "💀 ERROR: %s\n" "$@" + exit 1 +} # Function to check if vncserver is already installed check_installed() { @@ -248,30 +251,30 @@ get_http_dir() { echo $httpd_directory } -fix_server_index_file(){ - local fname=$${FUNCNAME[0]} # gets current function name - if [[ $# -ne 1 ]]; then - error "$fname requires exactly 1 parameter:\n\tpath to KasmVNC httpd_directory" - fi - local httpdir="$1" - if [[ ! -d "$httpdir" ]]; then - error "$fname: $httpdir is not a directory" - fi - pushd "$httpdir" > /dev/null +fix_server_index_file() { + local fname=$${FUNCNAME[0]} # gets current function name + if [[ $# -ne 1 ]]; then + error "$fname requires exactly 1 parameter:\n\tpath to KasmVNC httpd_directory" + fi + local httpdir="$1" + if [[ ! -d "$httpdir" ]]; then + error "$fname: $httpdir is not a directory" + fi + pushd "$httpdir" > /dev/null - cat <<'EOH' > /tmp/path_vnc.html + cat << 'EOH' > /tmp/path_vnc.html ${PATH_VNC_HTML} EOH - $SUDO mv /tmp/path_vnc.html . - # check for the switcheroo - if [[ -f "index.html" && -L "vnc.html" ]]; then - $SUDO mv $httpdir/index.html $httpdir/vnc.html - fi - $SUDO ln -s -f path_vnc.html index.html - popd > /dev/null + $SUDO mv /tmp/path_vnc.html . + # check for the switcheroo + if [[ -f "index.html" && -L "vnc.html" ]]; then + $SUDO mv $httpdir/index.html $httpdir/vnc.html + fi + $SUDO ln -s -f path_vnc.html index.html + popd > /dev/null } -patch_kasm_http_files(){ +patch_kasm_http_files() { homedir=$(get_http_dir) fix_server_index_file "$homedir" } @@ -292,7 +295,7 @@ set -e if [[ $RETVAL -ne 0 ]]; then echo "ERROR: Failed to start KasmVNC server. Return code: $RETVAL" - if [[ -f "$VNC_LOG" ]]; then + if [[ -f "$VNC_LOG" ]]; then echo "Full logs:" cat "$VNC_LOG" else diff --git a/registry/coder/modules/vault-jwt/run.sh b/registry/coder/modules/vault-jwt/run.sh index 6d478548..d95b45a2 100644 --- a/registry/coder/modules/vault-jwt/run.sh +++ b/registry/coder/modules/vault-jwt/run.sh @@ -9,11 +9,11 @@ CODER_OIDC_ACCESS_TOKEN=${CODER_OIDC_ACCESS_TOKEN} fetch() { dest="$1" url="$2" - if command -v curl >/dev/null 2>&1; then + if command -v curl > /dev/null 2>&1; then curl -sSL --fail "$${url}" -o "$${dest}" - elif command -v wget >/dev/null 2>&1; then + elif command -v wget > /dev/null 2>&1; then wget -O "$${dest}" "$${url}" - elif command -v busybox >/dev/null 2>&1; then + elif command -v busybox > /dev/null 2>&1; then busybox wget -O "$${dest}" "$${url}" else printf "curl, wget, or busybox is not installed. Please install curl or wget in your image.\n" @@ -22,9 +22,9 @@ fetch() { } unzip_safe() { - if command -v unzip >/dev/null 2>&1; then + if command -v unzip > /dev/null 2>&1; then command unzip "$@" - elif command -v busybox >/dev/null 2>&1; then + elif command -v busybox > /dev/null 2>&1; then busybox unzip "$@" else printf "unzip or busybox is not installed. Please install unzip in your image.\n" @@ -56,7 +56,7 @@ install() { # Check if the vault CLI is installed and has the correct version installation_needed=1 - if command -v vault >/dev/null 2>&1; then + if command -v vault > /dev/null 2>&1; then CURRENT_VERSION=$(vault version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') if [ "$${CURRENT_VERSION}" = "$${VAULT_CLI_VERSION}" ]; then printf "Vault version %s is already installed and up-to-date.\n\n" "$${CURRENT_VERSION}" @@ -81,7 +81,7 @@ install() { return 1 fi rm vault.zip - if sudo mv vault /usr/local/bin/vault 2>/dev/null; then + if sudo mv vault /usr/local/bin/vault 2> /dev/null; then printf "Vault installed successfully!\n\n" else mkdir -p ~/.local/bin diff --git a/registry/coder/modules/vscode-web/run.sh b/registry/coder/modules/vscode-web/run.sh index 9346b4bd..b2554d06 100644 --- a/registry/coder/modules/vscode-web/run.sh +++ b/registry/coder/modules/vscode-web/run.sh @@ -68,7 +68,7 @@ esac # Detect the platform if [ -n "${PLATFORM}" ]; then DETECTED_PLATFORM="${PLATFORM}" -elif [ -f /etc/alpine-release ] || grep -qi 'ID=alpine' /etc/os-release 2>/dev/null || command -v apk > /dev/null 2>&1; then +elif [ -f /etc/alpine-release ] || grep -qi 'ID=alpine' /etc/os-release 2> /dev/null || command -v apk > /dev/null 2>&1; then DETECTED_PLATFORM="alpine" elif [ "$(uname -s)" = "Darwin" ]; then DETECTED_PLATFORM="darwin" diff --git a/registry/coder/templates/proxmox-vm/main.tf b/registry/coder/templates/proxmox-vm/main.tf index 892dc931..56608877 100644 --- a/registry/coder/templates/proxmox-vm/main.tf +++ b/registry/coder/templates/proxmox-vm/main.tf @@ -50,7 +50,7 @@ variable "network_bridge" { provider "proxmox" { # API URL can be set via PM_API_URL environment variable or variable pm_api_url = var.proxmox_api_url != "" ? var.proxmox_api_url : null - + # Authentication via API token (recommended): # Set these environment variables: # PM_API_TOKEN_ID='terraform-prov@pve!mytoken' @@ -59,21 +59,21 @@ provider "proxmox" { # Or via username/password: # PM_USER="terraform-prov@pve" # PM_PASS="password" - + # TLS settings - pm_tls_insecure = true # Allow self-signed certificates (common in Proxmox) - + pm_tls_insecure = true # Allow self-signed certificates (common in Proxmox) + # Performance settings - pm_parallel = 2 # Allow 2 simultaneous operations - pm_timeout = 600 # 10 minute timeout for API calls - + pm_parallel = 2 # Allow 2 simultaneous operations + pm_timeout = 600 # 10 minute timeout for API calls + # Optional: Enable debugging pm_debug = false - + # Optional: Enable logging for troubleshooting pm_log_enable = false pm_log_file = "terraform-plugin-proxmox.log" - + # Minimum permission check pm_minimum_permission_check = true } @@ -176,8 +176,8 @@ resource "coder_agent" "main" { module "code-server" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/code-server/coder" - - version = "~> 1.0" + + version = "~> 1.0" agent_id = coder_agent.main.id order = 1 } @@ -190,17 +190,17 @@ module "jetbrains_gateway" { jetbrains_ides = ["IU", "PY", "WS", "PS", "RD", "CL", "GO", "RM"] default = "IU" folder = "/home/coder" - - version = "~> 1.0" + + version = "~> 1.0" agent_id = coder_agent.main.id agent_name = "main" order = 2 } locals { - vm_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}" - linux_user = "coder" - hostname = lower(data.coder_workspace.me.name) + vm_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}" + linux_user = "coder" + hostname = lower(data.coder_workspace.me.name) } # Cloud-init configuration @@ -264,7 +264,7 @@ resource "proxmox_file" "cloud_init_user_data" { content_type = "snippets" datastore_id = "local" node_name = data.coder_parameter.proxmox_node.value - + source_raw { data = data.cloudinit_config.user_data.rendered file_name = "coder-${data.coder_workspace.me.id}-user.yml" @@ -276,32 +276,32 @@ resource "proxmox_vm_qemu" "workspace" { count = data.coder_workspace.me.start_count name = local.vm_name target_node = data.coder_parameter.proxmox_node.value - + # Clone from template clone = var.vm_template full_clone = true - + # VM Configuration cores = data.coder_parameter.cpu_cores.value memory = data.coder_parameter.memory.value sockets = 1 - + # Enable Qemu Guest Agent for better integration agent = 1 - + # Operating system type os_type = "cloud-init" - + # Cloud-init configuration cloudinit_cdrom_storage = var.storage cicustom = "user=local:snippets/coder-${data.coder_workspace.me.id}-user.yml" - + # Network configuration network { bridge = var.network_bridge model = "virtio" } - + # Disk configuration following provider documentation format disks { scsi { @@ -309,17 +309,17 @@ resource "proxmox_vm_qemu" "workspace" { disk { size = data.coder_parameter.disk_size.value storage = var.storage - format = "raw" # Explicitly set format + format = "raw" # Explicitly set format } } } } - + # Boot settings boot = "order=scsi0" onboot = false startup = "" - + # VM lifecycle management lifecycle { ignore_changes = [ @@ -327,13 +327,13 @@ resource "proxmox_vm_qemu" "workspace" { desc, numa, hotplug, - disk, # Prevent disk recreation + disk, # Prevent disk recreation ] } - + # Tags for identification (supported in newer Proxmox versions) tags = "coder,workspace,${data.coder_workspace_owner.me.name}" - + # Depends on cloud-init file being created first depends_on = [proxmox_file.cloud_init_user_data] } @@ -341,23 +341,23 @@ resource "proxmox_vm_qemu" "workspace" { # VM power management based on workspace state resource "null_resource" "vm_power_management" { count = data.coder_workspace.me.start_count - + # Trigger on workspace state changes triggers = { workspace_transition = data.coder_workspace.me.transition - vm_id = proxmox_vm_qemu.workspace[0].vmid - node = data.coder_parameter.proxmox_node.value + vm_id = proxmox_vm_qemu.workspace[0].vmid + node = data.coder_parameter.proxmox_node.value } - + # Start VM on workspace start provisioner "local-exec" { when = create command = data.coder_workspace.me.transition == "start" ? "echo 'VM should be started'" : "echo 'VM created'" } - + # Note: Actual VM power management would require additional tooling # This is a placeholder for proper power management implementation - + depends_on = [proxmox_vm_qemu.workspace] } @@ -371,7 +371,7 @@ resource "coder_metadata" "workspace_info" { value = data.coder_parameter.proxmox_node.value } item { - key = "vm_id" + key = "vm_id" value = proxmox_vm_qemu.workspace[0].vmid } item {