Compare commits

..

22 Commits

Author SHA1 Message Date
Hugo Dutka 507b73a07e feat(agentapi): use wildcard alllowed hosts (#320)
Since https://github.com/coder/agentapi/pull/49 was merged, agentapi by
default only accepts requests with the `Host` header set to localhost,
127.0.0.1, or [::1]. In Coder, agentapi is served behind a reverse proxy
as a workspace app, so we need to use a wildcard
`AGENTAPI_ALLOWED_HOSTS` for agentapi-based modules to continue working.

This PR updates the claude code and agentapi modules, and a subsequent
PR will update modules that are based on the agentapi module.
2025-08-11 16:23:01 +02:00
Jullian Pepito 814f765313 fix(jetbrains): Ties var.group to the coder_app. (#310)
Co-authored-by: Jullian Pepito <jullian@mac.lan>
2025-08-11 13:22:11 +05:00
Atif Ali 92a154f54a chore: deploy registry on changes to contributers information (#315) 2025-08-10 00:53:39 +05:00
Ben Potter 7aa7dea5ad Fix contributor avatars and docs: use avatar key and correct anomaly image extension (#312)
Co-authored-by: bpmct <22407953+bpmct@users.noreply.github.com>
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
2025-08-10 00:27:31 +05:00
sharkymark 59b0472125 feat: sharkymark profile and claude docker template (#304)
Closes #

## Description

registry profile creation and template submission for claude on docker

## Type of Change

- [x] New template
- [ ] Bug fix
- [ ] Feature/enhancement
- [ ] Documentation
- [ ] Other

## Template Information

**Path:** `registry/sharkymark/templates/docker-claude`  
**New version:** `v1.0.0`  
**Breaking change:** [ ] Yes [x] No

## Testing & Validation

- [ ] Tests pass (`bun test`)
- [ ] Code formatted (`bun run fmt`)
- [x] Changes tested locally

## Related Issues

n/a

---------

Co-authored-by: DevCats <christofer@coder.com>
2025-08-09 18:40:04 +02:00
Muhammad Atif Ali 673caf2e95 Revert "chore(examples): add MODULE_NAME.tftest.hcl to new module sample and make run.sh optional"
This reverts commit a5edad7f17.
2025-08-08 17:29:11 +05:00
Muhammad Atif Ali ab5ff4b4be Revert "chore(test): add terraform tests for jetbrains, zed, code-server and keep mixed mode"
This reverts commit fb657b875d.
2025-08-08 17:29:11 +05:00
Muhammad Atif Ali f5a68b500b Revert "chore(test): migrate to terraform test and add initial .tftest for zed"
This reverts commit 016d4dc523.
2025-08-08 17:29:11 +05:00
Muhammad Atif Ali a5edad7f17 chore(examples): add MODULE_NAME.tftest.hcl to new module sample and make run.sh optional 2025-08-08 16:36:30 +05:00
Muhammad Atif Ali fb657b875d chore(test): add terraform tests for jetbrains, zed, code-server and keep mixed mode
- Add .tftest.hcl for jetbrains, zed, and code-server
- Remove Bun tests for these migrated modules only
- Keep Bun tests for other modules during transition
- Update contributing guide to mention terraform test
- Include runner script to execute terraform tests across modules
2025-08-08 16:33:35 +05:00
Muhammad Atif Ali 016d4dc523 chore(test): migrate to terraform test and add initial .tftest for zed
Replace Bun-based test runner with Terraform native testing. Adds script to discover and run tests across modules and updates docs/scripts to use terraform test.
2025-08-08 13:31:35 +05:00
Muhammad Atif Ali c8d99cfba3 fix: correct terraform state arg and log typos
- test/test.ts: ensure `-state` is immediately followed by the state file to avoid apply failures
- readmevalidation: fix two logger message typos (processing/processed)
2025-08-08 13:15:52 +05:00
Cian Johnston 74c8698566 feat: goose: add support for subdomain=false (#299)
Updates https://github.com/coder/coder/issues/18779
Builds on https://github.com/coder/registry/pull/297

## Description

Adds support for specifying `subdomain = false` in the agentapi module.
Change added in https://github.com/coder/registry/pull/297
NOTE: `AGENTAPI_CHAT_BASE_PATH` is exported before running `main.sh` in
agentapi, so this environment variable is available to calling modules
if `var.subdomain = false`.

## Type of Change

- [ ] New module
- [ ] Bug fix
- [X] Feature/enhancement
- [ ] Documentation
- [ ] Other

## Testing & Validation

- [X] Tests pass (`bun test`)
- [X] Code formatted (`bun run fmt`)
- [X] Changes tested locally

## Related Issues

https://github.com/coder/coder/issues/18779
2025-08-07 22:12:32 -05:00
DevCats 03333991a4 feat: introduce automated tag and release process in maintainer guide (#280) 2025-08-07 16:06:13 +05:00
Susana Ferreira 2b0dba4ed1 chore: add description to JetBrains IDEs parameter (#303)
## Description

<!-- Briefly describe what this PR does and why -->

This PR adds a description field to the `jetbrains_ides`
`coder_parameter`.

This allows the JetBrains IDEs parameter to display a helpful
description in both the Coder UI and the CLI, improving clarity for
users when selecting which IDEs to configure in a workspace.

<img width="1102" height="252" alt="Screenshot 2025-08-07 at 11 04 13"
src="https://github.com/user-attachments/assets/90c78088-700a-4152-8a16-4b8c88c52e2c"
/>

## Type of Change

- [ ] New module
- [ ] Bug fix
- [x] Feature/enhancement
- [ ] Documentation
- [ ] Other

## Testing & Validation

- [x] Tests pass (`bun test`)
- [x] Code formatted (`bun run fmt`)
- [x] Changes tested locally

## Related Issues

https://github.com/coder/coder/issues/19145
2025-08-07 11:42:48 +01:00
Cian Johnston 57c900b2c9 feat(agentapi): Add support for running under a subdomain (#297)
Updates https://github.com/coder/coder/issues/18779

A separate PR will update dependant modules `goose` and `aider`.

## Description

* Adds `subdomain` argument to `agentapi` module
* Updates `agentapi` module to set `AGENTAPI_CHAT_BASE_PATH` to an
autogenerated path if `var.subdomain = false`
* Updates default `agentapi` version to `v0.3.3` to support running
without subdomain

## Type of Change

- [ ] New module
- [ ] Bug fix
- [x] Feature/enhancement
- [ ] Documentation
- [ ] Other


## Testing & Validation

- [X] Tests pass (`bun test`)
- [X] Code formatted (`bun run fmt`)
- [x] Changes tested locally

## Related Issues

- https://github.com/coder/coder/issues/18779
2025-08-06 12:38:49 +01:00
Ben Potter 0ccee61192 chore: remove unnecessary parameter (#282)
Co-authored-by: DevCats <christofer@coder.com>
2025-08-06 07:05:18 +05:00
dependabot[bot] 494dc4b8a1 chore(deps): bump google-github-actions/auth from 2.1.11 to 2.1.12 (#288)
Bumps
[google-github-actions/auth](https://github.com/google-github-actions/auth)
from 2.1.11 to 2.1.12.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/google-github-actions/auth/releases">google-github-actions/auth's
releases</a>.</em></p>
<blockquote>
<h2>v2.1.12</h2>
<h2>What's Changed</h2>
<ul>
<li>Add retries for getIDToken by <a
href="https://github.com/sethvargo"><code>@​sethvargo</code></a> in <a
href="https://redirect.github.com/google-github-actions/auth/pull/502">google-github-actions/auth#502</a></li>
<li>Release: v2.1.12 by <a
href="https://github.com/google-github-actions-bot"><code>@​google-github-actions-bot</code></a>
in <a
href="https://redirect.github.com/google-github-actions/auth/pull/503">google-github-actions/auth#503</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/google-github-actions/auth/compare/v2.1.11...v2.1.12">https://github.com/google-github-actions/auth/compare/v2.1.11...v2.1.12</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/google-github-actions/auth/commit/b7593ed2efd1c1617e1b0254da33b86225adb2a5"><code>b7593ed</code></a>
Release: v2.1.12 (<a
href="https://redirect.github.com/google-github-actions/auth/issues/503">#503</a>)</li>
<li><a
href="https://github.com/google-github-actions/auth/commit/c1ee334b4fb145a02e9d8343bb2e9f0dd06e586b"><code>c1ee334</code></a>
Add retries for getIDToken (<a
href="https://redirect.github.com/google-github-actions/auth/issues/502">#502</a>)</li>
<li>See full diff in <a
href="https://github.com/google-github-actions/auth/compare/140bb5113ffb6b65a7e9b937a81fa96cf5064462...b7593ed2efd1c1617e1b0254da33b86225adb2a5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google-github-actions/auth&package-manager=github_actions&previous-version=2.1.11&new-version=2.1.12)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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 merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 21:01:49 -05:00
Hugo Dutka 3b135ad4a4 fix(claude-code): revert workaround (#298)
The workaround introduced in https://github.com/coder/registry/pull/283
sometimes causes Coder to associate the Coder agent with the
`terraform_data` resource instead of a compute resource. Additionally,
it creates a new agent once a workspace is stopped.

See https://codercom.slack.com/archives/C08PHACTZRB/p1754391012982629
for more details.

<img width="2480" height="1312" alt="image (6)"
src="https://github.com/user-attachments/assets/6ccf2b40-92e7-4c67-b61f-b26888753e72"
/>

Related to https://github.com/coder/coder/issues/18776.
2025-08-05 13:28:58 +02:00
Danielle Maywood 258591833f fix(devcontainers-cli): allow yarn to install when packageManager not yarn (#287)
On our dogfood workspaces, we fail to install `@devcontainers/cli` with
`yarn` because our agent directory `/home/coder/coder` contains a
`package.json` with `packageManager` being set to `pnpm`. This change
instead ensures to run `yarn global add` inside the
`$CODER_SCRIPT_DATA_DIR` so that we don't read a `package.json` and
cause things to break.
2025-08-04 13:00:13 +01:00
Michael Orlov 3efc22c589 fix/amazon-q mcp integration (#248)
Co-authored-by: Michael Orlov <michaelo@amdocs.com>
Co-authored-by: DevCats <christofer@coder.com>
2025-08-03 06:27:46 +00:00
Hugo Dutka 8ba4c323c2 fix(claude-code): workaround for a coder bug (#283)
Workaround to address https://github.com/coder/coder/issues/18776
2025-08-02 16:21:13 +02:00
34 changed files with 733 additions and 59 deletions
+2 -1
View File
@@ -14,6 +14,7 @@ on:
paths:
- ".github/workflows/deploy-registry.yaml"
- "registry/**/templates/**"
- "registry/**/README.md"
- ".icons/**"
jobs:
@@ -29,7 +30,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Authenticate with Google Cloud
uses: google-github-actions/auth@140bb5113ffb6b65a7e9b937a81fa96cf5064462
uses: google-github-actions/auth@b7593ed2efd1c1617e1b0254da33b86225adb2a5
with:
workload_identity_provider: projects/309789351055/locations/global/workloadIdentityPools/github-actions/providers/github
service_account: registry-v2-github@coder-registry-1.iam.gserviceaccount.com
+2 -2
View File
@@ -89,7 +89,7 @@ Create `registry/[your-username]/README.md`:
---
display_name: "Your Name"
bio: "Brief description of who you are and what you do"
avatar_url: "./.images/avatar.png"
avatar: "./.images/avatar.png"
github: "your-username"
linkedin: "https://www.linkedin.com/in/your-username" # Optional
website: "https://yourwebsite.com" # Optional
@@ -102,7 +102,7 @@ status: "community"
Brief description of who you are and what you do.
```
> **Note**: The `avatar_url` must point to `./.images/avatar.png` or `./.images/avatar.svg`.
> **Note**: The `avatar` must point to `./.images/avatar.png` or `./.images/avatar.svg`.
### 2. Generate Module Files
+51 -5
View File
@@ -42,12 +42,58 @@ go build ./cmd/readmevalidation && ./readmevalidation
## Making a Release
### Create Release Tags
### Automated Tag and Release Process
After merging a PR:
After merging a PR, use the automated script to create and push release tags:
1. Get the new version from the PR (shown as `old → new`)
2. Checkout the merge commit and create the tag:
**Prerequisites:**
- Ensure all module versions are updated in their respective README files (the script uses this as the source of truth)
- Make sure you have the necessary permissions to push tags to the repository
**Steps:**
1. **Checkout the merge commit:**
```bash
git checkout MERGE_COMMIT_ID
```
2. **Run the tag release script:**
```bash
./scripts/tag_release.sh
```
3. **Review and confirm:**
- The script will automatically scan all modules in the registry
- It will detect which modules need version bumps by comparing README versions to existing tags
- A summary will be displayed showing which modules need tagging
- Confirm the list is correct when prompted
4. **Automatic tagging:**
- After confirmation, the script will automatically create all necessary release tags
- Tags will be pushed to the remote repository
- The script operates on the current checked-out commit
**Example output:**
```text
🔍 Scanning all modules for missing release tags...
📦 coder/code-server: v4.1.2 (needs tag)
✅ coder/dotfiles: v1.0.5 (already tagged)
## Tags to be created:
- `release/coder/code-server/v4.1.2`
❓ Do you want to proceed with creating and pushing these release tags?
Continue? [y/N]: y
```
### Manual Process (Fallback)
If the automated script fails, you can manually tag and release modules:
```bash
# Checkout the merge commit
@@ -81,7 +127,7 @@ tags: ["tag1", "tag2"]
```yaml
display_name: "Your Name"
bio: "Brief description of who you are and what you do"
avatar_url: "./.images/avatar.png"
avatar: "./.images/avatar.png"
github: "username"
linkedin: "https://www.linkedin.com/in/username" # Optional
website: "https://yourwebsite.com" # Optional
+2 -2
View File
@@ -336,12 +336,12 @@ func validateAllCoderResourceFilesOfType(resourceType string) error {
return err
}
logger.Info(context.Background(), "rocessing README files", "num_files", len(allReadmeFiles))
logger.Info(context.Background(), "processing README files", "num_files", len(allReadmeFiles))
resources, err := parseCoderResourceReadmeFiles(resourceType, allReadmeFiles)
if err != nil {
return err
}
logger.Info(context.Background(), "rocessed README files as valid Coder resources", "num_files", len(resources), "type", resourceType)
logger.Info(context.Background(), "processed README files as valid Coder resources", "num_files", len(resources), "type", resourceType)
if err := validateCoderResourceRelativeURLs(resources); err != nil {
return err
+1 -1
View File
@@ -1,7 +1,7 @@
---
display_name: "Jay Kumar"
bio: "I'm a Software Engineer :)"
avatar_url: "./.images/avatar.png"
avatar: "./.images/avatar.jpeg"
github: "35C4n0r"
linkedin: "https://www.linkedin.com/in/jaykum4r"
support_email: "work.jaykumar@gmail.com"
@@ -118,7 +118,6 @@ data "coder_workspace_preset" "default" {
EOT
"preview_port" = "4200"
"container_image" = "codercom/example-universal:ubuntu"
"jetbrains_ide" = "PY"
}
# Pre-builds is a Coder Premium
+1 -1
View File
@@ -16,7 +16,7 @@ The AgentAPI module is a building block for modules that need to run an AgentAPI
```tf
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "1.0.2"
version = "1.1.1"
agent_id = var.agent_id
web_app_slug = local.app_slug
@@ -148,4 +148,105 @@ describe("agentapi", async () => {
]);
expect(respAgentAPI.exitCode).toBe(0);
});
test("no-subdomain-base-path", async () => {
const { id } = await setup({
moduleVariables: {
agentapi_subdomain: "false",
},
});
const respModuleScript = await execModuleScript(id);
expect(respModuleScript.exitCode).toBe(0);
await expectAgentAPIStarted(id);
const agentApiStartLog = await readFileContainer(
id,
"/home/coder/test-agentapi-start.log",
);
expect(agentApiStartLog).toContain("Using AGENTAPI_CHAT_BASE_PATH: /@default/default.foo/apps/agentapi-web/chat");
});
test("validate-agentapi-version", async () => {
const cases = [
{
moduleVariables: {
agentapi_version: "v0.3.2",
},
shouldThrow: "",
},
{
moduleVariables: {
agentapi_version: "v0.3.3",
},
shouldThrow: "",
},
{
moduleVariables: {
agentapi_version: "v0.0.1",
agentapi_subdomain: "false",
},
shouldThrow: "Running with subdomain = false is only supported by agentapi >= v0.3.3.",
},
{
moduleVariables: {
agentapi_version: "v0.3.2",
agentapi_subdomain: "false",
},
shouldThrow: "Running with subdomain = false is only supported by agentapi >= v0.3.3.",
},
{
moduleVariables: {
agentapi_version: "v0.3.3",
agentapi_subdomain: "false",
},
shouldThrow: "",
},
{
moduleVariables: {
agentapi_version: "v0.3.999",
agentapi_subdomain: "false",
},
shouldThrow: "",
},
{
moduleVariables: {
agentapi_version: "v0.999.999",
agentapi_subdomain: "false",
},
},
{
moduleVariables: {
agentapi_version: "v999.999.999",
agentapi_subdomain: "false",
},
},
{
moduleVariables: {
agentapi_version: "arbitrary-string-bypasses-validation",
},
shouldThrow: "",
}
];
for (const { moduleVariables, shouldThrow } of cases) {
if (shouldThrow) {
expect(setup({ moduleVariables: moduleVariables as Record<string, string> })).rejects.toThrow(shouldThrow);
} else {
expect(setup({ moduleVariables: moduleVariables as Record<string, string> })).resolves.toBeDefined();
}
}
});
test("agentapi-allowed-hosts", async () => {
// verify that the agentapi binary has access to the AGENTAPI_ALLOWED_HOSTS environment variable
// set in main.sh
const { id } = await setup();
await execModuleScript(id);
await expectAgentAPIStarted(id);
const agentApiStartLog = await readFileContainer(
id,
"/home/coder/agentapi-mock.log",
);
expect(agentApiStartLog).toContain("AGENTAPI_ALLOWED_HOSTS: *");
});
});
+36 -3
View File
@@ -117,7 +117,7 @@ variable "install_agentapi" {
variable "agentapi_version" {
type = string
description = "The version of AgentAPI to install."
default = "v0.2.3"
default = "v0.3.3"
}
variable "agentapi_port" {
@@ -126,6 +126,31 @@ variable "agentapi_port" {
default = 3284
}
locals {
# agentapi_subdomain_false_min_version_expr matches a semantic version >= v0.3.3.
# Initial support was added in v0.3.1 but configuration via environment variable
# was added in v0.3.3.
# This is unfortunately a regex because there is no builtin way to compare semantic versions in Terraform.
# See: https://regex101.com/r/oHPyRa/1
agentapi_subdomain_false_min_version_expr = "^v(0\\.(3\\.[3-9]|3.[1-9]\\d+|[4-9]\\.\\d+|[1-9]\\d+\\.\\d+)|[1-9]\\d*\\.\\d+\\.\\d+)$"
}
variable "agentapi_subdomain" {
type = bool
description = "Whether to use a subdomain for AgentAPI."
default = true
validation {
condition = var.agentapi_subdomain || (
# If version doesn't look like a valid semantic version, just allow it.
# Note that boolean operators do not short-circuit in Terraform.
can(regex("^v\\d+\\.\\d+\\.\\d+$", var.agentapi_version)) ?
can(regex(local.agentapi_subdomain_false_min_version_expr, var.agentapi_version)) :
true
)
error_message = "Running with subdomain = false is only supported by agentapi >= v0.3.3."
}
}
variable "module_dir_name" {
type = string
description = "Name of the subdirectory in the home directory for module files."
@@ -140,7 +165,14 @@ locals {
encoded_post_install_script = var.post_install_script != null ? base64encode(var.post_install_script) : ""
agentapi_start_script_b64 = base64encode(var.start_script)
agentapi_wait_for_start_script_b64 = base64encode(file("${path.module}/scripts/agentapi-wait-for-start.sh"))
main_script = file("${path.module}/scripts/main.sh")
// Chat base path is only set if not using a subdomain.
// NOTE:
// - Initial support for --chat-base-path was added in v0.3.1 but configuration
// via environment variable AGENTAPI_CHAT_BASE_PATH was added in v0.3.3.
// - As CODER_WORKSPACE_AGENT_NAME is a recent addition we use agent ID
// for backward compatibility.
agentapi_chat_base_path = var.agentapi_subdomain ? "" : "/@${data.coder_workspace_owner.me.name}/${data.coder_workspace.me.name}.${var.agent_id}/apps/${var.web_app_slug}/chat"
main_script = file("${path.module}/scripts/main.sh")
}
resource "coder_script" "agentapi" {
@@ -165,6 +197,7 @@ resource "coder_script" "agentapi" {
ARG_WAIT_FOR_START_SCRIPT="$(echo -n '${local.agentapi_wait_for_start_script_b64}' | base64 -d)" \
ARG_POST_INSTALL_SCRIPT="$(echo -n '${local.encoded_post_install_script}' | base64 -d)" \
ARG_AGENTAPI_PORT='${var.agentapi_port}' \
ARG_AGENTAPI_CHAT_BASE_PATH='${local.agentapi_chat_base_path}' \
/tmp/main.sh
EOT
run_on_start = true
@@ -178,7 +211,7 @@ resource "coder_app" "agentapi_web" {
icon = var.web_app_icon
order = var.web_app_order
group = var.web_app_group
subdomain = true
subdomain = var.agentapi_subdomain
healthcheck {
url = "http://localhost:${var.agentapi_port}/status"
interval = 3
@@ -13,6 +13,7 @@ START_SCRIPT="$ARG_START_SCRIPT"
WAIT_FOR_START_SCRIPT="$ARG_WAIT_FOR_START_SCRIPT"
POST_INSTALL_SCRIPT="$ARG_POST_INSTALL_SCRIPT"
AGENTAPI_PORT="$ARG_AGENTAPI_PORT"
AGENTAPI_CHAT_BASE_PATH="${ARG_AGENTAPI_CHAT_BASE_PATH:-}"
set +o nounset
command_exists() {
@@ -92,5 +93,9 @@ export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
cd "${WORKDIR}"
export AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}"
# Disable host header check since AgentAPI is proxied by Coder (which does its own validation)
export AGENTAPI_ALLOWED_HOSTS="*"
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}"
+10 -1
View File
@@ -24,7 +24,16 @@ export const setupContainer = async ({
});
const coderScript = findResourceInstance(state, "coder_script");
const id = await runContainer(image ?? "codercom/enterprise-node:latest");
return { id, coderScript, cleanup: () => removeContainer(id) };
return {
id, coderScript, cleanup: async () => {
if (process.env["DEBUG"] === "true" || process.env["DEBUG"] === "1" || process.env["DEBUG"] === "yes") {
console.log(`Not removing container ${id} in debug mode`);
console.log(`Run "docker rm -f ${id}" to remove it manually.`);
} else {
await removeContainer(id);
}
}
};
};
export const loadTestFile = async (
@@ -1,11 +1,13 @@
#!/usr/bin/env node
const http = require("http");
const fs = require("fs");
const args = process.argv.slice(2);
const portIdx = args.findIndex((arg) => arg === "--port") + 1;
const port = portIdx ? args[portIdx] : 3284;
console.log(`starting server on port ${port}`);
fs.writeFileSync("/home/coder/agentapi-mock.log", `AGENTAPI_ALLOWED_HOSTS: ${process.env.AGENTAPI_ALLOWED_HOSTS}`);
http
.createServer(function (_request, response) {
@@ -11,6 +11,12 @@ 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
AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}"
if [ -n "$AGENTAPI_CHAT_BASE_PATH" ]; then
echo "Using AGENTAPI_CHAT_BASE_PATH: $AGENTAPI_CHAT_BASE_PATH" >>/home/coder/test-agentapi-start.log
export AGENTAPI_CHAT_BASE_PATH
fi
agentapi server --port "$port" --term-width 67 --term-height 1190 -- \
bash -c aiagent \
>"$log_file_path" 2>&1
+7 -25
View File
@@ -125,24 +125,7 @@ variable "ai_prompt" {
locals {
encoded_pre_install_script = var.experiment_pre_install_script != null ? base64encode(var.experiment_pre_install_script) : ""
encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : ""
# We need to use allowed tools to limit the context Amazon Q receives.
# Amazon Q can't handle big contexts, and the `create_template_version` tool
# has a description that's too long.
mcp_json = <<EOT
{
"mcpServers": {
"coder": {
"command": "coder",
"args": ["exp", "mcp", "server", "--allowed-tools", "coder_report_task"],
"env": {
"CODER_MCP_APP_STATUS_SLUG": "amazon-q"
}
}
}
}
EOT
encoded_mcp_json = base64encode(local.mcp_json)
full_prompt = <<-EOT
full_prompt = <<-EOT
${var.system_prompt}
Your first task is:
@@ -211,6 +194,12 @@ resource "coder_script" "amazon_q" {
cd "$PREV_DIR"
echo "Extracted auth tarball"
if [ "${var.experiment_report_tasks}" = "true" ]; then
echo "Configuring Amazon Q to report tasks via Coder MCP..."
q mcp add --name coder --command "coder" --args "exp,mcp,server,--allowed-tools,coder_report_task" --env "CODER_MCP_APP_STATUS_SLUG=amazon-q" --scope global --force
echo "Added Coder MCP server to Amazon Q configuration"
fi
if [ -n "${local.encoded_post_install_script}" ]; then
echo "Running post-install script..."
echo "${local.encoded_post_install_script}" | base64 -d > /tmp/post_install.sh
@@ -218,13 +207,6 @@ resource "coder_script" "amazon_q" {
/tmp/post_install.sh
fi
if [ "${var.experiment_report_tasks}" = "true" ]; then
echo "Configuring Amazon Q to report tasks via Coder MCP..."
mkdir -p ~/.aws/amazonq
echo "${local.encoded_mcp_json}" | base64 -d > ~/.aws/amazonq/mcp.json
echo "Created the ~/.aws/amazonq/mcp.json configuration file"
fi
if [ "${var.experiment_use_tmux}" = "true" ] && [ "${var.experiment_use_screen}" = "true" ]; then
echo "Error: Both experiment_use_tmux and experiment_use_screen cannot be true simultaneously."
echo "Please set only one of them to true."
+3 -3
View File
@@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "2.0.4"
version = "2.0.7"
agent_id = coder_agent.example.id
folder = "/home/coder"
install_claude_code = true
@@ -84,7 +84,7 @@ resource "coder_agent" "main" {
module "claude-code" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/claude-code/coder"
version = "2.0.4"
version = "2.0.7"
agent_id = coder_agent.example.id
folder = "/home/coder"
install_claude_code = true
@@ -102,7 +102,7 @@ Run Claude Code as a standalone app in your workspace. This will install Claude
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "2.0.4"
version = "2.0.7"
agent_id = coder_agent.example.id
folder = "/home/coder"
install_claude_code = true
@@ -10,6 +10,7 @@ import path from "path";
import {
execContainer,
findResourceInstance,
readFileContainer,
removeContainer,
runContainer,
runTerraformApply,
@@ -319,4 +320,21 @@ describe("claude-code", async () => {
agentApiUrl: "http://localhost:3284",
});
});
// verify that the agentapi binary has access to the AGENTAPI_ALLOWED_HOSTS environment variable
// set in main.tf
test("agentapi-allowed-hosts", async () => {
const { id } = await setup();
const respModuleScript = await execModuleScript(id);
expect(respModuleScript.exitCode).toBe(0);
await expectAgentAPIStarted(id);
const agentApiStartLog = await readFileContainer(
id,
"/home/coder/agentapi-mock.log",
);
expect(agentApiStartLog).toContain("AGENTAPI_ALLOWED_HOSTS: *");
});
});
@@ -241,6 +241,10 @@ resource "coder_script" "claude_code" {
export LC_ALL=en_US.UTF-8
cd "${local.workdir}"
# Disable host header check since AgentAPI is proxied by Coder (which does its own validation)
export AGENTAPI_ALLOWED_HOSTS="*"
nohup "$module_path/scripts/agentapi-start.sh" use_prompt &> "$module_path/agentapi-start.log" &
"$module_path/scripts/agentapi-wait-for-start.sh"
EOT
@@ -20,6 +20,8 @@ if (
process.exit(1);
}
fs.writeFileSync("/home/coder/agentapi-mock.log", `AGENTAPI_ALLOWED_HOSTS: ${process.env.AGENTAPI_ALLOWED_HOSTS}`);
console.log(`starting server on port ${port}`);
http
@@ -15,7 +15,7 @@ The devcontainers-cli module provides an easy way to install [`@devcontainers/cl
```tf
module "devcontainers-cli" {
source = "registry.coder.com/coder/devcontainers-cli/coder"
version = "1.0.31"
version = "1.0.32"
agent_id = coder_agent.example.id
}
```
@@ -45,6 +45,8 @@ const executeScriptInContainerWithPackageManager = async (
console.log(path);
await execContainer(id, [shell, "-c", "mkdir -p /tmp/coder-script-data"]);
const resp = await execContainer(
id,
[shell, "-c", instance.script],
@@ -52,6 +54,8 @@ const executeScriptInContainerWithPackageManager = async (
"--env",
"CODER_SCRIPT_BIN_DIR=/tmp/coder-script-data/bin",
"--env",
"CODER_SCRIPT_DATA_DIR=/tmp/coder-script-data",
"--env",
`PATH=${path}:/tmp/coder-script-data/bin`,
],
);
+7 -1
View File
@@ -1,5 +1,11 @@
#!/usr/bin/env sh
# We want to cd into `$CODER_SCRIPT_DATA_DIR` as the current directory
# might contain a `package.json` with `packageManager` set to something
# other than the detected package manager. When this happens, it can
# cause the installation to fail.
cd "$CODER_SCRIPT_DATA_DIR"
# 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)!"
@@ -34,7 +40,7 @@ install() {
# so that the devcontainer command is available
if [ -z "$PNPM_HOME" ]; then
PNPM_HOME="$CODER_SCRIPT_BIN_DIR"
export M_HOME
export PNPM_HOME
fi
pnpm add -g @devcontainers/cli
elif [ "$PACKAGE_MANAGER" = "yarn" ]; then
+2 -2
View File
@@ -13,7 +13,7 @@ Run the [Goose](https://block.github.io/goose/) agent in your workspace to gener
```tf
module "goose" {
source = "registry.coder.com/coder/goose/coder"
version = "2.0.1"
version = "2.1.0"
agent_id = coder_agent.example.id
folder = "/home/coder"
install_goose = true
@@ -79,7 +79,7 @@ resource "coder_agent" "main" {
module "goose" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/goose/coder"
version = "2.0.1"
version = "2.1.0"
agent_id = coder_agent.example.id
folder = "/home/coder"
install_goose = true
+17
View File
@@ -251,4 +251,21 @@ describe("goose", async () => {
expect(prompt.exitCode).not.toBe(0);
expect(prompt.stderr).toContain("No such file or directory");
});
test("subdomain-false", async () => {
const { id } = await setup({
agentapiMockScript: await loadTestFile(
import.meta.dir,
"agentapi-mock-print-args.js",
),
moduleVariables: {
subdomain: "false",
},
});
await execModuleScript(id);
const agentapiMockOutput = await readFileContainer(id, agentapiStartLog);
expect(agentapiMockOutput).toContain("AGENTAPI_CHAT_BASE_PATH=/@default/default.foo/apps/goose/chat");
});
});
+9 -2
View File
@@ -63,7 +63,13 @@ variable "install_agentapi" {
variable "agentapi_version" {
type = string
description = "The version of AgentAPI to install."
default = "v0.2.3"
default = "v0.3.3"
}
variable "subdomain" {
type = bool
description = "Whether to use a subdomain for AgentAPI."
default = true
}
variable "goose_provider" {
@@ -133,7 +139,7 @@ EOT
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "1.0.0"
version = "1.1.0"
agent_id = var.agent_id
web_app_slug = local.app_slug
@@ -146,6 +152,7 @@ module "agentapi" {
module_dir_name = local.module_dir_name
install_agentapi = var.install_agentapi
agentapi_version = var.agentapi_version
agentapi_subdomain = var.subdomain
pre_install_script = var.pre_install_script
post_install_script = var.post_install_script
start_script = local.start_script
@@ -3,6 +3,7 @@
const http = require("http");
const args = process.argv.slice(2);
console.log(args);
console.log(`AGENTAPI_CHAT_BASE_PATH=${process.env["AGENTAPI_CHAT_BASE_PATH"]}`);
const port = 3284;
console.log(`starting server on port ${port}`);
+6 -6
View File
@@ -14,7 +14,7 @@ This module adds JetBrains IDE buttons to launch IDEs directly from the dashboar
module "jetbrains" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder"
version = "1.0.1"
version = "1.0.2"
agent_id = coder_agent.example.id
folder = "/home/coder/project"
}
@@ -39,7 +39,7 @@ When `default` contains IDE codes, those IDEs are created directly without user
module "jetbrains" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder"
version = "1.0.1"
version = "1.0.2"
agent_id = coder_agent.example.id
folder = "/home/coder/project"
default = ["PY", "IU"] # Pre-configure GoLand and IntelliJ IDEA
@@ -52,7 +52,7 @@ module "jetbrains" {
module "jetbrains" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder"
version = "1.0.1"
version = "1.0.2"
agent_id = coder_agent.example.id
folder = "/home/coder/project"
# Show parameter with limited options
@@ -66,7 +66,7 @@ module "jetbrains" {
module "jetbrains" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder"
version = "1.0.1"
version = "1.0.2"
agent_id = coder_agent.example.id
folder = "/home/coder/project"
default = ["IU", "PY"]
@@ -81,7 +81,7 @@ module "jetbrains" {
module "jetbrains" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder"
version = "1.0.1"
version = "1.0.2"
agent_id = coder_agent.example.id
folder = "/workspace/project"
@@ -107,7 +107,7 @@ module "jetbrains" {
module "jetbrains_pycharm" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains/coder"
version = "1.0.1"
version = "1.0.2"
agent_id = coder_agent.example.id
folder = "/workspace/project"
+2
View File
@@ -202,6 +202,7 @@ data "coder_parameter" "jetbrains_ides" {
count = length(var.default) == 0 ? 1 : 0
type = "list(string)"
name = "jetbrains_ides"
description = "Select which JetBrains IDEs to configure for use in this workspace."
display_name = "JetBrains IDEs"
icon = "/icon/jetbrains-toolbox.svg"
mutable = true
@@ -230,6 +231,7 @@ resource "coder_app" "jetbrains" {
icon = local.options_metadata[each.key].icon
external = true
order = var.coder_app_order
group = var.group
url = join("", [
"jetbrains://gateway/coder?&workspace=", # requires 2.6.3+ version of Toolbox
data.coder_workspace.me.name,
+1 -1
View File
@@ -1,7 +1,7 @@
---
display_name: "Eric Paulsen"
bio: "Field CTO, EMEA @ Coder"
avatar_url: "./.images/avatar.png"
avatar: "./.images/avatar.png"
github: "ericpaulsen"
linkedin: "https://www.linkedin.com/in/ericpaulsen17" # Optional
website: "https://ericpaulsen.io" # Optional
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

+32
View File
@@ -0,0 +1,32 @@
---
display_name: "Mark Milligan"
bio: "VP of Revenue at https://nuon.co. Former VP of Sales at Coder. Love building startup revenue teams and tinkering with technology."
avatar: "./.images/avatar.png"
github: "sharkymark"
linkedin: "https://www.linkedin.com/in/marktmilligan" # Optional
website: "https://markmilligan.io" # Optional
support_email: "mtm20176@gmail.com" # Optional
status: "community"
---
# Mark Milligan
Former VP of Sales at Coder for 4 years, and now VP of Revenue at Nuon. I love building startup revenue teams and tinkering with technology.
## About Me
Visit my [website](https://markmilligan.io) to learn more about my work and interests.
## Links
[My presentation about Great White Sharks](https://docs.google.com/presentation/d/13I3Af7l-ZSVCh-ovEvOKIM30ABIvNKhkRC3CnYZN450/edit?slide=id.p#slide=id.p) - given twice in 2020 and 2021 to the Coder team.
[NOAA Radar](https://radar.weather.gov/)
[Flight Radar](https://www.flightradar24.com/airport/aus)
### Webcams
[Austin - facing south](https://cctv.austinmobility.io/image/51.jpg)
[Austin - facing north](https://cctv.austinmobility.io/image/52.jpg)
@@ -0,0 +1,34 @@
---
display_name: "Claude Code AI Agent Template"
description: The goal is to try the experimental ai agent integration with Claude CodeAI agent
icon: "../../../../.icons/claude.svg"
verified: false
tags: ["ai", "docker", "container", "claude", "agent", "tasks"]
---
# ai agent template for a workspace in a container on a Docker host
### Docker image
1. Based on Coder-managed image `codercom/example-universal:ubuntu`
[Image on DockerHub](https://hub.docker.com/r/codercom/example-universal)
### Apps included
1. A web-based terminal
1. code-server Web IDE
1. A [sample app](https://github.com/gothinkster/realworld) to test the environment
1. [Claude Code AI agent](https://www.anthropic.com/claude-code) to assist with development tasks
### Resources
[Coder docs on AI agents and tasks](https://coder.com/docs/ai-coder/tasks)
[main.tf for Coder example](https://github.com/coder/registry/blob/main/registry/coder-labs/templates/tasks-docker/main.tf)
[Claude Code Coder Terraform module](https://registry.coder.com/modules/coder/claude-code)
[Docker Terraform provider](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs)
[Coder Terraform provider](https://registry.terraform.io/providers/coder/coder/latest/docs)
@@ -0,0 +1,363 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
docker = {
source = "kreuzwerker/docker"
}
}
}
provider "docker" {
host = var.socket
}
provider "coder" {
}
data "coder_workspace" "me" {
}
data "coder_workspace_owner" "me" {
}
data "coder_provisioner" "me" {
}
variable "socket" {
type = string
description = <<-EOF
The Unix socket that the Docker daemon listens on and how containers
communicate with the Docker daemon.
Either Unix or TCP
e.g., unix:///var/run/docker.sock
EOF
default = "unix:///var/run/docker.sock"
}
variable "anthropic_api_key" {
type = string
description = "Generate one at: https://console.anthropic.com/settings/keys"
sensitive = true
}
resource "coder_env" "anthropic_api_key" {
agent_id = coder_agent.dev.id
name = "CODER_MCP_CLAUDE_API_KEY"
value = var.anthropic_api_key
}
# The Claude Code module does the automatic task reporting
# Other agent modules: https://registry.coder.com/modules?search=agent
# Or use a custom agent:
module "claude-code" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/claude-code/coder"
version = "2.0.0"
agent_id = coder_agent.dev.id
folder = "/home/coder/projects"
install_claude_code = true
claude_code_version = "latest"
order = 999
experiment_post_install_script = data.coder_parameter.setup_script.value
# This enables Coder Tasks
experiment_report_tasks = true
}
# We are using presets to set the prompts, image, and set up instructions
# See https://coder.com/docs/admin/templates/extending-templates/parameters#workspace-presets
data "coder_workspace_preset" "default" {
name = "Real World App: Angular + Django"
default = true
parameters = {
"system_prompt" = <<-EOT
-- Framing --
You are a helpful assistant that can help with code. You are running inside a Coder Workspace and provide status updates to the user via Coder MCP. Stay on track, feel free to debug, but when the original plan fails, do not choose a different route/architecture without checking the user first.
-- Tool Selection --
- playwright: previewing your changes after you made them
to confirm it worked as expected
- desktop-commander - use only for commands that keep running
(servers, dev watchers, GUI apps).
- Built-in tools - use for everything else:
(file operations, git commands, builds & installs, one-off shell commands)
Remember this decision rule:
- Stays running? desktop-commander
- Finishes immediately? built-in tools
-- Context --
There is an existing app and tmux dev server running on port 8000. Be sure to read it's CLAUDE.md (./realworld-django-rest-framework-angular/CLAUDE.md) to learn more about it.
Since this app is for demo purposes and the user is previewing the homepage and subsequent pages, aim to make the first visual change/prototype very quickly so the user can preview it, then focus on backend or logic which can be a more involved, long-running architecture plan.
EOT
"setup_script" = <<-EOT
# Set up projects dir
mkdir -p /home/coder/projects
cd $HOME/projects
# Packages: Install additional packages
sudo apt-get update && sudo apt-get install -y tmux
if ! command -v google-chrome >/dev/null 2>&1; then
yes | npx playwright install chrome
fi
# MCP: Install and configure MCP Servers
npm install -g @wonderwhy-er/desktop-commander
claude mcp add playwright npx -- @playwright/mcp@latest --headless --isolated --no-sandbox
claude mcp add desktop-commander desktop-commander
# Repo: Clone and pull changes from the git repository
if [ ! -d "realworld-django-rest-framework-angular" ]; then
git clone https://github.com/coder-contrib/realworld-django-rest-framework-angular.git
else
cd realworld-django-rest-framework-angular
git fetch
# Check for uncommitted changes
if git diff-index --quiet HEAD -- && \
[ -z "$(git status --porcelain --untracked-files=no)" ] && \
[ -z "$(git log --branches --not --remotes)" ]; then
echo "Repo is clean. Pulling latest changes..."
git pull
else
echo "Repo has uncommitted or unpushed changes. Skipping pull."
fi
cd ..
fi
# Initialize: Start the development server
cd realworld-django-rest-framework-angular && ./start-dev.sh
EOT
"preview_port" = "4200"
"container_image" = "codercom/example-universal:ubuntu"
}
}
# Advanced parameters (these are all set via preset)
data "coder_parameter" "system_prompt" {
name = "system_prompt"
display_name = "System Prompt"
type = "string"
form_type = "textarea"
description = "System prompt for the agent with generalized instructions"
mutable = false
}
data "coder_parameter" "ai_prompt" {
type = "string"
name = "AI Prompt"
default = ""
description = "Write a prompt for Claude Code"
mutable = true
}
data "coder_parameter" "setup_script" {
name = "setup_script"
display_name = "Setup Script"
type = "string"
form_type = "textarea"
description = "Script to run before running the agent"
mutable = false
}
data "coder_parameter" "container_image" {
name = "container_image"
display_name = "Container Image"
type = "string"
default = "codercom/example-universal:ubuntu"
mutable = false
}
data "coder_parameter" "preview_port" {
name = "preview_port"
display_name = "Preview Port"
description = "The port the web app is running to preview in Tasks"
type = "number"
default = "3000"
mutable = false
}
# Other variables for Claude Code
resource "coder_env" "claude_task_prompt" {
agent_id = coder_agent.dev.id
name = "CODER_MCP_CLAUDE_TASK_PROMPT"
value = data.coder_parameter.ai_prompt.value
}
resource "coder_env" "app_status_slug" {
agent_id = coder_agent.dev.id
name = "CODER_MCP_APP_STATUS_SLUG"
value = "claude-code"
}
resource "coder_env" "claude_system_prompt" {
agent_id = coder_agent.dev.id
name = "CODER_MCP_CLAUDE_SYSTEM_PROMPT"
value = data.coder_parameter.system_prompt.value
}
module "coder-login" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/coder-login/coder"
agent_id = coder_agent.dev.id
}
module "dotfiles" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/dotfiles/coder"
agent_id = coder_agent.dev.id
}
module "code-server" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/code-server/coder"
agent_id = coder_agent.dev.id
folder = "/home/coder/projects"
}
module "git-config" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/git-config/coder"
agent_id = coder_agent.dev.id
}
resource "coder_agent" "dev" {
arch = data.coder_provisioner.me.arch
os = "linux"
# 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
}
display_apps {
vscode = true
vscode_insiders = false
ssh_helper = false
port_forwarding_helper = true
web_terminal = true
}
startup_script_behavior = "non-blocking"
connection_timeout = 300
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}"
}
startup_script = <<EOT
#!/bin/sh
EOT
}
resource "coder_app" "preview" {
agent_id = coder_agent.dev.id
slug = "preview"
display_name = "Preview your app"
icon = "${data.coder_workspace.me.access_url}/emojis/1f50e.png"
url = "http://localhost:${data.coder_parameter.preview_port.value}"
share = "authenticated"
subdomain = true
open_in = "tab"
order = 0
healthcheck {
url = "http://localhost:${data.coder_parameter.preview_port.value}/"
interval = 5
threshold = 15
}
}
resource "docker_container" "workspace" {
count = data.coder_workspace.me.start_count
image = data.coder_parameter.container_image.value
# Uses lower() to avoid Docker restriction on container names.
name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
hostname = lower(data.coder_workspace.me.name)
dns = ["1.1.1.1"]
# Use the docker gateway if the access URL is 127.0.0.1
#entrypoint = ["sh", "-c", replace(coder_agent.dev.init_script, "127.0.0.1", "host.docker.internal")]
# Use the docker gateway if the access URL is 127.0.0.1
command = [
"sh", "-c",
<<EOT
trap '[ $? -ne 0 ] && echo === Agent script exited with non-zero code. Sleeping infinitely to preserve logs... && sleep infinity' EXIT
${replace(coder_agent.dev.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")}
EOT
]
env = ["CODER_AGENT_TOKEN=${coder_agent.dev.token}"]
volumes {
container_path = "/home/coder/"
volume_name = docker_volume.coder_volume.name
read_only = false
}
host {
host = "host.docker.internal"
ip = "host-gateway"
}
}
resource "docker_volume" "coder_volume" {
name = "coder-${data.coder_workspace_owner.me.name}-${data.coder_workspace.me.name}"
}
resource "coder_metadata" "workspace_info" {
count = data.coder_workspace.me.start_count
resource_id = docker_container.workspace[0].id
item {
key = "image"
value = data.coder_parameter.container_image.value
}
}
+1 -1
View File
@@ -247,8 +247,8 @@ export const runTerraformApply = async <TVars extends TerraformVariables>(
"-compact-warnings",
"-input=false",
"-auto-approve",
"-state",
"-no-color",
"-state",
stateFile,
],
{