Skip to content
This repository was archived by the owner on May 15, 2025. It is now read-only.

Commit 03245ff

Browse files
authored
Merge branch 'main' into code-server-open-in
2 parents 210481b + 9d6dd83 commit 03245ff

File tree

15 files changed

+600
-55
lines changed

15 files changed

+600
-55
lines changed

.github/dependabot.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ updates:
44
directory: "/"
55
schedule:
66
interval: "weekly"
7+
commit-message:
8+
prefix: "chore"

.github/typos.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[default.extend-words]
2+
muc = "muc" # For Munich location code

.github/workflows/ci.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,13 @@ jobs:
4848
- name: Format
4949
run: bun fmt:ci
5050
- name: typos-action
51-
uses: crate-ci/typos@v1.17.2
51+
uses: crate-ci/typos@v1.31.1
52+
with:
53+
config: .github/typos.toml
5254
- name: Lint
5355
run: bun lint
5456
# Disable version check until https://github.com/coder/modules/pull/426 is merged.
55-
# This will alow us to use seperate versioning for each module without failing CI. The backend already supports that.
57+
# This will allow us to use separate versioning for each module without failing CI. The backend already supports that.
5658
# - name: Check version
5759
# shell: bash
5860
# run: |

.icons/devcontainers.svg

Lines changed: 2 additions & 0 deletions
Loading

claude-code/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
1414
```tf
1515
module "claude-code" {
1616
source = "registry.coder.com/modules/claude-code/coder"
17-
version = "1.0.31"
17+
version = "1.1.0"
1818
agent_id = coder_agent.example.id
1919
folder = "/home/coder"
2020
install_claude_code = true
@@ -25,7 +25,7 @@ module "claude-code" {
2525
### Prerequisites
2626

2727
- Node.js and npm must be installed in your workspace to install Claude Code
28-
- `screen` must be installed in your workspace to run Claude Code in the background
28+
- Either `screen` or `tmux` must be installed in your workspace to run Claude Code in the background
2929
- You must add the [Coder Login](https://registry.coder.com/modules/coder-login) module to your template
3030

3131
The `codercom/oss-dogfood:latest` container image can be used for testing on container-based workspaces.
@@ -43,7 +43,7 @@ The `codercom/oss-dogfood:latest` container image can be used for testing on con
4343
> Join our [Discord channel](https://discord.gg/coder) or
4444
> [contact us](https://coder.com/contact) to get help or share feedback.
4545
46-
Your workspace must have `screen` installed to use this.
46+
Your workspace must have either `screen` or `tmux` installed to use this.
4747

4848
```tf
4949
variable "anthropic_api_key" {
@@ -83,14 +83,14 @@ resource "coder_agent" "main" {
8383
module "claude-code" {
8484
count = data.coder_workspace.me.start_count
8585
source = "registry.coder.com/modules/claude-code/coder"
86-
version = "1.0.31"
86+
version = "1.1.0"
8787
agent_id = coder_agent.example.id
8888
folder = "/home/coder"
8989
install_claude_code = true
9090
claude_code_version = "0.2.57"
9191
9292
# Enable experimental features
93-
experiment_use_screen = true
93+
experiment_use_screen = true # Or use experiment_use_tmux = true to use tmux instead
9494
experiment_report_tasks = true
9595
}
9696
```
@@ -102,7 +102,7 @@ Run Claude Code as a standalone app in your workspace. This will install Claude
102102
```tf
103103
module "claude-code" {
104104
source = "registry.coder.com/modules/claude-code/coder"
105-
version = "1.0.31"
105+
version = "1.1.0"
106106
agent_id = coder_agent.example.id
107107
folder = "/home/coder"
108108
install_claude_code = true

claude-code/main.tf

Lines changed: 87 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,35 @@ variable "experiment_use_screen" {
5454
default = false
5555
}
5656

57+
variable "experiment_use_tmux" {
58+
type = bool
59+
description = "Whether to use tmux instead of screen for running Claude Code in the background."
60+
default = false
61+
}
62+
5763
variable "experiment_report_tasks" {
5864
type = bool
5965
description = "Whether to enable task reporting."
6066
default = false
6167
}
6268

69+
variable "experiment_pre_install_script" {
70+
type = string
71+
description = "Custom script to run before installing Claude Code."
72+
default = null
73+
}
74+
75+
variable "experiment_post_install_script" {
76+
type = string
77+
description = "Custom script to run after installing Claude Code."
78+
default = null
79+
}
80+
81+
locals {
82+
encoded_pre_install_script = var.experiment_pre_install_script != null ? base64encode(var.experiment_pre_install_script) : ""
83+
encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : ""
84+
}
85+
6386
# Install and Initialize Claude Code
6487
resource "coder_script" "claude_code" {
6588
agent_id = var.agent_id
@@ -74,6 +97,14 @@ resource "coder_script" "claude_code" {
7497
command -v "$1" >/dev/null 2>&1
7598
}
7699
100+
# Run pre-install script if provided
101+
if [ -n "${local.encoded_pre_install_script}" ]; then
102+
echo "Running pre-install script..."
103+
echo "${local.encoded_pre_install_script}" | base64 -d > /tmp/pre_install.sh
104+
chmod +x /tmp/pre_install.sh
105+
/tmp/pre_install.sh
106+
fi
107+
77108
# Install Claude Code if enabled
78109
if [ "${var.install_claude_code}" = "true" ]; then
79110
if ! command_exists npm; then
@@ -84,11 +115,52 @@ resource "coder_script" "claude_code" {
84115
npm install -g @anthropic-ai/claude-code@${var.claude_code_version}
85116
fi
86117
118+
# Run post-install script if provided
119+
if [ -n "${local.encoded_post_install_script}" ]; then
120+
echo "Running post-install script..."
121+
echo "${local.encoded_post_install_script}" | base64 -d > /tmp/post_install.sh
122+
chmod +x /tmp/post_install.sh
123+
/tmp/post_install.sh
124+
fi
125+
87126
if [ "${var.experiment_report_tasks}" = "true" ]; then
88127
echo "Configuring Claude Code to report tasks via Coder MCP..."
89128
coder exp mcp configure claude-code ${var.folder}
90129
fi
91130
131+
# Handle terminal multiplexer selection (tmux or screen)
132+
if [ "${var.experiment_use_tmux}" = "true" ] && [ "${var.experiment_use_screen}" = "true" ]; then
133+
echo "Error: Both experiment_use_tmux and experiment_use_screen cannot be true simultaneously."
134+
echo "Please set only one of them to true."
135+
exit 1
136+
fi
137+
138+
# Run with tmux if enabled
139+
if [ "${var.experiment_use_tmux}" = "true" ]; then
140+
echo "Running Claude Code in the background with tmux..."
141+
142+
# Check if tmux is installed
143+
if ! command_exists tmux; then
144+
echo "Error: tmux is not installed. Please install tmux manually."
145+
exit 1
146+
fi
147+
148+
touch "$HOME/.claude-code.log"
149+
150+
export LANG=en_US.UTF-8
151+
export LC_ALL=en_US.UTF-8
152+
153+
# Create a new tmux session in detached mode
154+
tmux new-session -d -s claude-code -c ${var.folder} "claude"
155+
156+
# Send the prompt to the tmux session if needed
157+
if [ -n "$CODER_MCP_CLAUDE_TASK_PROMPT" ]; then
158+
tmux send-keys -t claude-code "$CODER_MCP_CLAUDE_TASK_PROMPT"
159+
sleep 5
160+
tmux send-keys -t claude-code Enter
161+
fi
162+
fi
163+
92164
# Run with screen if enabled
93165
if [ "${var.experiment_use_screen}" = "true" ]; then
94166
echo "Running Claude Code in the background..."
@@ -149,20 +221,27 @@ resource "coder_app" "claude_code" {
149221
#!/bin/bash
150222
set -e
151223
152-
if [ "${var.experiment_use_screen}" = "true" ]; then
224+
export LANG=en_US.UTF-8
225+
export LC_ALL=en_US.UTF-8
226+
227+
if [ "${var.experiment_use_tmux}" = "true" ]; then
228+
if tmux has-session -t claude-code 2>/dev/null; then
229+
echo "Attaching to existing Claude Code tmux session." | tee -a "$HOME/.claude-code.log"
230+
tmux attach-session -t claude-code
231+
else
232+
echo "Starting a new Claude Code tmux session." | tee -a "$HOME/.claude-code.log"
233+
tmux new-session -s claude-code -c ${var.folder} "claude --dangerously-skip-permissions | tee -a \"$HOME/.claude-code.log\"; exec bash"
234+
fi
235+
elif [ "${var.experiment_use_screen}" = "true" ]; then
153236
if screen -list | grep -q "claude-code"; then
154-
export LANG=en_US.UTF-8
155-
export LC_ALL=en_US.UTF-8
156-
echo "Attaching to existing Claude Code session." | tee -a "$HOME/.claude-code.log"
237+
echo "Attaching to existing Claude Code screen session." | tee -a "$HOME/.claude-code.log"
157238
screen -xRR claude-code
158239
else
159-
echo "Starting a new Claude Code session." | tee -a "$HOME/.claude-code.log"
160-
screen -S claude-code bash -c 'export LANG=en_US.UTF-8; export LC_ALL=en_US.UTF-8; claude --dangerously-skip-permissions | tee -a "$HOME/.claude-code.log"; exec bash'
240+
echo "Starting a new Claude Code screen session." | tee -a "$HOME/.claude-code.log"
241+
screen -S claude-code bash -c 'claude --dangerously-skip-permissions | tee -a "$HOME/.claude-code.log"; exec bash'
161242
fi
162243
else
163244
cd ${var.folder}
164-
export LANG=en_US.UTF-8
165-
export LC_ALL=en_US.UTF-8
166245
claude
167246
fi
168247
EOT

devcontainers-cli/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
display_name: devcontainers-cli
3+
description: devcontainers-cli module provides an easy way to install @devcontainers/cli into a workspace
4+
icon: ../.icons/devcontainers.svg
5+
verified: true
6+
maintainer_github: coder
7+
tags: [devcontainers]
8+
---
9+
10+
# devcontainers-cli
11+
12+
The devcontainers-cli module provides an easy way to install [`@devcontainers/cli`](https://github.com/devcontainers/cli) into a workspace. It can be used within any workspace as it runs only if
13+
@devcontainers/cli is not installed yet.
14+
`npm` is required and should be pre-installed in order for the module to work.
15+
16+
```tf
17+
module "devcontainers-cli" {
18+
source = "registry.coder.com/modules/devcontainers-cli/coder"
19+
version = "1.0.1"
20+
agent_id = coder_agent.example.id
21+
}
22+
```

devcontainers-cli/main.test.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { describe, expect, it } from "bun:test";
2+
import {
3+
execContainer,
4+
executeScriptInContainer,
5+
findResourceInstance,
6+
runContainer,
7+
runTerraformApply,
8+
runTerraformInit,
9+
testRequiredVariables,
10+
type TerraformState,
11+
} from "../test";
12+
13+
const executeScriptInContainerWithPackageManager = async (
14+
state: TerraformState,
15+
image: string,
16+
packageManager: string,
17+
shell = "sh",
18+
): Promise<{
19+
exitCode: number;
20+
stdout: string[];
21+
stderr: string[];
22+
}> => {
23+
const instance = findResourceInstance(state, "coder_script");
24+
const id = await runContainer(image);
25+
26+
// Install the specified package manager
27+
if (packageManager === "npm") {
28+
await execContainer(id, [shell, "-c", "apk add nodejs npm"]);
29+
} else if (packageManager === "pnpm") {
30+
await execContainer(id, [
31+
shell,
32+
"-c",
33+
`wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL="$(which sh)" sh -`,
34+
]);
35+
} else if (packageManager === "yarn") {
36+
await execContainer(id, [
37+
shell,
38+
"-c",
39+
"apk add nodejs npm && npm install -g yarn",
40+
]);
41+
}
42+
43+
const resp = await execContainer(id, [shell, "-c", instance.script]);
44+
const stdout = resp.stdout.trim().split("\n");
45+
const stderr = resp.stderr.trim().split("\n");
46+
return {
47+
exitCode: resp.exitCode,
48+
stdout,
49+
stderr,
50+
};
51+
};
52+
53+
describe("devcontainers-cli", async () => {
54+
await runTerraformInit(import.meta.dir);
55+
56+
testRequiredVariables(import.meta.dir, {
57+
agent_id: "some-agent-id",
58+
});
59+
60+
it("misses all package managers", async () => {
61+
const state = await runTerraformApply(import.meta.dir, {
62+
agent_id: "some-agent-id",
63+
});
64+
const output = await executeScriptInContainer(state, "docker:dind");
65+
expect(output.exitCode).toBe(1);
66+
expect(output.stderr).toEqual([
67+
"ERROR: No supported package manager (npm, pnpm, yarn) is installed. Please install one first.",
68+
]);
69+
}, 15000);
70+
71+
it("installs devcontainers-cli with npm", async () => {
72+
const state = await runTerraformApply(import.meta.dir, {
73+
agent_id: "some-agent-id",
74+
});
75+
76+
const output = await executeScriptInContainerWithPackageManager(
77+
state,
78+
"docker:dind",
79+
"npm",
80+
);
81+
expect(output.exitCode).toBe(0);
82+
83+
expect(output.stdout[0]).toEqual(
84+
"Installing @devcontainers/cli using npm...",
85+
);
86+
expect(output.stdout[output.stdout.length - 1]).toEqual(
87+
"🥳 @devcontainers/cli has been installed into /usr/local/bin/devcontainer!",
88+
);
89+
}, 15000);
90+
91+
it("installs devcontainers-cli with yarn", async () => {
92+
const state = await runTerraformApply(import.meta.dir, {
93+
agent_id: "some-agent-id",
94+
});
95+
96+
const output = await executeScriptInContainerWithPackageManager(
97+
state,
98+
"docker:dind",
99+
"yarn",
100+
);
101+
expect(output.exitCode).toBe(0);
102+
103+
expect(output.stdout[0]).toEqual(
104+
"Installing @devcontainers/cli using yarn...",
105+
);
106+
expect(output.stdout[output.stdout.length - 1]).toEqual(
107+
"🥳 @devcontainers/cli has been installed into /usr/local/bin/devcontainer!",
108+
);
109+
}, 15000);
110+
111+
it("displays warning if docker is not installed", async () => {
112+
const state = await runTerraformApply(import.meta.dir, {
113+
agent_id: "some-agent-id",
114+
});
115+
116+
const output = await executeScriptInContainerWithPackageManager(
117+
state,
118+
"alpine",
119+
"npm",
120+
);
121+
expect(output.exitCode).toBe(0);
122+
123+
expect(output.stdout[0]).toEqual(
124+
"WARNING: Docker was not found but is required to use @devcontainers/cli, please make sure it is available.",
125+
);
126+
expect(output.stdout[output.stdout.length - 1]).toEqual(
127+
"🥳 @devcontainers/cli has been installed into /usr/local/bin/devcontainer!",
128+
);
129+
}, 15000);
130+
});

devcontainers-cli/main.tf

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
terraform {
2+
required_version = ">= 1.0"
3+
4+
required_providers {
5+
coder = {
6+
source = "coder/coder"
7+
version = ">= 0.17"
8+
}
9+
}
10+
}
11+
12+
variable "agent_id" {
13+
type = string
14+
description = "The ID of a Coder agent."
15+
}
16+
17+
resource "coder_script" "devcontainers-cli" {
18+
agent_id = var.agent_id
19+
display_name = "devcontainers-cli"
20+
icon = "/icon/devcontainers.svg"
21+
script = templatefile("${path.module}/run.sh", {})
22+
run_on_start = true
23+
}

0 commit comments

Comments
 (0)