Split single-container Coder template into per-env templates
The old root main.tf tried to switch dev profiles inside one shared container via a coder_parameter dropdown; the settings-application path looked for a *.json cache file that never existed (the cache was written as *.code-profile), so profile settings never actually applied, and VS Code extensions were copied from extensions/ (which turns out to be Claude Code plugin bundles, not real VS Code extension packages). Replace it with one independent Coder template per environment (templates/default, 3d-printing, cobol, python, ttrpg, web). Each reads its matching profile-templates/*.code-profile file at template-push time via file()/jsondecode(), feeds the extension ID list straight into the code-server module's `extensions` input, and writes the raw settings.json text via a coder_script - no runtime Gitea zip download needed anymore. Also add scripts/cli-setup-wizard.sh, hooked into every new interactive shell until completed, offering to install/log into GitHub Copilot CLI, Google Antigravity CLI, and Claude Code CLI. VS Code extensions are deliberately not asked about there since Terraform already handles them. All six templates verified with `terraform init`/`validate` against the real coder-server container on octo-winsrv. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
}
|
||||
docker = {
|
||||
source = "kreuzwerker/docker"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
env_name = "Python Engineering"
|
||||
profile = jsondecode(file("${path.module}/../../profile-templates/Python.code-profile"))
|
||||
settings_raw = jsondecode(local.profile.settings).settings
|
||||
extensions = [for e in jsondecode(local.profile.extensions) : e.identifier.id]
|
||||
}
|
||||
|
||||
variable "docker_socket" {
|
||||
default = ""
|
||||
description = "(Optional) Docker socket URI"
|
||||
type = string
|
||||
}
|
||||
|
||||
provider "docker" {
|
||||
# Defaulting to null if the variable is an empty string lets us have an optional variable without having to set our own default
|
||||
host = var.docker_socket != "" ? var.docker_socket : null
|
||||
}
|
||||
|
||||
data "coder_provisioner" "me" {}
|
||||
data "coder_workspace" "me" {}
|
||||
data "coder_workspace_owner" "me" {}
|
||||
|
||||
resource "coder_agent" "main" {
|
||||
arch = data.coder_provisioner.me.arch
|
||||
os = "linux"
|
||||
startup_script = <<-EOT
|
||||
set -e
|
||||
|
||||
# Prepare user home with default files on first start.
|
||||
if [ ! -f ~/.init_done ]; then
|
||||
cp -rT /etc/skel ~
|
||||
touch ~/.init_done
|
||||
fi
|
||||
|
||||
# Add any commands that should be executed at workspace startup (e.g install requirements, start a program, etc) here
|
||||
EOT
|
||||
|
||||
# These environment variables allow you to make Git commits right away after creating a
|
||||
# workspace. Note that they take precedence over configuration defined in ~/.gitconfig!
|
||||
# You can remove this block if you'd prefer to configure Git manually or using
|
||||
# dotfiles. (see docs/dotfiles.md)
|
||||
env = {
|
||||
GIT_AUTHOR_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
|
||||
GIT_AUTHOR_EMAIL = "${data.coder_workspace_owner.me.email}"
|
||||
GIT_COMMITTER_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
|
||||
GIT_COMMITTER_EMAIL = "${data.coder_workspace_owner.me.email}"
|
||||
}
|
||||
|
||||
# The following metadata blocks are optional. They are used to display
|
||||
# information about your workspace in the dashboard. You can remove them
|
||||
# if you don't want to display any information.
|
||||
# For basic resources, you can use the `coder stat` command.
|
||||
# If you need more control, you can write your own script.
|
||||
metadata {
|
||||
display_name = "CPU Usage"
|
||||
key = "0_cpu_usage"
|
||||
script = "coder stat cpu"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "RAM Usage"
|
||||
key = "1_ram_usage"
|
||||
script = "coder stat mem"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Home Disk"
|
||||
key = "3_home_disk"
|
||||
script = "coder stat disk --path $${HOME}"
|
||||
interval = 60
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "CPU Usage (Host)"
|
||||
key = "4_cpu_usage_host"
|
||||
script = "coder stat cpu --host"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Memory Usage (Host)"
|
||||
key = "5_mem_usage_host"
|
||||
script = "coder stat mem --host"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Load Average (Host)"
|
||||
key = "6_load_host"
|
||||
# get load avg scaled by number of cores
|
||||
script = <<EOT
|
||||
echo "`cat /proc/loadavg | awk '{ print $1 }'` `nproc`" | awk '{ printf "%0.2f", $1/$2 }'
|
||||
EOT
|
||||
interval = 60
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Swap Usage (Host)"
|
||||
key = "7_swap_host"
|
||||
script = <<EOT
|
||||
free -b | awk '/^Swap/ { printf("%.1f/%.1f", $3/1024.0/1024.0/1024.0, $2/1024.0/1024.0/1024.0) }'
|
||||
EOT
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
}
|
||||
|
||||
# See https://registry.coder.com/modules/coder/code-server
|
||||
# `extensions` is populated at template-push time from this env's
|
||||
# profile-templates/*.code-profile file, so no interactive prompt is
|
||||
# needed for VS Code extensions - Terraform handles it declaratively.
|
||||
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
|
||||
|
||||
# Pass the target folder here natively
|
||||
folder = "/home/coder/workspace"
|
||||
|
||||
extensions = local.extensions
|
||||
order = 1
|
||||
}
|
||||
|
||||
# See https://registry.coder.com/modules/coder/jetbrains
|
||||
module "jetbrains" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/jetbrains/coder"
|
||||
version = "~> 1.1"
|
||||
agent_id = coder_agent.main.id
|
||||
agent_name = "main"
|
||||
folder = "/home/coder"
|
||||
tooltip = "You need to [install JetBrains Toolbox](https://coder.com/docs/user-guides/workspace-access/jetbrains/toolbox) to use this app."
|
||||
}
|
||||
|
||||
resource "docker_volume" "home_volume" {
|
||||
name = "coder-${data.coder_workspace.me.id}-home"
|
||||
# Protect the volume from being deleted due to changes in attributes.
|
||||
lifecycle {
|
||||
ignore_changes = all
|
||||
}
|
||||
# Add labels in Docker to keep track of orphan resources.
|
||||
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
|
||||
}
|
||||
# This field becomes outdated if the workspace is renamed but can
|
||||
# be useful for debugging or cleaning out dangling volumes.
|
||||
labels {
|
||||
label = "coder.workspace_name_at_creation"
|
||||
value = data.coder_workspace.me.name
|
||||
}
|
||||
}
|
||||
|
||||
resource "docker_container" "workspace" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
image = "codercom/enterprise-base:ubuntu"
|
||||
# Uses lower() to avoid Docker restriction on container names.
|
||||
name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
|
||||
# Hostname makes the shell more user friendly: coder@my-workspace:~$
|
||||
hostname = data.coder_workspace.me.name
|
||||
# Use the docker gateway if the access URL is 127.0.0.1
|
||||
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"
|
||||
}
|
||||
host {
|
||||
host = "code.octoturge.com"
|
||||
ip = "host-gateway"
|
||||
}
|
||||
volumes {
|
||||
container_path = "/home/coder"
|
||||
volume_name = docker_volume.home_volume.name
|
||||
read_only = false
|
||||
}
|
||||
|
||||
# Add labels in Docker to keep track of orphan resources.
|
||||
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
|
||||
}
|
||||
labels {
|
||||
label = "coder.profile"
|
||||
value = local.env_name
|
||||
}
|
||||
}
|
||||
|
||||
# Writes this env's VS Code settings.json, sourced straight from the
|
||||
# matching profile-templates/*.code-profile file at template-push time.
|
||||
resource "coder_script" "apply_settings" {
|
||||
agent_id = coder_agent.main.id
|
||||
display_name = "Apply ${local.env_name} VS Code Settings"
|
||||
run_on_start = true
|
||||
script = <<-EOT
|
||||
#!/bin/bash
|
||||
set -e
|
||||
mkdir -p "$HOME/workspace"
|
||||
mkdir -p "$HOME/.local/share/code-server/User"
|
||||
echo '${base64encode(local.settings_raw)}' | base64 -d > "$HOME/.local/share/code-server/User/settings.json"
|
||||
EOT
|
||||
}
|
||||
|
||||
# Drops the shared CLI setup wizard onto the workspace and hooks it into
|
||||
# every new interactive shell (via .bashrc) until the user completes it.
|
||||
# See ../../scripts/cli-setup-wizard.sh for what it actually asks.
|
||||
resource "coder_script" "cli_setup_wizard" {
|
||||
agent_id = coder_agent.main.id
|
||||
display_name = "Install CLI Setup Wizard"
|
||||
run_on_start = true
|
||||
script = <<-EOT
|
||||
#!/bin/bash
|
||||
set -e
|
||||
mkdir -p /opt/coder
|
||||
echo '${base64encode(file("${path.module}/../../scripts/cli-setup-wizard.sh"))}' | base64 -d > /opt/coder/cli-setup-wizard.sh
|
||||
chmod +x /opt/coder/cli-setup-wizard.sh
|
||||
|
||||
MARKER="# >>> coder cli setup wizard >>>"
|
||||
if ! grep -qF "$MARKER" "$HOME/.bashrc" 2>/dev/null; then
|
||||
{
|
||||
echo ""
|
||||
echo "$MARKER"
|
||||
echo 'export PATH="$HOME/.local/bin:$PATH"'
|
||||
echo 'source /opt/coder/cli-setup-wizard.sh'
|
||||
echo "# <<< coder cli setup wizard <<<"
|
||||
} >> "$HOME/.bashrc"
|
||||
fi
|
||||
EOT
|
||||
}
|
||||
Reference in New Issue
Block a user