mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
Bumps the coder-modules group with 1 update in the /dogfood/coder directory: coder/vscode-web/coder. Bumps the coder-modules group with 1 update in the /dogfood/vscode-coder directory: coder/vscode-web/coder. Updates `coder/vscode-web/coder` from 1.5.1 to 1.6.0 Updates `coder/vscode-web/coder` from 1.5.1 to 1.6.0 Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
568 lines
16 KiB
Terraform
568 lines
16 KiB
Terraform
terraform {
|
|
required_providers {
|
|
coder = {
|
|
source = "coder/coder"
|
|
version = ">= 2.13.0"
|
|
}
|
|
docker = {
|
|
source = "kreuzwerker/docker"
|
|
version = "~> 4.0"
|
|
}
|
|
}
|
|
}
|
|
|
|
locals {
|
|
// These are cluster service addresses mapped to Tailscale nodes.
|
|
// Ask Dean or Kyle for help.
|
|
docker_host = {
|
|
"" = "tcp://rubinsky-pit-cdr-dev.tailscale.svc.cluster.local:2375"
|
|
"us-pittsburgh" = "tcp://rubinsky-pit-cdr-dev.tailscale.svc.cluster.local:2375"
|
|
// For legacy reasons, this host is labelled `eu-helsinki` but it's
|
|
// actually in Germany now.
|
|
"eu-helsinki" = "tcp://katerose-fsn-cdr-dev.tailscale.svc.cluster.local:2375"
|
|
"ap-sydney" = "tcp://wolfgang-syd-cdr-dev.tailscale.svc.cluster.local:2375"
|
|
"za-cpt" = "tcp://schonkopf-cpt-cdr-dev.tailscale.svc.cluster.local:2375"
|
|
}
|
|
|
|
repo_base_dir = data.coder_parameter.repo_base_dir.value == "~" ? "/home/coder" : replace(data.coder_parameter.repo_base_dir.value, "/^~\\//", "/home/coder/")
|
|
repo_dir = replace(try(module.git-clone[0].repo_dir, ""), "/^~\\//", "/home/coder/")
|
|
container_name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
|
|
}
|
|
|
|
# --- Parameters ---
|
|
|
|
data "coder_parameter" "repo_base_dir" {
|
|
type = "string"
|
|
name = "Repository Base Directory"
|
|
default = "~"
|
|
description = "The directory specified will be created (if missing) and [coder/vscode-coder](https://github.com/coder/vscode-coder) will be automatically cloned into [base directory]/vscode-coder."
|
|
mutable = true
|
|
}
|
|
|
|
locals {
|
|
default_regions = {
|
|
"north-america" : "us-pittsburgh"
|
|
"europe" : "eu-helsinki"
|
|
"australia" : "ap-sydney"
|
|
"africa" : "za-cpt"
|
|
}
|
|
|
|
user_groups = data.coder_workspace_owner.me.groups
|
|
user_region = coalescelist([
|
|
for g in local.user_groups :
|
|
local.default_regions[g] if contains(keys(local.default_regions), g)
|
|
], ["us-pittsburgh"])[0]
|
|
}
|
|
|
|
data "coder_parameter" "region" {
|
|
type = "string"
|
|
name = "Region"
|
|
icon = "/emojis/1f30e.png"
|
|
default = local.user_region
|
|
option {
|
|
icon = "/emojis/1f1fa-1f1f8.png"
|
|
name = "Pittsburgh"
|
|
value = "us-pittsburgh"
|
|
}
|
|
option {
|
|
icon = "/emojis/1f1e9-1f1ea.png"
|
|
name = "Falkenstein"
|
|
// For legacy reasons, this host is labelled `eu-helsinki` but it's
|
|
// actually in Germany now.
|
|
value = "eu-helsinki"
|
|
}
|
|
option {
|
|
icon = "/emojis/1f1e6-1f1fa.png"
|
|
name = "Sydney"
|
|
value = "ap-sydney"
|
|
}
|
|
option {
|
|
icon = "/emojis/1f1ff-1f1e6.png"
|
|
name = "Cape Town"
|
|
value = "za-cpt"
|
|
}
|
|
}
|
|
|
|
data "coder_parameter" "res_mon_memory_threshold" {
|
|
type = "number"
|
|
name = "Memory usage threshold"
|
|
default = 80
|
|
description = "The memory usage threshold used in resources monitoring to trigger notifications."
|
|
mutable = true
|
|
validation {
|
|
min = 0
|
|
max = 100
|
|
}
|
|
}
|
|
|
|
data "coder_parameter" "res_mon_volume_threshold" {
|
|
type = "number"
|
|
name = "Volume usage threshold"
|
|
default = 90
|
|
description = "The volume usage threshold used in resources monitoring to trigger notifications."
|
|
mutable = true
|
|
validation {
|
|
min = 0
|
|
max = 100
|
|
}
|
|
}
|
|
|
|
data "coder_parameter" "res_mon_volume_path" {
|
|
type = "string"
|
|
name = "Volume path"
|
|
default = "/home/coder"
|
|
description = "The path monitored in resources monitoring to trigger notifications."
|
|
mutable = true
|
|
}
|
|
|
|
data "coder_parameter" "use_ai_gateway" {
|
|
type = "bool"
|
|
name = "Use AI Gateway"
|
|
default = true
|
|
description = "If enabled, AI requests will be sent via AI Gateway."
|
|
mutable = true
|
|
}
|
|
|
|
# Fallback when AI Gateway is disabled. Injected by dogfood/main.tf
|
|
# from the CODER_DOGFOOD_ANTHROPIC_API_KEY secret.
|
|
variable "anthropic_api_key" {
|
|
type = string
|
|
description = "Anthropic API key, used when AI Gateway is disabled."
|
|
default = ""
|
|
sensitive = true
|
|
}
|
|
|
|
data "coder_parameter" "ide_choices" {
|
|
type = "list(string)"
|
|
name = "Select IDEs"
|
|
form_type = "multi-select"
|
|
mutable = true
|
|
description = "Choose one or more IDEs to enable in your workspace"
|
|
default = jsonencode(["vscode", "code-server", "cursor"])
|
|
option {
|
|
name = "VS Code Desktop"
|
|
value = "vscode"
|
|
icon = "/icon/code.svg"
|
|
}
|
|
option {
|
|
name = "code-server"
|
|
value = "code-server"
|
|
icon = "/icon/code.svg"
|
|
}
|
|
option {
|
|
name = "VS Code Web"
|
|
value = "vscode-web"
|
|
icon = "/icon/code.svg"
|
|
}
|
|
option {
|
|
name = "Cursor"
|
|
value = "cursor"
|
|
icon = "/icon/cursor.svg"
|
|
}
|
|
option {
|
|
name = "Windsurf"
|
|
value = "windsurf"
|
|
icon = "/icon/windsurf.svg"
|
|
}
|
|
option {
|
|
name = "Zed"
|
|
value = "zed"
|
|
icon = "/icon/zed.svg"
|
|
}
|
|
}
|
|
|
|
data "coder_parameter" "vscode_channel" {
|
|
count = contains(jsondecode(data.coder_parameter.ide_choices.value), "vscode") ? 1 : 0
|
|
type = "string"
|
|
name = "VS Code Desktop channel"
|
|
description = "Choose the VS Code Desktop channel"
|
|
mutable = true
|
|
default = "stable"
|
|
option {
|
|
value = "stable"
|
|
name = "Stable"
|
|
icon = "/icon/code.svg"
|
|
}
|
|
option {
|
|
value = "insiders"
|
|
name = "Insiders"
|
|
icon = "/icon/code-insiders.svg"
|
|
}
|
|
}
|
|
|
|
# --- Providers and data sources ---
|
|
|
|
provider "docker" {
|
|
host = lookup(local.docker_host, data.coder_parameter.region.value)
|
|
}
|
|
|
|
provider "coder" {}
|
|
|
|
data "coder_external_auth" "github" {
|
|
id = "github"
|
|
}
|
|
|
|
data "coder_workspace" "me" {}
|
|
data "coder_workspace_owner" "me" {}
|
|
data "coder_workspace_tags" "tags" {
|
|
tags = {
|
|
"cluster" : "dogfood-v2"
|
|
"env" : "gke"
|
|
}
|
|
}
|
|
|
|
# --- Modules ---
|
|
|
|
module "dotfiles" {
|
|
count = data.coder_workspace.me.start_count
|
|
source = "dev.registry.coder.com/coder/dotfiles/coder"
|
|
version = "1.4.2"
|
|
agent_id = coder_agent.dev.id
|
|
}
|
|
|
|
module "git-config" {
|
|
count = data.coder_workspace.me.start_count
|
|
source = "dev.registry.coder.com/coder/git-config/coder"
|
|
version = "1.0.34"
|
|
agent_id = coder_agent.dev.id
|
|
allow_email_change = true
|
|
}
|
|
|
|
module "git-clone" {
|
|
count = data.coder_workspace.me.start_count
|
|
source = "dev.registry.coder.com/coder/git-clone/coder"
|
|
version = "1.3.0"
|
|
agent_id = coder_agent.dev.id
|
|
url = "https://github.com/coder/vscode-coder"
|
|
base_dir = local.repo_base_dir
|
|
post_clone_script = <<-EOT
|
|
#!/usr/bin/env bash
|
|
set -eux -o pipefail
|
|
coder exp sync start git-clone
|
|
coder exp sync complete git-clone
|
|
EOT
|
|
}
|
|
|
|
module "personalize" {
|
|
count = data.coder_workspace.me.start_count
|
|
source = "dev.registry.coder.com/coder/personalize/coder"
|
|
version = "1.0.32"
|
|
agent_id = coder_agent.dev.id
|
|
}
|
|
|
|
module "code-server" {
|
|
count = contains(jsondecode(data.coder_parameter.ide_choices.value), "code-server") ? data.coder_workspace.me.start_count : 0
|
|
source = "dev.registry.coder.com/coder/code-server/coder"
|
|
version = "1.5.0"
|
|
agent_id = coder_agent.dev.id
|
|
folder = local.repo_dir
|
|
auto_install_extensions = true
|
|
group = "Web Editors"
|
|
}
|
|
|
|
module "vscode-web" {
|
|
count = contains(jsondecode(data.coder_parameter.ide_choices.value), "vscode-web") ? data.coder_workspace.me.start_count : 0
|
|
source = "dev.registry.coder.com/coder/vscode-web/coder"
|
|
version = "1.6.0"
|
|
agent_id = coder_agent.dev.id
|
|
folder = local.repo_dir
|
|
extensions = ["github.copilot"]
|
|
auto_install_extensions = true
|
|
accept_license = true
|
|
group = "Web Editors"
|
|
}
|
|
|
|
module "filebrowser" {
|
|
count = data.coder_workspace.me.start_count
|
|
source = "dev.registry.coder.com/coder/filebrowser/coder"
|
|
version = "1.1.5"
|
|
agent_id = coder_agent.dev.id
|
|
agent_name = "dev"
|
|
}
|
|
|
|
module "coder-login" {
|
|
count = data.coder_workspace.me.start_count
|
|
source = "dev.registry.coder.com/coder/coder-login/coder"
|
|
version = "1.1.1"
|
|
agent_id = coder_agent.dev.id
|
|
}
|
|
|
|
module "cursor" {
|
|
count = contains(jsondecode(data.coder_parameter.ide_choices.value), "cursor") ? data.coder_workspace.me.start_count : 0
|
|
source = "dev.registry.coder.com/coder/cursor/coder"
|
|
version = "1.4.1"
|
|
agent_id = coder_agent.dev.id
|
|
folder = local.repo_dir
|
|
}
|
|
|
|
module "windsurf" {
|
|
count = contains(jsondecode(data.coder_parameter.ide_choices.value), "windsurf") ? data.coder_workspace.me.start_count : 0
|
|
source = "dev.registry.coder.com/coder/windsurf/coder"
|
|
version = "1.3.1"
|
|
agent_id = coder_agent.dev.id
|
|
folder = local.repo_dir
|
|
}
|
|
|
|
module "zed" {
|
|
count = contains(jsondecode(data.coder_parameter.ide_choices.value), "zed") ? data.coder_workspace.me.start_count : 0
|
|
source = "dev.registry.coder.com/coder/zed/coder"
|
|
version = "1.1.5"
|
|
agent_id = coder_agent.dev.id
|
|
agent_name = "dev"
|
|
folder = local.repo_dir
|
|
}
|
|
|
|
# --- Agent ---
|
|
|
|
resource "coder_agent" "dev" {
|
|
arch = "amd64"
|
|
os = "linux"
|
|
dir = local.repo_dir
|
|
env = merge(
|
|
{
|
|
OIDC_TOKEN : data.coder_workspace_owner.me.oidc_access_token,
|
|
},
|
|
data.coder_parameter.use_ai_gateway.value ? {
|
|
ANTHROPIC_BASE_URL : "https://dev.coder.com/api/v2/ai-gateway/anthropic",
|
|
ANTHROPIC_AUTH_TOKEN : data.coder_workspace_owner.me.session_token,
|
|
OPENAI_BASE_URL : "https://dev.coder.com/api/v2/ai-gateway/openai/v1",
|
|
OPENAI_API_KEY : data.coder_workspace_owner.me.session_token,
|
|
} : {}
|
|
)
|
|
startup_script_behavior = "blocking"
|
|
|
|
display_apps {
|
|
vscode = contains(jsondecode(data.coder_parameter.ide_choices.value), "vscode") && try(data.coder_parameter.vscode_channel[0].value, "stable") == "stable"
|
|
vscode_insiders = contains(jsondecode(data.coder_parameter.ide_choices.value), "vscode") && try(data.coder_parameter.vscode_channel[0].value, "stable") == "insiders"
|
|
}
|
|
|
|
metadata {
|
|
display_name = "CPU Usage"
|
|
key = "cpu_usage"
|
|
order = 0
|
|
script = "coder stat cpu"
|
|
interval = 10
|
|
timeout = 1
|
|
}
|
|
|
|
metadata {
|
|
display_name = "RAM Usage"
|
|
key = "ram_usage"
|
|
order = 1
|
|
script = "coder stat mem"
|
|
interval = 10
|
|
timeout = 1
|
|
}
|
|
|
|
metadata {
|
|
display_name = "/home Usage"
|
|
key = "home_usage"
|
|
order = 2
|
|
script = "sudo du -sh /home/coder | awk '{print $1}'"
|
|
interval = 3600
|
|
timeout = 60
|
|
}
|
|
|
|
metadata {
|
|
display_name = "Word of the Day"
|
|
key = "word"
|
|
order = 3
|
|
script = <<EOT
|
|
#!/usr/bin/env bash
|
|
curl -o - --silent https://www.merriam-webster.com/word-of-the-day 2>&1 | awk ' $0 ~ "Word of the Day: [A-z]+" { print $5; exit }'
|
|
EOT
|
|
interval = 86400
|
|
timeout = 5
|
|
}
|
|
|
|
resources_monitoring {
|
|
memory {
|
|
enabled = true
|
|
threshold = data.coder_parameter.res_mon_memory_threshold.value
|
|
}
|
|
volume {
|
|
enabled = true
|
|
threshold = data.coder_parameter.res_mon_volume_threshold.value
|
|
path = data.coder_parameter.res_mon_volume_path.value
|
|
}
|
|
}
|
|
|
|
startup_script = <<-EOT
|
|
#!/usr/bin/env bash
|
|
set -eux -o pipefail
|
|
|
|
function cleanup() {
|
|
coder exp sync complete agent-startup
|
|
touch /tmp/.coder-startup-script.done
|
|
}
|
|
trap cleanup EXIT
|
|
coder exp sync start agent-startup
|
|
|
|
# Start dbus to suppress noisy Electron/Chromium errors in tests.
|
|
sudo mkdir -p /run/dbus
|
|
sudo dbus-daemon --system 2>/dev/null || true
|
|
|
|
if ! gh api user --jq .login >/dev/null 2>&1; then
|
|
echo "Logging into GitHub CLI..."
|
|
if ! coder external-auth access-token github | gh auth login --hostname github.com --with-token; then
|
|
echo "GitHub CLI authentication failed; gh commands may not work."
|
|
fi
|
|
else
|
|
echo "GitHub CLI already has working credentials."
|
|
fi
|
|
EOT
|
|
}
|
|
|
|
# --- Scripts ---
|
|
|
|
resource "coder_script" "install-deps" {
|
|
agent_id = coder_agent.dev.id
|
|
display_name = "Installing Dependencies"
|
|
run_on_start = true
|
|
start_blocks_login = false
|
|
script = <<EOT
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
trap 'coder exp sync complete install-deps' EXIT
|
|
coder exp sync want install-deps git-clone
|
|
coder exp sync start install-deps
|
|
|
|
cd "${local.repo_dir}" && pnpm install
|
|
|
|
# Playwright maintains the list of system libs each Chromium version
|
|
# needs, so we don't have to track them in the Dockerfile.
|
|
# Electron additionally requires libgtk-3-0 which Playwright skips
|
|
# because its bundled Chromium doesn't use GTK. xauth is needed
|
|
# by xvfb-run for integration tests.
|
|
sudo npx --yes playwright install-deps chromium
|
|
sudo apt-get install -y --no-install-recommends libgtk-3-0 xauth
|
|
EOT
|
|
}
|
|
|
|
# --- Docker resources ---
|
|
|
|
resource "coder_metadata" "home_volume" {
|
|
resource_id = docker_volume.home_volume.id
|
|
daily_cost = 1
|
|
}
|
|
|
|
resource "docker_volume" "home_volume" {
|
|
name = "coder-${data.coder_workspace.me.id}-home"
|
|
lifecycle {
|
|
ignore_changes = all
|
|
}
|
|
labels {
|
|
label = "coder.owner"
|
|
value = data.coder_workspace_owner.me.name
|
|
}
|
|
labels {
|
|
label = "coder.owner_id"
|
|
value = data.coder_workspace_owner.me.id
|
|
}
|
|
labels {
|
|
label = "coder.workspace_id"
|
|
value = data.coder_workspace.me.id
|
|
}
|
|
labels {
|
|
label = "coder.workspace_name_at_creation"
|
|
value = data.coder_workspace.me.name
|
|
}
|
|
}
|
|
|
|
data "docker_registry_image" "vscode_coder" {
|
|
name = "codercom/oss-dogfood-vscode-coder:latest"
|
|
}
|
|
|
|
resource "docker_image" "vscode_coder" {
|
|
name = "codercom/oss-dogfood-vscode-coder:latest@${data.docker_registry_image.vscode_coder.sha256_digest}"
|
|
pull_triggers = [
|
|
data.docker_registry_image.vscode_coder.sha256_digest,
|
|
filesha1("Dockerfile"),
|
|
]
|
|
keep_locally = true
|
|
}
|
|
|
|
resource "docker_container" "workspace" {
|
|
lifecycle {
|
|
ignore_changes = [name, hostname, labels, env, entrypoint]
|
|
}
|
|
count = data.coder_workspace.me.start_count
|
|
image = docker_image.vscode_coder.name
|
|
name = local.container_name
|
|
hostname = data.coder_workspace.me.name
|
|
entrypoint = ["sh", "-c", coder_agent.dev.init_script]
|
|
memory = 8192
|
|
|
|
# Allow the agent to finish cleanup traps on shutdown.
|
|
destroy_grace_seconds = 60
|
|
stop_timeout = 60
|
|
stop_signal = "SIGINT"
|
|
|
|
env = [
|
|
"CODER_AGENT_TOKEN=${coder_agent.dev.token}",
|
|
]
|
|
host {
|
|
host = "host.docker.internal"
|
|
ip = "host-gateway"
|
|
}
|
|
volumes {
|
|
container_path = "/home/coder/"
|
|
volume_name = docker_volume.home_volume.name
|
|
read_only = false
|
|
}
|
|
labels {
|
|
label = "coder.owner"
|
|
value = data.coder_workspace_owner.me.name
|
|
}
|
|
labels {
|
|
label = "coder.owner_id"
|
|
value = data.coder_workspace_owner.me.id
|
|
}
|
|
labels {
|
|
label = "coder.workspace_id"
|
|
value = data.coder_workspace.me.id
|
|
}
|
|
labels {
|
|
label = "coder.workspace_name"
|
|
value = data.coder_workspace.me.name
|
|
}
|
|
}
|
|
|
|
resource "coder_metadata" "container_info" {
|
|
count = data.coder_workspace.me.start_count
|
|
resource_id = docker_container.workspace[0].id
|
|
item {
|
|
key = "memory"
|
|
value = docker_container.workspace[0].memory
|
|
}
|
|
item {
|
|
key = "region"
|
|
value = data.coder_parameter.region.option[index(data.coder_parameter.region.option.*.value, data.coder_parameter.region.value)].name
|
|
}
|
|
}
|
|
|
|
module "claude-code" {
|
|
count = data.coder_workspace.me.start_count
|
|
source = "dev.registry.coder.com/coder/claude-code/coder"
|
|
version = "5.2.0"
|
|
enable_ai_gateway = data.coder_parameter.use_ai_gateway.value
|
|
anthropic_api_key = data.coder_parameter.use_ai_gateway.value ? "" : var.anthropic_api_key
|
|
agent_id = coder_agent.dev.id
|
|
workdir = local.repo_dir
|
|
}
|
|
|
|
resource "coder_app" "claude" {
|
|
agent_id = coder_agent.dev.id
|
|
slug = "claude"
|
|
display_name = "Claude Code"
|
|
icon = "/icon/claude.svg"
|
|
open_in = "slim-window"
|
|
command = <<-EOT
|
|
#!/bin/bash
|
|
set -e
|
|
cd "${local.repo_dir}"
|
|
exec tmux new-session -A -s claude claude
|
|
EOT
|
|
}
|