122 lines
3.9 KiB
HCL
122 lines
3.9 KiB
HCL
# code-server workspace template — one dev container per project, with
|
|
# browser VS Code and Claude Code (extension + CLI) ready on first open.
|
|
#
|
|
# Source of truth is the repo copy at
|
|
# pyinfra/framework/compose/coder/templates/code-server/main.tf; pyinfra
|
|
# ships it to /srv/docker/coder/templates/, mounted read-only into the
|
|
# server container at /templates. Push after edits:
|
|
# cd /srv/docker/coder
|
|
# docker compose exec coder coder templates push code-server \
|
|
# --directory /templates/code-server --yes
|
|
|
|
terraform {
|
|
required_providers {
|
|
coder = {
|
|
source = "coder/coder"
|
|
}
|
|
docker = {
|
|
source = "kreuzwerker/docker"
|
|
}
|
|
}
|
|
}
|
|
|
|
provider "coder" {}
|
|
|
|
# Talks to the host daemon via the socket mounted into the server
|
|
# container (default unix:///var/run/docker.sock) — workspace containers
|
|
# are siblings of the compose stacks, not children.
|
|
provider "docker" {}
|
|
|
|
data "coder_workspace" "me" {}
|
|
data "coder_workspace_owner" "me" {}
|
|
|
|
resource "coder_agent" "main" {
|
|
arch = "amd64"
|
|
os = "linux"
|
|
|
|
# Claude Code CLI — native installer, lands in ~/.local/bin inside the
|
|
# persisted home volume, so this is a no-op after the first start. The
|
|
# code-server extension bundles its own CLI, but having `claude` on
|
|
# PATH enables tmux-based long runs in the workspace terminal.
|
|
startup_script = <<-EOT
|
|
set -e
|
|
command -v claude >/dev/null 2>&1 || curl -fsSL https://claude.ai/install.sh | bash
|
|
EOT
|
|
|
|
env = {
|
|
GIT_AUTHOR_NAME = data.coder_workspace_owner.me.full_name
|
|
GIT_AUTHOR_EMAIL = data.coder_workspace_owner.me.email
|
|
GIT_COMMITTER_NAME = data.coder_workspace_owner.me.full_name
|
|
GIT_COMMITTER_EMAIL = data.coder_workspace_owner.me.email
|
|
}
|
|
|
|
metadata {
|
|
display_name = "CPU"
|
|
key = "cpu"
|
|
script = "coder stat cpu"
|
|
interval = 10
|
|
timeout = 1
|
|
}
|
|
metadata {
|
|
display_name = "RAM"
|
|
key = "mem"
|
|
script = "coder stat mem"
|
|
interval = 10
|
|
timeout = 1
|
|
}
|
|
}
|
|
|
|
# Browser VS Code inside the workspace, surfaced as a dashboard app.
|
|
# Extensions install from Open VSX — anthropic.claude-code is the
|
|
# official Claude Code extension
|
|
# (https://open-vsx.org/extension/Anthropic/claude-code). OAuth creds
|
|
# land in ~/.claude inside the home volume and survive rebuilds.
|
|
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
|
|
folder = "/home/coder/project"
|
|
|
|
extensions = [
|
|
"anthropic.claude-code",
|
|
]
|
|
}
|
|
|
|
# Home survives workspace stop/start AND template-driven rebuilds —
|
|
# ignore_changes keeps Terraform from recreating the volume (and wiping
|
|
# ~/.claude, extensions, repos) when template metadata shifts.
|
|
resource "docker_volume" "home" {
|
|
name = "coder-${data.coder_workspace.me.id}-home"
|
|
lifecycle {
|
|
ignore_changes = all
|
|
}
|
|
}
|
|
|
|
resource "docker_container" "workspace" {
|
|
# start_count is 0 when the workspace is stopped — the container is
|
|
# deleted but the home volume above persists. This is what idle
|
|
# autostop reclaims: RAM back to the inference stacks.
|
|
count = data.coder_workspace.me.start_count
|
|
image = "codercom/enterprise-base:ubuntu"
|
|
name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
|
|
hostname = data.coder_workspace.me.name
|
|
|
|
# Agent bootstrap, rewritten to reach the control plane through the
|
|
# docker bridge (the workspace is a sibling container; "localhost"
|
|
# inside it isn't the Coder server).
|
|
entrypoint = ["sh", "-c", replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")]
|
|
env = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"]
|
|
|
|
host {
|
|
host = "host.docker.internal"
|
|
ip = "host-gateway"
|
|
}
|
|
|
|
volumes {
|
|
container_path = "/home/coder"
|
|
volume_name = docker_volume.home.name
|
|
read_only = false
|
|
}
|
|
}
|