Compare commits

...

17 Commits

Author SHA1 Message Date
Matt Hazinski 50f4d5388b fix(codex): pass folder variable to agentapi module (#477)
## Description

The folder variable was not being passed from the codex module to the
agentapi module, causing agentapi to use its default value of
`/home/coder` instead of the user-specified folder path.

This resulted in permission errors when the codex module tried to create
directories in `/home/coder` when users specified a different folder
like `/home/matt/foo`.

Fix by adding `folder = var.folder` to the agentapi module invocation.

## Type of Change

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

## Module Information

Path: registry/coder-labs/modules/codex
New version: v2.1.1
Breaking change: [ ] Yes [X] No

## Testing & Validation

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

## Related Issues
Fixes https://github.com/coder/registry/issues/476

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: DevelopmentCats <christofer@coder.com>
2025-10-14 16:49:52 +00:00
dependabot[bot] 36943d1dfb chore(deps): bump crate-ci/typos from 1.37.2 to 1.38.1 in the github-actions group (#475)
Bumps the github-actions group with 1 update:
[crate-ci/typos](https://github.com/crate-ci/typos).

Updates `crate-ci/typos` from 1.37.2 to 1.38.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/releases">crate-ci/typos's
releases</a>.</em></p>
<blockquote>
<h2>v1.38.1</h2>
<h2>[1.38.1] - 2025-10-07</h2>
<h3>Fixes</h3>
<ul>
<li>Ignore common golang identifiers</li>
</ul>
<h2>v1.38.0</h2>
<h2>[1.38.0] - 2025-10-06</h2>
<h3>Features</h3>
<ul>
<li>Update type list</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Don't correct <code>typ</code></li>
<li>Consistently error on unused config fields</li>
</ul>
<h2>v1.37.3</h2>
<h2>[1.37.3] - 2025-10-06</h2>
<h3>Fixes</h3>
<ul>
<li>Don't correct <code>PN</code> for <code>bitbake</code> file
types</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/blob/master/CHANGELOG.md">crate-ci/typos's
changelog</a>.</em></p>
<blockquote>
<h2>[1.38.1] - 2025-10-07</h2>
<h3>Fixes</h3>
<ul>
<li>Ignore common golang identifiers</li>
</ul>
<h2>[1.38.0] - 2025-10-06</h2>
<h3>Features</h3>
<ul>
<li>Update type list</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Don't correct <code>typ</code></li>
<li>Consistently error on unused config fields</li>
</ul>
<h2>[1.37.3] - 2025-10-06</h2>
<h3>Fixes</h3>
<ul>
<li>Don't correct <code>PN</code> for <code>bitbake</code> file
types</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/crate-ci/typos/commit/80c8a4945eec0f6d464eaf9e65ed98ef085283d1"><code>80c8a49</code></a>
chore: Release</li>
<li><a
href="https://github.com/crate-ci/typos/commit/c1008ce1b695c69fa611c3a79c32852be029709a"><code>c1008ce</code></a>
docs: Update changelog</li>
<li><a
href="https://github.com/crate-ci/typos/commit/62a3b5083afa59e8054f76ff5dbb94bb676ce5e4"><code>62a3b50</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/1398">#1398</a>
from ccoveille-forks/go-exclusions</li>
<li><a
href="https://github.com/crate-ci/typos/commit/e6bedbde77058052de3f00d82a67284618385615"><code>e6bedbd</code></a>
fix(config): Add some Go exclusions</li>
<li><a
href="https://github.com/crate-ci/typos/commit/90cacd60e824aaf9adff4afa0d6582f52631bc6d"><code>90cacd6</code></a>
docs(ref): Speak to glob ambiguity</li>
<li><a
href="https://github.com/crate-ci/typos/commit/b81b12ea1b8702b57e1a917e5a7bfc26d46c21e9"><code>b81b12e</code></a>
docs(ref): Clarify directories are not spell checked</li>
<li><a
href="https://github.com/crate-ci/typos/commit/eaf25df9941e5b6a2f145729a76b06af9eab44ca"><code>eaf25df</code></a>
docs(ref): Speak to locale's behavior</li>
<li><a
href="https://github.com/crate-ci/typos/commit/a9735e2e141b9a8f08340e41aac57b52805ae185"><code>a9735e2</code></a>
docs(ref): Provide identifier/word config examples</li>
<li><a
href="https://github.com/crate-ci/typos/commit/3c14191fcc71bad3e87e231c86a6d3a7876ae8a2"><code>3c14191</code></a>
docs(ref): Talk about include lists</li>
<li><a
href="https://github.com/crate-ci/typos/commit/d0f81dc972d4fc9862590daafb26ee03a2dbfda5"><code>d0f81dc</code></a>
docs(ref): Re-organize help more like cargo</li>
<li>Additional commits viewable in <a
href="https://github.com/crate-ci/typos/compare/v1.37.2...v1.38.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=crate-ci/typos&package-manager=github_actions&previous-version=1.37.2&new-version=1.38.1)](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 <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>
2025-10-13 07:40:01 -05:00
greg-the-coder e7d705bf98 Fixes from AWS Workshop testing (#428)
Closes #

## Description

Changes to code-server and jetbrains modules that were not caught during
initial unit-testing, that appear to be related to older versions of the
modules or recent changes.

## Type of Change

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

## Testing & Validation

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

## Related Issues

None

---------

Co-authored-by: DevelopmentCats <christofer@coder.com>
2025-10-10 06:27:18 +05:00
Atif Ali 898219b16b Enhance PR template with template information section (#474) 2025-10-09 15:50:38 +00:00
chgl fc071e0930 refactor: refactored get_http_dir (#360)
Closes #

## Description

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

I just couldn't get the script to execute properly in its current form.
I saw e.g.

```console
[[: 1989{#d[@]}: syntax error: invalid arithmetic operator (error token is "{#d[@]}")
```

when trying to run the script locally. (GNU bash, version
5.2.21(1)-release (x86_64-pc-linux-gnu)).

This uses a likely simpler bash script, but requires both grep and awk.

## Type of Change

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

## Module Information

<!-- Delete this section if not applicable -->

**Path:** `registry/coder/modules/kasmvnc`  
**New version:** `v1.2.3`  
**Breaking change:** [ ] Yes [x] No

## Testing & Validation

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

## Related Issues

<!-- Link related issues or write "None" if not applicable -->

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Atif Ali <atif@coder.com>
Co-authored-by: DevCats <christofer@coder.com>
2025-10-09 09:36:14 -05:00
DevCats d516aff908 chore: set verified to false and bump to 1.0.1 (#473)
## Description

Removes verified status from nexus module.
<!-- Briefly describe what this PR does and why -->

## Type of Change

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

## Module Information

<!-- Delete this section if not applicable -->

**Path:** `registry/mavrickrishi/modules/nexus-repository`  
**New version:** `v1.0.1`  
**Breaking change:** [ ] Yes [X] No

## Testing & Validation

- [X] Tests pass (`bun test`)
- [X] Code formatted (`bun run fmt`)
- [X] Changes tested locally
2025-10-09 08:13:06 -05:00
DevCats ccdca6daf5 chore: update CONTRIBUTION docs to explain both tests, and update CI for both tests (#384)
Closes #383 

## Description

- Update CONTRIBUTION.md to elaborate on ts and tf tests
- Add ./scripts/terraform_test_all.sh to CI for ts tests

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

## Type of Change

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

## Testing & Validation

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

---------

Co-authored-by: Atif Ali <atif@coder.com>
2025-10-09 12:42:07 +00:00
Rishi Mondal ce039f64df Add Sonatype Nexus repository integration module (#262)
# Add Sonatype Nexus Repository Integration Module

## Summary
Implements a Coder module for Sonatype Nexus Repository Manager
integration that automatically configures Maven, npm, PyPI, and Docker
registries for development workspaces.

## Demo Video & Screenshots

https://github.com/user-attachments/assets/2c51f229-d34d-483b-a0e9-f4e0d79332c2

![Nexus Repository
Integration](https://github.com/user-attachments/assets/1a778a8f-0e48-40f2-ae0f-5b8d5d5ce849)

## Features
-  **Maven Support**: Automatic `settings.xml` configuration
-  **npm Support**: Automatic `.npmrc` configuration with scoped
packages
-  **PyPI Support**: Automatic `pip.conf` configuration
-  **Docker Support**: Registry authentication setup
-  **Flexible Configuration**: Support for multiple repositories per
package manager
-  **Secure Credentials**: API token and password support
-  **Username Options**: Configurable username field (username or
email)

## Nexus Repository Manager Requirements

### Version Requirements
**Yes, this module requires Nexus Repository Manager Pro version** for
full functionality, though basic features work with the Community
Edition (OSS).

### Supported Authentication Methods
This module supports **4 authentication methods**:

1. **User Token Authentication** (Recommended - Pro only)
   - Enhanced security with two-part tokens
   - Ideal for CI/CD and automated environments
   - Requires `nx-usertoken-current` privilege

2. **API Token Authentication** (Pro only)
   - Single-use access tokens via REST API
   - Programmatic token generation and management

3. **Basic Authentication** (OSS & Pro)
   - Standard HTTP Basic Auth with username/password
   - Works with both OSS and Pro versions

4. **Base64 Encoded Credentials** (OSS & Pro)  
   - Base64 encoded `username:password` format
   - Compatible with npm and other package managers

### Testing Instructions

#### Prerequisites
- Nexus Repository Manager instance (OSS or Pro)
- Admin access to configure repositories
- Test repositories for each package manager you want to test

#### Setup Test Environment
1. **Create Test Repositories** in your Nexus instance:
   - Maven: `maven-public`, `maven-releases` 
   - npm: `npm-public`, `@company:npm-private`
   - PyPI: `pypi-public`, `pypi-private`
   - Docker: `docker-public`, `docker-private`

2. **Configure Authentication**:
   - For Pro: Generate user tokens via UI (User menu → User Token)
   - For OSS: Use username/password or base64 encoded credentials
   - Set up appropriate permissions for test repositories

3. **Test the Module**:
   ```hcl
   module "nexus" {
     source         = "registry.coder.com/mavrickrishi/nexus/coder"
     version        = "1.0.0"
     agent_id       = coder_agent.main.id
     nexus_url      = "https://your-nexus-instance.com"
     nexus_password = var.nexus_api_token  # or password
     package_managers = {
       maven  = ["maven-public", "maven-releases"]
       npm    = ["npm-public", "@company:npm-private"]
       pypi   = ["pypi-public", "pypi-private"]
       docker = ["docker-public", "docker-private"]
     }
   }
   ```

4. **Verify Configuration**:
   - Check generated config files in workspace
   - Test package installation from configured repositories
   - Verify authentication works for each package manager

#### EC2 Deployment Testing
Tested by deploying on EC2 instance with:
- Ubuntu 22.04 LTS
- Nexus Repository Manager Pro
- All package managers (Maven, npm, PyPI, Docker)
- Both token and basic authentication methods

## Usage Example
```hcl
module "nexus" {
  source         = "registry.coder.com/mavrickrishi/nexus/coder"
  version        = "1.0.0"
  agent_id       = coder_agent.main.id
  nexus_url      = "https://nexus.company.com"
  nexus_password = var.nexus_api_token
  package_managers = {
    maven  = ["maven-public", "maven-releases"]
    npm    = ["npm-public", "@company:npm-private"]
    pypi   = ["pypi-public", "pypi-private"]
    docker = ["docker-public", "docker-private"]
  }
}
```

## Testing
-  11 comprehensive tests covering all functionality
-  Variable validation tests
-  Package manager configuration tests
-  Error handling tests
-  All tests passing
-  EC2 deployment tested

## Files Added
- `registry/mavrickrishi/modules/nexus/main.tf` - Main module
configuration
- `registry/mavrickrishi/modules/nexus/README.md` - Complete
documentation
- `registry/mavrickrishi/modules/nexus/main.test.ts` - Test suite

## Checklist
- [x] Module follows existing patterns and conventions
- [x] Comprehensive test coverage (11 tests)
- [x] Complete documentation with examples
- [x] Input validation and error handling
- [x] Secure credential handling
- [x] All tests passing
- [x] Demo video included
- [x] Screenshots added
- [x] Testing instructions provided
- [x] Authentication methods documented
- [x] EC2 deployment tested

Closes #202
/claim #202

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Atif Ali <me@matifali.dev>
Co-authored-by: DevCats <christofer@coder.com>
Co-authored-by: DevCats <chris@dualriver.com>
2025-10-09 07:31:43 -05:00
DevCats 8acda84dd7 chore: update icons for auto-start-dev-server module (#471)
## Description

Adds icons for module, and update all refrences.

PR for Site Icon's Addition: https://github.com/coder/coder/pull/20219

## Type of Change

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

## Module Information

<!-- Delete this section if not applicable -->

**Path:** `registry/mavrickrishi/modules/auto-start-dev-server`  
**New version:** `v1.0.1`  
**Breaking change:** [ ] Yes [ ] No

## Testing & Validation

- [X] Tests pass (`bun test`)
- [X] Code formatted (`bun run fmt`)
- [X] Changes tested locally
2025-10-08 13:20:45 -05:00
Atif Ali 76c1299968 docs: upgrade alert style to a GFM style tip for JetBrains Gateway (#468) 2025-10-08 06:39:54 +00:00
Jullian Pepito 60372ff797 fix(git-clone): Update README.md (#448)
Changes `coder_git_auth` to `coder_external_auth` in README

## Description

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

## Type of Change

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

## Module Information

<!-- Delete this section if not applicable -->

**Path:** `registry/coder/modules/git-clone` 
**New version:** `v1.1.2`  
**Breaking change:** [ ] Yes [X] No

## Testing & Validation

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

## Related Issues

<!-- Link related issues or write "None" if not applicable -->

---------

Co-authored-by: Jullian Pepito <jullian@MacBook-Pro.local>
Co-authored-by: DevCats <christofer@coder.com>
2025-10-07 15:35:02 -05:00
Rishi Mondal f28bcdb713 Auto-Start Development Servers Module (#316)
# Auto-Start Development Servers Module

## Summary

/claim #204

Implements automatic detection and startup of development servers based
on project detection as requested in #204.

-  **Multi-language support**: Node.js, Rails, Django, Flask, Spring
Boot, Go, PHP, Rust, .NET
-  **Background execution**: Servers start automatically without user
intervention
-  **Devcontainer.json integration**: Uses custom start commands when
available
-  **Smart fallback**: Creates sample project when no existing projects
found
-  **Comprehensive logging**: Full activity logs for troubleshooting



https://github.com/user-attachments/assets/2eddf67c-3ac1-4e55-a5ba-79292d61e918



## Addresses GitHub Issue

Closes #204 - "Auto-start development servers based on project
detection"

---------

Co-authored-by: DevCats <christofer@coder.com>
Co-authored-by: DevCats <chris@dualriver.com>
2025-10-07 14:44:00 -05:00
romracer cb553209a5 fix: update CLI icon for copilot module to same icon as web app (#469)
## Description

Sets `cli_app_icon` in agentapi to the same icon used for
`web_app_icon`. Its currently using the default of Claude.

## Type of Change

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

## Module Information

<!-- Delete this section if not applicable -->

**Path:** `registry/coder-labs/modules/copilot`  
**New version:** `v0.2.1`  
**Breaking change:** [ ] Yes [x] No

## Testing & Validation

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

## Related Issues

<!-- Link related issues or write "None" if not applicable -->
2025-10-07 13:54:14 -05:00
35C4n0r 5d0504aef9 feat: update agentapi_version to 0.10.0 (#456)
Closes #

## Description

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

## Type of Change

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

## Module Information

<!-- Delete this section if not applicable -->

**Path:** `registry/[namespace]/modules/[module-name]`  
**New version:** `v1.0.0`  
**Breaking change:** [ ] Yes [ ] No

## Testing & Validation

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

## Related Issues

<!-- Link related issues or write "None" if not applicable -->
2025-10-07 13:27:12 -05:00
35C4n0r c1c0dec90f chore: bump agentapi module version (#465) 2025-10-07 18:09:44 +00:00
DevCats 59b67c2c98 chore: update display name for copilot module to Copilot CLI (#467)
## Description

update display name for copilot module to Copilot CLI

## Type of Change

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

## Module Information

<!-- Delete this section if not applicable -->

**Path:** `registry/coder-labs/modules/copilot`  
**New version:** `v0.1.2`  
**Breaking change:** [ ] Yes [X] No

## Testing & Validation

- [X] Tests pass (`bun test`)
- [X] Code formatted (`bun run fmt`)
- [X] Changes tested locally
2025-10-07 17:40:23 +00:00
DevCats 7abe422e0a fix: Add COPILOT_MODEL to install script args (#464)
Closes #462

## Description

<!-- Briefly describe what this PR does and why -->
Fixes missing COPILOT_MODEL arg from install script

## Type of Change

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

## Module Information

<!-- Delete this section if not applicable -->

**Path:** `registry/coder-labs/modules/copilot`  
**New version:** `v0.1.1`  
**Breaking change:** [ ] Yes [X] No

## Testing & Validation

- [X] Tests pass (`bun test`)
- [X] Code formatted (`bun run fmt`)
- [X] Changes tested locally
2025-10-07 12:05:50 -05:00
43 changed files with 1605 additions and 113 deletions
+8 -3
View File
@@ -1,5 +1,3 @@
Closes #
## Description
<!-- Briefly describe what this PR does and why -->
@@ -7,6 +5,7 @@ Closes #
## Type of Change
- [ ] New module
- [ ] New template
- [ ] Bug fix
- [ ] Feature/enhancement
- [ ] Documentation
@@ -20,10 +19,16 @@ Closes #
**New version:** `v1.0.0`
**Breaking change:** [ ] Yes [ ] No
## Template Information
<!-- Delete this section if not applicable -->
**Path:** `registry/[namespace]/templates/[template-name]`
## Testing & Validation
- [ ] Tests pass (`bun test`)
- [ ] Code formatted (`bun run fmt`)
- [ ] Code formatted (`bun fmt`)
- [ ] Changes tested locally
## Related Issues
+3 -1
View File
@@ -28,6 +28,8 @@ jobs:
run: bun install
- name: Run TypeScript tests
run: bun test
- name: Run Terraform tests
run: ./scripts/terraform_test_all.sh
- name: Run Terraform Validate
run: bun terraform-validate
validate-style:
@@ -48,7 +50,7 @@ jobs:
- name: Validate formatting
run: bun fmt:ci
- name: Check for typos
uses: crate-ci/typos@v1.37.2
uses: crate-ci/typos@v1.38.1
with:
config: .github/typos.toml
validate-readme-files:
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="512pt" height="512pt" version="1.1" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<path d="m500.48 262.2-48.18 73.984c-0.73438 1.1367-2 1.8242-3.3555 1.8242-1.3516 0-2.6172-0.6875-3.3516-1.8242l-48.129-73.984c-0.78125-1.2227-0.83594-2.7773-0.14453-4.0547 0.69141-1.2734 2.0195-2.0742 3.4727-2.0898h24.781c-0.007813-29.523-7.7188-58.531-22.375-84.156-14.652-25.629-35.742-46.988-61.184-61.969-2.3711-1.3633-3.8633-3.8594-3.9453-6.5938-0.085937-2.7305 1.2539-5.3125 3.5352-6.8203l27.035-17.613c3.4766-2.3633 8.043-2.3633 11.52 0 28.473 19.934 51.723 46.441 67.773 77.27 16.051 30.828 24.434 65.074 24.438 99.832h24.781c1.4688 0 2.8203 0.80859 3.5156 2.1055 0.69531 1.293 0.62109 2.8633-0.1875 4.0898zm-85.043 79.359c-1.5078-2.2812-4.0898-3.6211-6.8203-3.5391-2.7344 0.085937-5.2305 1.5781-6.5938 3.9492-14.965 25.434-36.305 46.523-61.914 61.188-25.609 14.664-54.602 22.391-84.109 22.422v-24.781c-0.011719-1.4531-0.8125-2.7812-2.0898-3.4727-1.2773-0.69141-2.832-0.63672-4.0547 0.14453l-74.035 47.977c-1.1367 0.73438-1.8242 1.9961-1.8242 3.3516s0.6875 2.6172 1.8242 3.3555l73.984 48.18c1.2227 0.78125 2.7773 0.83594 4.0547 0.14453 1.2734-0.69141 2.0742-2.0234 2.0898-3.4727v-24.68c34.734-0.015624 68.957-8.3984 99.766-24.441 30.812-16.039 57.301-39.27 77.23-67.719 2.3672-3.4766 2.3672-8.043 0-11.52zm-245.45 60.52c-25.434-14.977-46.516-36.328-61.172-61.945-14.652-25.617-22.371-54.617-22.387-84.129h24.781c1.4531-0.011719 2.7812-0.8125 3.4727-2.0898 0.69141-1.2773 0.63672-2.832-0.14453-4.0547l-47.977-74.035c-0.73438-1.1367-1.9961-1.8242-3.3516-1.8242s-2.6172 0.6875-3.3555 1.8242l-48.332 73.984c-0.80859 1.2266-0.88281 2.7969-0.1875 4.0898 0.69531 1.2969 2.0469 2.1055 3.5156 2.1055h24.781c0.015625 34.734 8.3984 68.957 24.438 99.766 16.043 30.812 39.273 57.301 67.723 77.234 3.4766 2.3633 8.043 2.3633 11.52 0l27.086-17.664c2.2109-1.5195 3.4961-4.0625 3.4141-6.7422-0.082032-2.6836-1.5234-5.1406-3.8242-6.5195zm92.16-390.5c-1.2227-0.78125-2.7773-0.83594-4.0547-0.14453-1.2773 0.69141-2.0781 2.0195-2.0898 3.4727v24.73c-34.734 0.015625-68.957 8.3984-99.766 24.438-30.812 16.043-57.301 39.273-77.234 67.723-2.3633 3.4766-2.3633 8.043 0 11.52l17.664 27.086c1.5078 2.2812 4.0898 3.6211 6.8242 3.5352 2.7305-0.082032 5.2266-1.5742 6.5898-3.9453 14.965-25.41 36.289-46.48 61.879-61.133 25.59-14.652 54.555-22.383 84.043-22.426v24.781c0.011719 1.4531 0.8125 2.7812 2.0898 3.4727 1.2773 0.69141 2.832 0.63672 4.0547-0.14453l74.035-47.977c1.1367-0.73438 1.8242-1.9961 1.8242-3.3516s-0.6875-2.6172-1.8242-3.3555zm-6.1445 210.23c-9.0703 0-17.77 3.6055-24.184 10.02-6.4141 6.4141-10.02 15.113-10.02 24.184s3.6055 17.77 10.02 24.184c6.4141 6.4141 15.113 10.02 24.184 10.02s17.77-3.6055 24.184-10.02c6.4141-6.4141 10.02-15.113 10.02-24.184s-3.6055-17.77-10.02-24.184c-6.4141-6.4141-15.113-10.02-24.184-10.02zm90.727-26.828-10.344 14.953c4.0039 6.9414 7.0859 14.375 9.1641 22.117l17.973 2.9688c6.543 1.1445 11.316 6.8242 11.316 13.465v15.055c0 6.6406-4.7734 12.32-11.316 13.465l-17.766 3.125v-0.003907c-2.1562 7.6992-5.3086 15.082-9.3711 21.965l10.238 14.797h0.003906c3.8047 5.4375 3.1562 12.82-1.5352 17.512l-10.648 10.648h-0.003906c-4.6914 4.6953-12.074 5.3438-17.508 1.5391l-14.797-10.238v-0.003907c-6.9453 4.0039-14.379 7.0859-22.121 9.1641l-3.0195 18.023c-1.1445 6.543-6.8242 11.316-13.465 11.316h-15.055c-6.6406 0-12.32-4.7734-13.465-11.316l-3.125-17.766h0.003907c-7.7031-2.1758-15.086-5.3398-21.965-9.4219l-14.797 10.238v0.003907c-5.4375 3.8047-12.82 3.1562-17.512-1.5391l-10.648-10.648c-4.6953-4.6914-5.3438-12.074-1.5391-17.512l10.238-14.797h0.003907c-4.0039-6.9414-7.0859-14.375-9.1641-22.117l-18.023-2.9688c-6.543-1.1445-11.316-6.8242-11.316-13.465v-15.055c0-6.6406 4.7734-12.32 11.316-13.465l17.766-3.125v0.003907c2.1562-7.6992 5.3086-15.082 9.3711-21.965l-10.238-14.797h-0.003906c-3.8047-5.4375-3.1562-12.82 1.5352-17.512l10.648-10.648h0.003906c4.6914-4.6953 12.074-5.3438 17.508-1.5391l14.797 10.238v0.003907c6.9453-4.0039 14.379-7.0859 22.121-9.1641l3.0195-18.023c1.1445-6.543 6.8242-11.316 13.465-11.316h15.055c6.6406 0 12.32 4.7734 13.465 11.316l3.125 17.766h-0.003907c7.6992 2.1562 15.082 5.3086 21.965 9.3711l14.797-10.238v-0.003906c5.4375-3.8047 12.82-3.1562 17.512 1.5352l10.648 10.648v0.003906c4.6875 4.6367 5.3984 11.957 1.6914 17.406zm-36.047 61.031c0-14.504-5.7578-28.41-16.016-38.664-10.254-10.258-24.16-16.016-38.664-16.016s-28.41 5.7578-38.664 16.016c-10.258 10.254-16.016 24.16-16.016 38.664s5.7578 28.41 16.016 38.664c10.254 10.258 24.16 16.016 38.664 16.016 14.5-0.011719 28.398-5.7773 38.652-16.027 10.25-10.254 16.016-24.152 16.027-38.652z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 27 KiB

+15 -8
View File
@@ -124,18 +124,23 @@ This script generates:
- Accurate description and usage examples
- Correct icon path (usually `../../../../.icons/your-icon.svg`)
- Proper tags that describe your module
3. **Create at least one `.tftest.hcl`** to test your module with `terraform test`
3. **Create tests for your module:**
- **Terraform tests**: Create a `*.tftest.hcl` file and test with `terraform test`
- **TypeScript tests**: Create `main.test.ts` file if your module runs scripts or has business logic that Terraform tests can't cover
4. **Add any scripts** or additional files your module needs
### 4. Test and Submit
```bash
# Test your module (from the module directory)
# Test your module
cd registry/[namespace]/modules/[module-name]
# Required: Test Terraform functionality
terraform init -upgrade
terraform test -verbose
# Or run all tests in the repo
./scripts/terraform_test_all.sh
# Optional: Test TypeScript files if you have main.test.ts
bun test main.test.ts
# Format code
bun run fmt
@@ -343,8 +348,8 @@ coder templates push test-[template-name] -d .
terraform init -upgrade
terraform test -verbose
# Test all modules
./scripts/terraform_test_all.sh
# Optional: If you have TypeScript tests
bun test main.test.ts
```
### 3. Maintain Backward Compatibility
@@ -393,7 +398,9 @@ Example: `https://github.com/coder/registry/compare/main...your-branch?template=
### Every Module Must Have
- `main.tf` - Terraform code
- One or more `.tftest.hcl` files - Working tests with `terraform test`
- **Tests**:
- `*.tftest.hcl` files with `terraform test` (to test terraform specific logic)
- `main.test.ts` file with `bun test` (to test business logic, i.e., `coder_script` to install a package.)
- `README.md` - Documentation with frontmatter
### Every Template Must Have
@@ -493,7 +500,7 @@ When reporting bugs, include:
2. **No tests** or broken tests
3. **Hardcoded values** instead of variables
4. **Breaking changes** without defaults
5. **Not running** formatting (`bun run fmt`) and tests (`terraform test`) before submitting
5. **Not running** formatting (`bun run fmt`) and tests (`terraform test`, and `bun test main.test.ts` if applicable) before submitting
## For Maintainers
+2 -2
View File
@@ -15,7 +15,7 @@ run "app_url_uses_port" {
}
assert {
condition = resource.coder_app.MODULE_NAME.url == "http://localhost:19999"
error_message = "Expected MODULE_NAME app URL to include configured port"
condition = resource.coder_app.module_name.url == "http://localhost:19999"
error_message = "Expected module-name app URL to include configured port"
}
}
+12 -12
View File
@@ -35,13 +35,13 @@ variable "agent_id" {
variable "log_path" {
type = string
description = "The path to log MODULE_NAME to."
default = "/tmp/MODULE_NAME.log"
description = "The path to the module log file."
default = "/tmp/module_name.log"
}
variable "port" {
type = number
description = "The port to run MODULE_NAME on."
description = "The port to run the application on."
default = 19999
}
@@ -59,9 +59,9 @@ variable "order" {
# Add other variables here
resource "coder_script" "MODULE_NAME" {
resource "coder_script" "module_name" {
agent_id = var.agent_id
display_name = "MODULE_NAME"
display_name = "Module Name"
icon = local.icon_url
script = templatefile("${path.module}/run.sh", {
LOG_PATH : var.log_path,
@@ -70,10 +70,10 @@ resource "coder_script" "MODULE_NAME" {
run_on_stop = false
}
resource "coder_app" "MODULE_NAME" {
resource "coder_app" "module_name" {
agent_id = var.agent_id
slug = "MODULE_NAME"
display_name = "MODULE_NAME"
slug = "module-name"
display_name = "Module Name"
url = "http://localhost:${var.port}"
icon = local.icon_url
subdomain = false
@@ -88,10 +88,10 @@ resource "coder_app" "MODULE_NAME" {
}
}
data "coder_parameter" "MODULE_NAME" {
type = "list(string)"
name = "MODULE_NAME"
display_name = "MODULE_NAME"
data "coder_parameter" "module_name" {
type = "string"
name = "module_name"
display_name = "Module Name"
icon = local.icon_url
mutable = var.mutable
default = local.options["Option 1"]["value"]
+3 -3
View File
@@ -13,7 +13,7 @@ Run Auggie CLI in your workspace to access Augment's AI coding assistant with ad
```tf
module "auggie" {
source = "registry.coder.com/coder-labs/auggie/coder"
version = "0.1.0"
version = "0.2.0"
agent_id = coder_agent.example.id
folder = "/home/coder/project"
}
@@ -47,7 +47,7 @@ module "coder-login" {
module "auggie" {
source = "registry.coder.com/coder-labs/auggie/coder"
version = "0.1.0"
version = "0.2.0"
agent_id = coder_agent.example.id
folder = "/home/coder/project"
@@ -103,7 +103,7 @@ EOF
```tf
module "auggie" {
source = "registry.coder.com/coder-labs/auggie/coder"
version = "0.1.0"
version = "0.2.0"
agent_id = coder_agent.example.id
folder = "/home/coder/project"
+2 -2
View File
@@ -66,7 +66,7 @@ variable "install_agentapi" {
variable "agentapi_version" {
type = string
description = "The version of AgentAPI to install."
default = "v0.6.0"
default = "v0.10.0"
validation {
condition = can(regex("^v[0-9]+\\.[0-9]+\\.[0-9]+", var.agentapi_version))
error_message = "agentapi_version must be a valid semantic version starting with 'v', like 'v0.3.3'."
@@ -178,7 +178,7 @@ locals {
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "1.1.1"
version = "1.2.0"
agent_id = var.agent_id
web_app_slug = local.app_slug
+4 -4
View File
@@ -13,7 +13,7 @@ Run Codex CLI in your workspace to access OpenAI's models through the Codex inte
```tf
module "codex" {
source = "registry.coder.com/coder-labs/codex/coder"
version = "2.0.0"
version = "2.1.1"
agent_id = coder_agent.example.id
openai_api_key = var.openai_api_key
folder = "/home/coder/project"
@@ -33,7 +33,7 @@ module "codex" {
module "codex" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder-labs/codex/coder"
version = "2.0.0"
version = "2.1.1"
agent_id = coder_agent.example.id
openai_api_key = "..."
folder = "/home/coder/project"
@@ -60,7 +60,7 @@ module "coder-login" {
module "codex" {
source = "registry.coder.com/coder-labs/codex/coder"
version = "2.0.0"
version = "2.1.1"
agent_id = coder_agent.example.id
openai_api_key = "..."
ai_prompt = data.coder_parameter.ai_prompt.value
@@ -106,7 +106,7 @@ For custom Codex configuration, use `base_config_toml` and/or `additional_mcp_se
```tf
module "codex" {
source = "registry.coder.com/coder-labs/codex/coder"
version = "2.0.0"
version = "2.1.1"
# ... other variables ...
# Override default configuration
+3 -2
View File
@@ -80,7 +80,7 @@ variable "install_agentapi" {
variable "agentapi_version" {
type = string
description = "The version of AgentAPI to install."
default = "v0.5.0"
default = "v0.10.0"
}
variable "codex_model" {
@@ -128,9 +128,10 @@ locals {
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "1.1.1"
version = "1.2.0"
agent_id = var.agent_id
folder = var.folder
web_app_slug = local.app_slug
web_app_order = var.order
web_app_group = var.group
@@ -1,5 +1,5 @@
---
display_name: Copilot
display_name: Copilot CLI
description: GitHub Copilot CLI agent for AI-powered terminal assistance
icon: ../../../../.icons/github.svg
verified: false
@@ -13,7 +13,7 @@ Run [GitHub Copilot CLI](https://docs.github.com/copilot/concepts/agents/about-c
```tf
module "copilot" {
source = "registry.coder.com/coder-labs/copilot/coder"
version = "0.1.0"
version = "0.2.1"
agent_id = coder_agent.example.id
workdir = "/home/coder/projects"
}
@@ -51,7 +51,7 @@ data "coder_parameter" "ai_prompt" {
module "copilot" {
source = "registry.coder.com/coder-labs/copilot/coder"
version = "0.1.0"
version = "0.2.1"
agent_id = coder_agent.example.id
workdir = "/home/coder/projects"
@@ -71,7 +71,7 @@ Customize tool permissions, MCP servers, and Copilot settings:
```tf
module "copilot" {
source = "registry.coder.com/coder-labs/copilot/coder"
version = "0.1.0"
version = "0.2.1"
agent_id = coder_agent.example.id
workdir = "/home/coder/projects"
@@ -142,7 +142,7 @@ variable "github_token" {
module "copilot" {
source = "registry.coder.com/coder-labs/copilot/coder"
version = "0.1.0"
version = "0.2.1"
agent_id = coder_agent.example.id
workdir = "/home/coder/projects"
github_token = var.github_token
@@ -156,7 +156,7 @@ Run Copilot as a command-line tool without task reporting or web interface. This
```tf
module "copilot" {
source = "registry.coder.com/coder-labs/copilot/coder"
version = "0.1.0"
version = "0.2.1"
agent_id = coder_agent.example.id
workdir = "/home/coder"
report_tasks = false
+3 -1
View File
@@ -242,7 +242,7 @@ resource "coder_env" "github_token" {
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "1.1.1"
version = "1.2.0"
agent_id = var.agent_id
folder = local.workdir
@@ -253,6 +253,7 @@ module "agentapi" {
web_app_display_name = var.web_app_display_name
cli_app = var.cli_app
cli_app_slug = var.cli_app ? "${local.app_slug}-cli" : null
cli_app_icon = var.cli_app ? var.icon : null
cli_app_display_name = var.cli_app ? var.cli_app_display_name : null
agentapi_subdomain = var.subdomain
module_dir_name = local.module_dir_name
@@ -295,6 +296,7 @@ module "agentapi" {
ARG_COPILOT_CONFIG='${base64encode(local.final_copilot_config)}' \
ARG_EXTERNAL_AUTH_ID='${var.external_auth_id}' \
ARG_COPILOT_VERSION='${var.copilot_version}' \
ARG_COPILOT_MODEL='${var.copilot_model}' \
/tmp/install.sh
EOT
}
@@ -14,6 +14,7 @@ ARG_MCP_CONFIG=$(echo -n "${ARG_MCP_CONFIG:-}" | base64 -d 2> /dev/null || echo
ARG_COPILOT_CONFIG=$(echo -n "${ARG_COPILOT_CONFIG:-}" | base64 -d 2> /dev/null || echo "")
ARG_EXTERNAL_AUTH_ID=${ARG_EXTERNAL_AUTH_ID:-github}
ARG_COPILOT_VERSION=${ARG_COPILOT_VERSION:-0.0.334}
ARG_COPILOT_MODEL=${ARG_COPILOT_MODEL:-claude-sonnet-4.5}
validate_prerequisites() {
if ! command_exists node; then
@@ -84,7 +85,6 @@ setup_copilot_configurations() {
local module_path="$HOME/.copilot-module"
mkdir -p "$module_path"
mkdir -p "$HOME/.config"
setup_copilot_config
@@ -92,7 +92,8 @@ setup_copilot_configurations() {
}
setup_copilot_config() {
local copilot_config_dir="$HOME/.copilot"
export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
local copilot_config_dir="$XDG_CONFIG_HOME/.copilot"
local copilot_config_file="$copilot_config_dir/config.json"
local mcp_config_file="$copilot_config_dir/mcp-config.json"
@@ -82,6 +82,7 @@ check_existing_session() {
}
setup_github_authentication() {
export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
echo "Setting up GitHub authentication..."
if [ -n "${GITHUB_TOKEN:-}" ]; then
@@ -13,7 +13,7 @@ Run the Cursor Agent CLI in your workspace for interactive coding assistance and
```tf
module "cursor_cli" {
source = "registry.coder.com/coder-labs/cursor-cli/coder"
version = "0.1.1"
version = "0.2.0"
agent_id = coder_agent.example.id
folder = "/home/coder/project"
}
@@ -42,7 +42,7 @@ module "coder-login" {
module "cursor_cli" {
source = "registry.coder.com/coder-labs/cursor-cli/coder"
version = "0.1.1"
version = "0.2.0"
agent_id = coder_agent.example.id
folder = "/home/coder/project"
@@ -56,7 +56,7 @@ variable "install_agentapi" {
variable "agentapi_version" {
type = string
description = "The version of AgentAPI to install."
default = "v0.5.0"
default = "v0.10.0"
}
variable "force" {
@@ -131,7 +131,7 @@ resource "coder_env" "cursor_api_key" {
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "1.1.1"
version = "1.2.0"
agent_id = var.agent_id
web_app_slug = local.app_slug
+4 -4
View File
@@ -13,7 +13,7 @@ Run [Gemini CLI](https://github.com/google-gemini/gemini-cli) in your workspace
```tf
module "gemini" {
source = "registry.coder.com/coder-labs/gemini/coder"
version = "2.0.0"
version = "2.1.0"
agent_id = coder_agent.example.id
folder = "/home/coder/project"
}
@@ -46,7 +46,7 @@ variable "gemini_api_key" {
module "gemini" {
source = "registry.coder.com/coder-labs/gemini/coder"
version = "2.0.0"
version = "2.1.0"
agent_id = coder_agent.example.id
gemini_api_key = var.gemini_api_key
folder = "/home/coder/project"
@@ -94,7 +94,7 @@ data "coder_parameter" "ai_prompt" {
module "gemini" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder-labs/gemini/coder"
version = "2.0.0"
version = "2.1.0"
agent_id = coder_agent.example.id
gemini_api_key = var.gemini_api_key
gemini_model = "gemini-2.5-flash"
@@ -118,7 +118,7 @@ For enterprise users who prefer Google's Vertex AI platform:
```tf
module "gemini" {
source = "registry.coder.com/coder-labs/gemini/coder"
version = "2.0.0"
version = "2.1.0"
agent_id = coder_agent.example.id
gemini_api_key = var.gemini_api_key
folder = "/home/coder/project"
+2 -2
View File
@@ -81,7 +81,7 @@ variable "install_agentapi" {
variable "agentapi_version" {
type = string
description = "The version of AgentAPI to install."
default = "v0.2.3"
default = "v0.10.0"
}
variable "gemini_model" {
@@ -176,7 +176,7 @@ EOT
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "1.1.1"
version = "1.2.0"
agent_id = var.agent_id
web_app_slug = local.app_slug
@@ -13,7 +13,7 @@ Run [Amp CLI](https://ampcode.com/) in your workspace to access Sourcegraph's AI
```tf
module "amp-cli" {
source = "registry.coder.com/coder-labs/sourcegraph-amp/coder"
version = "1.0.3"
version = "1.1.0"
agent_id = coder_agent.example.id
sourcegraph_amp_api_key = var.sourcegraph_amp_api_key
install_sourcegraph_amp = true
@@ -60,7 +60,7 @@ variable "sourcegraph_amp_api_key" {
module "amp-cli" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder-labs/sourcegraph-amp/coder"
version = "1.0.3"
version = "1.1.0"
agent_id = coder_agent.example.id
sourcegraph_amp_api_key = var.sourcegraph_amp_api_key # recommended for authenticated usage
install_sourcegraph_amp = true
@@ -69,7 +69,7 @@ variable "install_agentapi" {
variable "agentapi_version" {
type = string
description = "The version of AgentAPI to install."
default = "v0.3.0"
default = "v0.10.0"
}
variable "pre_install_script" {
@@ -151,7 +151,7 @@ locals {
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "1.0.1"
version = "1.2.0"
agent_id = var.agent_id
web_app_slug = local.app_slug
+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.1.1"
version = "1.2.0"
agent_id = var.agent_id
web_app_slug = local.app_slug
+1 -1
View File
@@ -117,7 +117,7 @@ variable "install_agentapi" {
variable "agentapi_version" {
type = string
description = "The version of AgentAPI to install."
default = "v0.3.3"
default = "v0.10.0"
}
variable "agentapi_port" {
+10 -10
View File
@@ -13,7 +13,7 @@ Run [Amazon Q](https://aws.amazon.com/q/) in your workspace to access Amazon's A
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
version = "2.1.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
@@ -102,7 +102,7 @@ data "coder_parameter" "ai_prompt" {
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
version = "2.1.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
@@ -228,7 +228,7 @@ If no custom `agent_config` is provided, the default agent name "agent" is used.
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
version = "2.1.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
@@ -258,7 +258,7 @@ This example will:
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
version = "2.1.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
@@ -279,7 +279,7 @@ module "amazon-q" {
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
version = "2.1.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
@@ -305,7 +305,7 @@ module "amazon-q" {
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
version = "2.1.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
@@ -319,7 +319,7 @@ module "amazon-q" {
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
version = "2.1.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
@@ -340,14 +340,14 @@ module "amazon-q" {
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
version = "2.1.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
# AgentAPI configuration for environments without wildcard access url. https://coder.com/docs/admin/setup#wildcard-access-url
agentapi_chat_based_path = true
agentapi_version = "v0.6.1"
agentapi_version = "v0.10.0"
}
```
@@ -358,7 +358,7 @@ For environments without direct internet access, you can host Amazon Q installat
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
version = "2.1.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
+2 -2
View File
@@ -88,7 +88,7 @@ variable "post_install_script" {
variable "agentapi_version" {
type = string
description = "The version of AgentAPI to install."
default = "v0.6.1"
default = "v0.10.0"
}
variable "workdir" {
@@ -215,7 +215,7 @@ locals {
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "1.1.1"
version = "1.2.0"
agent_id = var.agent_id
web_app_slug = local.app_slug
+4 -4
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 = "3.0.3"
version = "3.1.0"
agent_id = coder_agent.example.id
workdir = "/home/coder/project"
claude_api_key = "xxxx-xxxxx-xxxx"
@@ -49,7 +49,7 @@ data "coder_parameter" "ai_prompt" {
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "3.0.3"
version = "3.1.0"
agent_id = coder_agent.example.id
workdir = "/home/coder/project"
@@ -58,7 +58,7 @@ module "claude-code" {
claude_code_oauth_token = "xxxxx-xxxx-xxxx"
claude_code_version = "1.0.82" # Pin to a specific version
agentapi_version = "v0.6.1"
agentapi_version = "v0.10.0"
ai_prompt = data.coder_parameter.ai_prompt.value
model = "sonnet"
@@ -85,7 +85,7 @@ Run and configure Claude Code as a standalone CLI in your workspace.
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "3.0.3"
version = "3.1.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
install_claude_code = true
+2 -2
View File
@@ -86,7 +86,7 @@ variable "install_agentapi" {
variable "agentapi_version" {
type = string
description = "The version of AgentAPI to install."
default = "v0.7.1"
default = "v0.10.0"
}
variable "ai_prompt" {
@@ -265,7 +265,7 @@ locals {
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "1.1.1"
version = "1.2.0"
agent_id = var.agent_id
web_app_slug = local.app_slug
+10 -10
View File
@@ -14,7 +14,7 @@ This module allows you to automatically clone a repository by URL and skip if it
module "git-clone" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/git-clone/coder"
version = "1.1.1"
version = "1.1.2"
agent_id = coder_agent.example.id
url = "https://github.com/coder/coder"
}
@@ -28,7 +28,7 @@ module "git-clone" {
module "git-clone" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/git-clone/coder"
version = "1.1.1"
version = "1.1.2"
agent_id = coder_agent.example.id
url = "https://github.com/coder/coder"
base_dir = "~/projects/coder"
@@ -43,12 +43,12 @@ To use with [Git Authentication](https://coder.com/docs/v2/latest/admin/git-prov
module "git-clone" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/git-clone/coder"
version = "1.1.1"
version = "1.1.2"
agent_id = coder_agent.example.id
url = "https://github.com/coder/coder"
}
data "coder_git_auth" "github" {
data "coder_external_auth" "github" {
id = "github"
}
```
@@ -69,7 +69,7 @@ data "coder_parameter" "git_repo" {
module "git_clone" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/git-clone/coder"
version = "1.1.1"
version = "1.1.2"
agent_id = coder_agent.example.id
url = data.coder_parameter.git_repo.value
}
@@ -103,7 +103,7 @@ Configuring `git-clone` for a self-hosted GitHub Enterprise Server running at `g
module "git-clone" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/git-clone/coder"
version = "1.1.1"
version = "1.1.2"
agent_id = coder_agent.example.id
url = "https://github.example.com/coder/coder/tree/feat/example"
git_providers = {
@@ -122,7 +122,7 @@ To GitLab clone with a specific branch like `feat/example`
module "git-clone" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/git-clone/coder"
version = "1.1.1"
version = "1.1.2"
agent_id = coder_agent.example.id
url = "https://gitlab.com/coder/coder/-/tree/feat/example"
}
@@ -134,7 +134,7 @@ Configuring `git-clone` for a self-hosted GitLab running at `gitlab.example.com`
module "git-clone" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/git-clone/coder"
version = "1.1.1"
version = "1.1.2"
agent_id = coder_agent.example.id
url = "https://gitlab.example.com/coder/coder/-/tree/feat/example"
git_providers = {
@@ -155,7 +155,7 @@ For example, to clone the `feat/example` branch:
module "git-clone" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/git-clone/coder"
version = "1.1.1"
version = "1.1.2"
agent_id = coder_agent.example.id
url = "https://github.com/coder/coder"
branch_name = "feat/example"
@@ -173,7 +173,7 @@ For example, this will clone into the `~/projects/coder/coder-dev` folder:
module "git-clone" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/git-clone/coder"
version = "1.1.1"
version = "1.1.2"
agent_id = coder_agent.example.id
url = "https://github.com/coder/coder"
folder_name = "coder-dev"
+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.1.2"
version = "2.2.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.1.2"
version = "2.2.0"
agent_id = coder_agent.example.id
folder = "/home/coder"
install_goose = true
+2 -2
View File
@@ -63,7 +63,7 @@ variable "install_agentapi" {
variable "agentapi_version" {
type = string
description = "The version of AgentAPI to install."
default = "v0.3.3"
default = "v0.10.0"
}
variable "subdomain" {
@@ -139,7 +139,7 @@ EOT
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "1.1.1"
version = "1.2.0"
agent_id = var.agent_id
web_app_slug = local.app_slug
@@ -10,6 +10,7 @@ tags: [ide, jetbrains, parameter, gateway]
This module adds a JetBrains Gateway Button to open any workspace with a single click.
> [!TIP]
> We recommend using the [Coder Toolbox module](https://registry.coder.com/modules/coder/jetbrains), which offers significant stability and connectivity benefits over Gateway. Reference our [documentation](https://coder.com/docs/user-guides/workspace-access/jetbrains/toolbox) for more information.
JetBrains recommends a minimum of 4 CPU cores and 8GB of RAM.
@@ -19,7 +20,7 @@ Consult the [JetBrains documentation](https://www.jetbrains.com/help/idea/prereq
module "jetbrains_gateway" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains-gateway/coder"
version = "1.2.4"
version = "1.2.5"
agent_id = coder_agent.example.id
folder = "/home/coder/example"
jetbrains_ides = ["CL", "GO", "IU", "PY", "WS"]
@@ -37,7 +38,7 @@ module "jetbrains_gateway" {
module "jetbrains_gateway" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains-gateway/coder"
version = "1.2.4"
version = "1.2.5"
agent_id = coder_agent.example.id
folder = "/home/coder/example"
jetbrains_ides = ["GO", "WS"]
@@ -51,7 +52,7 @@ module "jetbrains_gateway" {
module "jetbrains_gateway" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains-gateway/coder"
version = "1.2.4"
version = "1.2.5"
agent_id = coder_agent.example.id
folder = "/home/coder/example"
jetbrains_ides = ["IU", "PY"]
@@ -66,7 +67,7 @@ module "jetbrains_gateway" {
module "jetbrains_gateway" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains-gateway/coder"
version = "1.2.4"
version = "1.2.5"
agent_id = coder_agent.example.id
folder = "/home/coder/example"
jetbrains_ides = ["IU", "PY"]
@@ -91,7 +92,7 @@ module "jetbrains_gateway" {
module "jetbrains_gateway" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains-gateway/coder"
version = "1.2.4"
version = "1.2.5"
agent_id = coder_agent.example.id
folder = "/home/coder/example"
jetbrains_ides = ["GO", "WS"]
@@ -109,7 +110,7 @@ Due to the highest priority of the `ide_download_link` parameter in the `(jetbra
module "jetbrains_gateway" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains-gateway/coder"
version = "1.2.4"
version = "1.2.5"
agent_id = coder_agent.example.id
folder = "/home/coder/example"
jetbrains_ides = ["GO", "WS"]
+1 -1
View File
@@ -14,7 +14,7 @@ Automatically install [KasmVNC](https://kasmweb.com/kasmvnc) in a workspace, and
module "kasmvnc" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/kasmvnc/coder"
version = "1.2.3"
version = "1.2.4"
agent_id = coder_agent.example.id
desktop_environment = "xfce"
subdomain = true
+9 -8
View File
@@ -60,6 +60,9 @@ install_deb() {
sudo apt-get -o DPkg::Lock::Timeout=300 -qq update
fi
echo "Installing required Perl DateTime module..."
DEBIAN_FRONTEND=noninteractive sudo apt-get -o DPkg::Lock::Timeout=300 install --yes -qq --no-install-recommends --no-install-suggests libdatetime-perl
DEBIAN_FRONTEND=noninteractive sudo apt-get -o DPkg::Lock::Timeout=300 install --yes -qq --no-install-recommends --no-install-suggests "$kasmdeb"
rm "$kasmdeb"
}
@@ -233,19 +236,17 @@ get_http_dir() {
# Check the system configuration path
if [[ -e /etc/kasmvnc/kasmvnc.yaml ]]; then
d=($(grep -E "^\s*httpd_directory:.*$" /etc/kasmvnc/kasmvnc.yaml))
# If this grep is successful, it will return:
# httpd_directory: /usr/share/kasmvnc/www
if [[ $${#d[@]} -eq 2 && -d "$${d[1]}" ]]; then
httpd_directory="$${d[1]}"
d=$(grep -E '^\s*httpd_directory:.*$' "/etc/kasmvnc/kasmvnc.yaml" | awk '{print $$2}')
if [[ -n "$d" && -d "$d" ]]; then
httpd_directory=$d
fi
fi
# Check the home directory for overriding values
if [[ -e "$HOME/.vnc/kasmvnc.yaml" ]]; then
d=($(grep -E "^\s*httpd_directory:.*$" "$HOME/.vnc/kasmvnc.yaml"))
if [[ $${#d[@]} -eq 2 && -d "$${d[1]}" ]]; then
httpd_directory="$${d[1]}"
d=$(grep -E '^\s*httpd_directory:.*$' "$HOME/.vnc/kasmvnc.yaml" | awk '{print $$2}')
if [[ -n "$d" && -d "$d" ]]; then
httpd_directory=$d
fi
fi
echo $httpd_directory
@@ -426,15 +426,14 @@ module "code-server" {
# This ensures that the latest non-breaking version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production.
version = "~> 1.0"
agent_id = coder_agent.main.id
agent_name = "main"
order = 1
agent_id = coder_agent.main.id
order = 1
}
# See https://registry.coder.com/modules/coder/jetbrains
module "jetbrains" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/coder/jetbrains/coder"
source = "registry.coder.com/coder/jetbrains/coder"
version = "~> 1.0"
agent_id = coder_agent.main.id
agent_name = "main"
+2
View File
@@ -19,3 +19,5 @@ participating in LFX CNCF programs, and helping the developer community grow.
## Modules
- **aws-ami-snapshot**: Create and manage AMI snapshots for Coder workspaces with restore capabilities
- [nexus-repository](./modules/nexus-repository/) - Configure package managers to use Sonatype Nexus Repository
- [auto-start-dev-server](modules/auto-start-dev-server/README.md) - Automatically detect and start development servers for various project types
@@ -0,0 +1,151 @@
---
display_name: Auto-Start Dev Servers
description: Automatically detect and start development servers for various project types
icon: ../../../../.icons/auto-dev-server.svg
verified: false
tags: [development, automation, servers]
---
# Auto-Start Development Servers
Automatically detect and start development servers for various project types when a workspace starts. This module scans your workspace for common project structures and starts the appropriate development servers in the background without manual intervention.
```tf
module "auto_start_dev_servers" {
source = "registry.coder.com/mavrickrishi/auto-start-dev-server/coder"
version = "1.0.1"
agent_id = coder_agent.main.id
}
```
## Features
- **Multi-language support**: Detects and starts servers for Node.js, Python (Django/Flask), Ruby (Rails), Java (Spring Boot), Go, PHP, Rust, and .NET projects
- **Smart script prioritization**: Prioritizes `dev` scripts over `start` scripts for better development experience
- **Intelligent frontend detection**: Automatically identifies frontend projects (React, Vue, Angular, Next.js, Nuxt, Svelte, Vite) and prioritizes them for preview apps
- **Devcontainer integration**: Respects custom start commands defined in `.devcontainer/devcontainer.json`
- **Configurable scanning**: Adjustable directory scan depth and project type toggles
- **Non-blocking startup**: Servers start in the background with configurable startup delay
- **Comprehensive logging**: All server output and detection results logged to a central file
- **Smart detection**: Uses project-specific files and configurations to identify project types
- **Integrated live preview**: Automatically creates a preview app for the primary frontend project
## Supported Project Types
| Framework/Language | Detection Files | Start Commands (in priority order) |
| ------------------ | -------------------------------------------- | ----------------------------------------------------- |
| **Node.js/npm** | `package.json` | `npm run dev`, `npm run serve`, `npm start` (or yarn) |
| **Ruby on Rails** | `Gemfile` with rails gem | `bundle exec rails server` |
| **Django** | `manage.py` | `python manage.py runserver` |
| **Flask** | `requirements.txt` with Flask | `python app.py/main.py/run.py` |
| **Spring Boot** | `pom.xml` or `build.gradle` with spring-boot | `mvn spring-boot:run`, `gradle bootRun` |
| **Go** | `go.mod` | `go run main.go` |
| **PHP** | `composer.json` | `php -S 0.0.0.0:8080` |
| **Rust** | `Cargo.toml` | `cargo run` |
| **.NET** | `*.csproj` | `dotnet run` |
## Examples
### Basic Usage
```tf
module "auto_start" {
source = "./modules/auto-start-dev-server"
version = "1.0.1"
agent_id = coder_agent.main.id
}
```
### Advanced Usage
```tf
module "auto_start_dev_servers" {
source = "./modules/auto-start-dev-server"
version = "1.0.1"
agent_id = coder_agent.main.id
# Optional: Configure which project types to detect
enable_npm = true
enable_rails = true
enable_django = true
enable_flask = true
enable_spring_boot = true
enable_go = true
enable_php = true
enable_rust = true
enable_dotnet = true
# Optional: Enable devcontainer.json integration
enable_devcontainer = true
# Optional: Workspace directory to scan (supports environment variables)
workspace_directory = "$HOME"
# Optional: Directory scan depth (1-5)
scan_depth = 2
# Optional: Startup delay in seconds
startup_delay = 10
# Optional: Log file path
log_path = "/tmp/dev-servers.log"
# Optional: Enable automatic preview app (default: true)
enable_preview_app = true
}
```
### Disable Preview App
```tf
module "auto_start" {
source = "./modules/auto-start-dev-server"
version = "1.0.1"
agent_id = coder_agent.main.id
# Disable automatic preview app creation
enable_preview_app = false
}
```
### Selective Project Types
```tf
module "auto_start" {
source = "./modules/auto-start-dev-server"
version = "1.0.1"
agent_id = coder_agent.main.id
# Only enable web development projects
enable_npm = true
enable_rails = true
enable_django = true
enable_flask = true
# Disable other project types
enable_spring_boot = false
enable_go = false
enable_php = false
enable_rust = false
enable_dotnet = false
}
```
### Deep Workspace Scanning
```tf
module "auto_start" {
source = "./modules/auto-start-dev-server"
version = "1.0.1"
agent_id = coder_agent.main.id
workspace_directory = "/workspaces"
scan_depth = 3
startup_delay = 5
log_path = "/var/log/dev-servers.log"
}
```
## License
This module is provided under the same license as the Coder Registry.
@@ -0,0 +1,109 @@
import { describe, expect, it } from "bun:test";
import {
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "~test";
describe("auto-start-dev-server", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {
agent_id: "test-agent-123",
});
it("validates scan_depth range", () => {
const t1 = async () => {
await runTerraformApply(import.meta.dir, {
agent_id: "test-agent-123",
scan_depth: "0",
});
};
expect(t1).toThrow("Scan depth must be between 1 and 5");
const t2 = async () => {
await runTerraformApply(import.meta.dir, {
agent_id: "test-agent-123",
scan_depth: "6",
});
};
expect(t2).toThrow("Scan depth must be between 1 and 5");
});
it("applies successfully with default values", async () => {
await runTerraformApply(import.meta.dir, {
agent_id: "test-agent-123",
});
});
it("applies successfully with all project types enabled", async () => {
await runTerraformApply(import.meta.dir, {
agent_id: "test-agent-123",
enable_npm: "true",
enable_rails: "true",
enable_django: "true",
enable_flask: "true",
enable_spring_boot: "true",
enable_go: "true",
enable_php: "true",
enable_rust: "true",
enable_dotnet: "true",
enable_devcontainer: "true",
});
});
it("applies successfully with all project types disabled", async () => {
await runTerraformApply(import.meta.dir, {
agent_id: "test-agent-123",
enable_npm: "false",
enable_rails: "false",
enable_django: "false",
enable_flask: "false",
enable_spring_boot: "false",
enable_go: "false",
enable_php: "false",
enable_rust: "false",
enable_dotnet: "false",
enable_devcontainer: "false",
});
});
it("applies successfully with custom configuration", async () => {
await runTerraformApply(import.meta.dir, {
agent_id: "test-agent-123",
workspace_directory: "/custom/workspace",
scan_depth: "3",
startup_delay: "5",
log_path: "/var/log/custom-dev-servers.log",
display_name: "Custom Dev Server Startup",
});
});
it("validates scan_depth boundary values", async () => {
// Test valid boundary values
await runTerraformApply(import.meta.dir, {
agent_id: "test-agent-123",
scan_depth: "1",
});
await runTerraformApply(import.meta.dir, {
agent_id: "test-agent-123",
scan_depth: "5",
});
});
it("applies with selective project type configuration", async () => {
await runTerraformApply(import.meta.dir, {
agent_id: "test-agent-123",
enable_npm: "true",
enable_django: "true",
enable_go: "true",
enable_rails: "false",
enable_flask: "false",
enable_spring_boot: "false",
enable_php: "false",
enable_rust: "false",
enable_dotnet: "false",
});
});
});
@@ -0,0 +1,195 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 2.5"
}
}
}
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
variable "workspace_directory" {
type = string
description = "The directory to scan for development projects."
default = "$HOME"
}
variable "project_detection" {
type = bool
description = "Enable automatic project detection for all supported types. When true, all project types are detected unless individually disabled. When false, only explicitly enabled project types are detected."
default = true
}
variable "enable_npm" {
type = bool
description = "Enable auto-detection and startup of npm projects."
default = null
}
variable "enable_rails" {
type = bool
description = "Enable auto-detection and startup of Rails projects."
default = null
}
variable "enable_django" {
type = bool
description = "Enable auto-detection and startup of Django projects."
default = null
}
variable "enable_flask" {
type = bool
description = "Enable auto-detection and startup of Flask projects."
default = null
}
variable "enable_spring_boot" {
type = bool
description = "Enable auto-detection and startup of Spring Boot projects."
default = null
}
variable "enable_go" {
type = bool
description = "Enable auto-detection and startup of Go projects."
default = null
}
variable "enable_php" {
type = bool
description = "Enable auto-detection and startup of PHP projects."
default = null
}
variable "enable_rust" {
type = bool
description = "Enable auto-detection and startup of Rust projects."
default = null
}
variable "enable_dotnet" {
type = bool
description = "Enable auto-detection and startup of .NET projects."
default = null
}
variable "enable_devcontainer" {
type = bool
description = "Enable integration with devcontainer.json configuration."
default = null
}
variable "log_path" {
type = string
description = "The path to log development server output to."
default = "/tmp/dev-servers.log"
}
variable "scan_depth" {
type = number
description = "Maximum directory depth to scan for projects (1-5)."
default = 2
validation {
condition = var.scan_depth >= 1 && var.scan_depth <= 5
error_message = "Scan depth must be between 1 and 5."
}
}
variable "startup_delay" {
type = number
description = "Delay in seconds before starting dev servers (allows other setup to complete)."
default = 10
}
variable "display_name" {
type = string
description = "Display name for the auto-start dev server script."
default = "Auto-Start Dev Servers"
}
variable "enable_preview_app" {
type = bool
description = "Enable automatic creation of a preview app for the first detected project."
default = true
}
# Read the detected port from the file written by the script
locals {
detected_port = var.enable_preview_app ? try(tonumber(trimspace(file("/tmp/detected-port.txt"))), 3000) : 3000
# Attempt to read project information for better preview naming
detected_projects = try(jsondecode(file("/tmp/detected-projects.json")), [])
preview_project = length(local.detected_projects) > 0 ? local.detected_projects[0] : null
}
resource "coder_script" "auto_start_dev_server" {
agent_id = var.agent_id
display_name = var.display_name
icon = "/icon/auto-dev-server.svg"
script = templatefile("${path.module}/run.sh", {
WORKSPACE_DIR = var.workspace_directory
ENABLE_NPM = coalesce(var.enable_npm, var.project_detection)
ENABLE_RAILS = coalesce(var.enable_rails, var.project_detection)
ENABLE_DJANGO = coalesce(var.enable_django, var.project_detection)
ENABLE_FLASK = coalesce(var.enable_flask, var.project_detection)
ENABLE_SPRING_BOOT = coalesce(var.enable_spring_boot, var.project_detection)
ENABLE_GO = coalesce(var.enable_go, var.project_detection)
ENABLE_PHP = coalesce(var.enable_php, var.project_detection)
ENABLE_RUST = coalesce(var.enable_rust, var.project_detection)
ENABLE_DOTNET = coalesce(var.enable_dotnet, var.project_detection)
ENABLE_DEVCONTAINER = coalesce(var.enable_devcontainer, var.project_detection)
LOG_PATH = var.log_path
SCAN_DEPTH = var.scan_depth
STARTUP_DELAY = var.startup_delay
})
run_on_start = true
}
# Create preview app for first detected project
resource "coder_app" "preview" {
count = var.enable_preview_app ? 1 : 0
agent_id = var.agent_id
slug = "dev-preview"
display_name = "Live Preview"
url = "http://localhost:${local.detected_port}"
icon = "/icon/auto-dev-server.svg"
subdomain = true
share = "owner"
}
output "log_path" {
value = var.log_path
description = "Path to the log file for dev server output"
}
# Example output values for common port mappings
output "common_ports" {
value = {
nodejs = 3000
rails = 3000
django = 8000
flask = 5000
spring = 8080
go = 8080
php = 8080
rust = 8000
dotnet = 5000
}
description = "Common default ports for different project types"
}
output "preview_url" {
value = var.enable_preview_app ? try(coder_app.preview[0].url, null) : null
description = "URL of the live preview app (if enabled)"
}
output "detected_port" {
value = local.detected_port
description = "Port of the first detected development server"
}
+468
View File
@@ -0,0 +1,468 @@
#!/usr/bin/env bash
set -euo pipefail
# Color codes for output
BOLD='\033[0;1m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
RED='\033[0;31m'
RESET='\033[0m'
echo -e "$${BOLD}🚀 Auto-Start Development Servers$${RESET}"
echo "Workspace Directory: ${WORKSPACE_DIR}"
echo "Log Path: ${LOG_PATH}"
echo "Scan Depth: ${SCAN_DEPTH}"
# Wait for startup delay to allow other setup to complete
if [ "${STARTUP_DELAY}" -gt 0 ]; then
echo -e "$${YELLOW}⏳ Waiting ${STARTUP_DELAY} seconds for system initialization...$${RESET}"
sleep "${STARTUP_DELAY}"
fi
# Initialize log file
echo "=== Auto-Start Dev Servers Log ===" > "${LOG_PATH}"
echo "Started at: $(date)" >> "${LOG_PATH}"
# Initialize detected projects JSON file
DETECTED_PROJECTS_FILE="/tmp/detected-projects.json"
echo '[]' > "$DETECTED_PROJECTS_FILE"
# Initialize detected port file for preview app
DETECTED_PORT_FILE="/tmp/detected-port.txt"
FIRST_PORT_DETECTED=false
FRONTEND_PROJECT_DETECTED=false
# Function to log messages
log_message() {
echo -e "$1"
echo "$1" >> "${LOG_PATH}"
}
# Function to determine if a project is likely a frontend project
is_frontend_project() {
local project_dir="$1"
local project_type="$2"
# Check for common frontend indicators
if [ "$project_type" = "nodejs" ]; then
# Check package.json for frontend dependencies
if [ -f "$project_dir/package.json" ] && command -v jq &> /dev/null; then
# Check for common frontend frameworks
local has_react=$(jq '.dependencies.react // .devDependencies.react // empty' "$project_dir/package.json")
local has_vue=$(jq '.dependencies.vue // .devDependencies.vue // empty' "$project_dir/package.json")
local has_angular=$(jq '.dependencies["@angular/core"] // .devDependencies["@angular/core"] // empty' "$project_dir/package.json")
local has_next=$(jq '.dependencies.next // .devDependencies.next // empty' "$project_dir/package.json")
local has_nuxt=$(jq '.dependencies.nuxt // .devDependencies.nuxt // empty' "$project_dir/package.json")
local has_svelte=$(jq '.dependencies.svelte // .devDependencies.svelte // empty' "$project_dir/package.json")
local has_vite=$(jq '.dependencies.vite // .devDependencies.vite // empty' "$project_dir/package.json")
if [ -n "$has_react" ] || [ -n "$has_vue" ] || [ -n "$has_angular" ] \
|| [ -n "$has_next" ] || [ -n "$has_nuxt" ] || [ -n "$has_svelte" ] \
|| [ -n "$has_vite" ]; then
return 0 # It's a frontend project
fi
fi
# Check for common frontend directory structures
if [ -d "$project_dir/src/components" ] || [ -d "$project_dir/components" ] \
|| [ -d "$project_dir/pages" ] || [ -d "$project_dir/views" ] \
|| [ -f "$project_dir/index.html" ] || [ -f "$project_dir/public/index.html" ]; then
return 0 # It's likely a frontend project
fi
fi
# Rails projects with webpack/webpacker are frontend-enabled
if [ "$project_type" = "rails" ]; then
if [ -f "$project_dir/config/webpacker.yml" ] || [ -f "$project_dir/webpack.config.js" ]; then
return 0
fi
fi
# Django projects with static/templates are frontend-enabled
if [ "$project_type" = "django" ]; then
if [ -d "$project_dir/static" ] || [ -d "$project_dir/templates" ]; then
return 0
fi
fi
return 1 # Not a frontend project
}
# Function to add detected project to JSON
add_detected_project() {
local project_dir="$1"
local project_type="$2"
local port="$3"
local command="$4"
# Check if this is a frontend project
local is_frontend=false
if is_frontend_project "$project_dir" "$project_type"; then
is_frontend=true
log_message "$${BLUE}🎨 Detected frontend project at $project_dir$${RESET}"
fi
# Prioritize frontend projects for the preview app
# Set port if: 1) No port set yet, OR 2) This is frontend and no frontend detected yet
if [ "$FIRST_PORT_DETECTED" = false ] || ([ "$is_frontend" = true ] && [ "$FRONTEND_PROJECT_DETECTED" = false ]); then
echo "$port" > "$DETECTED_PORT_FILE"
FIRST_PORT_DETECTED=true
if [ "$is_frontend" = true ]; then
FRONTEND_PROJECT_DETECTED=true
log_message "$${BLUE}🎯 Frontend project detected - Preview app will be available on port $port$${RESET}"
else
log_message "$${BLUE}🎯 Project detected - Preview app will be available on port $port$${RESET}"
fi
fi
# Create JSON entry for this project
local project_json=$(jq -n \
--arg dir "$project_dir" \
--arg type "$project_type" \
--arg port "$port" \
--arg cmd "$command" \
--arg frontend "$is_frontend" \
'{"directory": $dir, "type": $type, "port": $port, "command": $cmd, "is_frontend": ($frontend == "true")}')
# Append to the detected projects file
jq ". += [$project_json]" "$DETECTED_PROJECTS_FILE" > "$DETECTED_PROJECTS_FILE.tmp" \
&& mv "$DETECTED_PROJECTS_FILE.tmp" "$DETECTED_PROJECTS_FILE"
}
# Function to detect and start npm/yarn projects
detect_npm_projects() {
if [ "${ENABLE_NPM}" != "true" ]; then
return
fi
log_message "$${BLUE}🔍 Scanning for Node.js/npm projects...$${RESET}"
# Use find with maxdepth to respect scan depth
while IFS= read -r -d '' package_json; do
project_dir=$(dirname "$package_json")
log_message "$${GREEN}📦 Found Node.js project: $project_dir$${RESET}"
cd "$project_dir"
# Check package.json for start script
if [ -f "package.json" ] && command -v jq &> /dev/null; then
start_script=$(jq -r '.scripts.start // empty' package.json)
dev_script=$(jq -r '.scripts.dev // empty' package.json)
serve_script=$(jq -r '.scripts.serve // empty' package.json)
# Determine port (check for common port configurations)
local project_port=3000
if [ -n "$dev_script" ] && echo "$dev_script" | grep -q "\-\-port"; then
project_port=$(echo "$dev_script" | grep -oE "\-\-port[[:space:]]+[0-9]+" | grep -oE "[0-9]+$" || echo "3000")
fi
# Use yarn if yarn.lock exists
local pkg_manager="npm"
local cmd_prefix=""
if [ -f "yarn.lock" ] && command -v yarn &> /dev/null; then
pkg_manager="yarn"
cmd_prefix=""
else
cmd_prefix="run "
fi
# Prioritize scripts: 'dev' > 'serve' > 'start' for development environments
if [ -n "$dev_script" ]; then
if [ "$pkg_manager" = "yarn" ]; then
log_message "$${GREEN}🟢 Starting project with 'yarn dev' in $project_dir$${RESET}"
nohup yarn dev >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "nodejs" "$project_port" "yarn dev"
else
log_message "$${GREEN}🟢 Starting project with 'npm run dev' in $project_dir$${RESET}"
nohup npm run dev >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "nodejs" "$project_port" "npm run dev"
fi
elif [ -n "$serve_script" ]; then
if [ "$pkg_manager" = "yarn" ]; then
log_message "$${GREEN}🟢 Starting project with 'yarn serve' in $project_dir$${RESET}"
nohup yarn serve >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "nodejs" "$project_port" "yarn serve"
else
log_message "$${GREEN}🟢 Starting project with 'npm run serve' in $project_dir$${RESET}"
nohup npm run serve >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "nodejs" "$project_port" "npm run serve"
fi
elif [ -n "$start_script" ]; then
if [ "$pkg_manager" = "yarn" ]; then
log_message "$${GREEN}🟢 Starting project with 'yarn start' in $project_dir$${RESET}"
nohup yarn start >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "nodejs" "$project_port" "yarn start"
else
log_message "$${GREEN}🟢 Starting project with 'npm start' in $project_dir$${RESET}"
nohup npm start >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "nodejs" "$project_port" "npm start"
fi
fi
fi
done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "package.json" -type f -print0)
}
# Function to detect and start Rails projects
detect_rails_projects() {
if [ "${ENABLE_RAILS}" != "true" ]; then
return
fi
log_message "$${BLUE}🔍 Scanning for Ruby on Rails projects...$${RESET}"
while IFS= read -r -d '' gemfile; do
project_dir=$(dirname "$gemfile")
log_message "$${GREEN}💎 Found Rails project: $project_dir$${RESET}"
cd "$project_dir"
# Check if it's actually a Rails project
if grep -q "gem ['\"]rails['\"]" Gemfile 2> /dev/null; then
log_message "$${GREEN}🟢 Starting Rails server in $project_dir$${RESET}"
nohup bundle exec rails server >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "rails" "3000" "bundle exec rails server"
fi
done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "Gemfile" -type f -print0)
}
# Function to detect and start Django projects
detect_django_projects() {
if [ "${ENABLE_DJANGO}" != "true" ]; then
return
fi
log_message "$${BLUE}🔍 Scanning for Django projects...$${RESET}"
while IFS= read -r -d '' manage_py; do
project_dir=$(dirname "$manage_py")
log_message "$${GREEN}🐍 Found Django project: $project_dir$${RESET}"
cd "$project_dir"
log_message "$${GREEN}🟢 Starting Django development server in $project_dir$${RESET}"
nohup python manage.py runserver 0.0.0.0:8000 >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "django" "8000" "python manage.py runserver"
done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "manage.py" -type f -print0)
}
# Function to detect and start Flask projects
detect_flask_projects() {
if [ "${ENABLE_FLASK}" != "true" ]; then
return
fi
log_message "$${BLUE}🔍 Scanning for Flask projects...$${RESET}"
while IFS= read -r -d '' requirements_txt; do
project_dir=$(dirname "$requirements_txt")
# Check if Flask is in requirements
if grep -q -i "flask" "$requirements_txt" 2> /dev/null; then
log_message "$${GREEN}🌶️ Found Flask project: $project_dir$${RESET}"
cd "$project_dir"
# Look for common Flask app files
for app_file in app.py main.py run.py; do
if [ -f "$app_file" ]; then
log_message "$${GREEN}🟢 Starting Flask application ($app_file) in $project_dir$${RESET}"
export FLASK_ENV=development
nohup python "$app_file" >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "flask" "5000" "python $app_file"
break
fi
done
fi
done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "requirements.txt" -type f -print0)
}
# Function to detect and start Spring Boot projects
detect_spring_boot_projects() {
if [ "${ENABLE_SPRING_BOOT}" != "true" ]; then
return
fi
log_message "$${BLUE}🔍 Scanning for Spring Boot projects...$${RESET}"
# Maven projects
while IFS= read -r -d '' pom_xml; do
project_dir=$(dirname "$pom_xml")
# Check if it's a Spring Boot project
if grep -q "spring-boot" "$pom_xml" 2> /dev/null; then
log_message "$${GREEN}🍃 Found Spring Boot Maven project: $project_dir$${RESET}"
cd "$project_dir"
if command -v ./mvnw &> /dev/null; then
log_message "$${GREEN}🟢 Starting Spring Boot application with Maven wrapper in $project_dir$${RESET}"
nohup ./mvnw spring-boot:run >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "spring-boot" "8080" "./mvnw spring-boot:run"
elif command -v mvn &> /dev/null; then
log_message "$${GREEN}🟢 Starting Spring Boot application with Maven in $project_dir$${RESET}"
nohup mvn spring-boot:run >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "spring-boot" "8080" "mvn spring-boot:run"
fi
fi
done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "pom.xml" -type f -print0)
# Gradle projects
while IFS= read -r -d '' build_gradle; do
project_dir=$(dirname "$build_gradle")
# Check if it's a Spring Boot project
if grep -q "spring-boot" "$build_gradle" 2> /dev/null; then
log_message "$${GREEN}🍃 Found Spring Boot Gradle project: $project_dir$${RESET}"
cd "$project_dir"
if command -v ./gradlew &> /dev/null; then
log_message "$${GREEN}🟢 Starting Spring Boot application with Gradle wrapper in $project_dir$${RESET}"
nohup ./gradlew bootRun >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "spring-boot" "8080" "./gradlew bootRun"
elif command -v gradle &> /dev/null; then
log_message "$${GREEN}🟢 Starting Spring Boot application with Gradle in $project_dir$${RESET}"
nohup gradle bootRun >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "spring-boot" "8080" "gradle bootRun"
fi
fi
done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "build.gradle" -type f -print0)
}
# Function to detect and start Go projects
detect_go_projects() {
if [ "${ENABLE_GO}" != "true" ]; then
return
fi
log_message "$${BLUE}🔍 Scanning for Go projects...$${RESET}"
while IFS= read -r -d '' go_mod; do
project_dir=$(dirname "$go_mod")
log_message "$${GREEN}🐹 Found Go project: $project_dir$${RESET}"
cd "$project_dir"
# Look for main.go or check if there's a main function
if [ -f "main.go" ]; then
log_message "$${GREEN}🟢 Starting Go application in $project_dir$${RESET}"
nohup go run main.go >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "go" "8080" "go run main.go"
elif [ -f "cmd/main.go" ]; then
log_message "$${GREEN}🟢 Starting Go application (cmd/main.go) in $project_dir$${RESET}"
nohup go run cmd/main.go >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "go" "8080" "go run cmd/main.go"
fi
done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "go.mod" -type f -print0)
}
# Function to detect and start PHP projects
detect_php_projects() {
if [ "${ENABLE_PHP}" != "true" ]; then
return
fi
log_message "$${BLUE}🔍 Scanning for PHP projects...$${RESET}"
while IFS= read -r -d '' composer_json; do
project_dir=$(dirname "$composer_json")
log_message "$${GREEN}🐘 Found PHP project: $project_dir$${RESET}"
cd "$project_dir"
# Look for common PHP entry points
for entry_file in index.php public/index.php; do
if [ -f "$entry_file" ]; then
log_message "$${GREEN}🟢 Starting PHP development server in $project_dir$${RESET}"
nohup php -S 0.0.0.0:8080 -t "$(dirname "$entry_file")" >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "php" "8080" "php -S 0.0.0.0:8080"
break
fi
done
done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "composer.json" -type f -print0)
}
# Function to detect and start Rust projects
detect_rust_projects() {
if [ "${ENABLE_RUST}" != "true" ]; then
return
fi
log_message "$${BLUE}🔍 Scanning for Rust projects...$${RESET}"
while IFS= read -r -d '' cargo_toml; do
project_dir=$(dirname "$cargo_toml")
log_message "$${GREEN}🦀 Found Rust project: $project_dir$${RESET}"
cd "$project_dir"
# Check if it's a binary project (has [[bin]] or default main.rs)
if grep -q "\[\[bin\]\]" Cargo.toml 2> /dev/null || [ -f "src/main.rs" ]; then
log_message "$${GREEN}🟢 Starting Rust application in $project_dir$${RESET}"
nohup cargo run >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "rust" "8000" "cargo run"
fi
done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "Cargo.toml" -type f -print0)
}
# Function to detect and start .NET projects
detect_dotnet_projects() {
if [ "${ENABLE_DOTNET}" != "true" ]; then
return
fi
log_message "$${BLUE}🔍 Scanning for .NET projects...$${RESET}"
while IFS= read -r -d '' csproj; do
project_dir=$(dirname "$csproj")
log_message "$${GREEN}🔷 Found .NET project: $project_dir$${RESET}"
cd "$project_dir"
log_message "$${GREEN}🟢 Starting .NET application in $project_dir$${RESET}"
nohup dotnet run >> "${LOG_PATH}" 2>&1 &
add_detected_project "$project_dir" "dotnet" "5000" "dotnet run"
done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "*.csproj" -type f -print0)
}
log_message "Starting auto-detection of development projects..."
# Expand workspace directory if it contains variables
WORKSPACE_DIR=$(eval echo "${WORKSPACE_DIR}")
# Check if workspace directory exists
if [ ! -d "$WORKSPACE_DIR" ]; then
log_message "$${RED}❌ Workspace directory does not exist: $WORKSPACE_DIR$${RESET}"
exit 1
fi
cd "$WORKSPACE_DIR"
# Run all detection functions
detect_npm_projects
detect_rails_projects
detect_django_projects
detect_flask_projects
detect_spring_boot_projects
detect_go_projects
detect_php_projects
detect_rust_projects
detect_dotnet_projects
log_message "$${GREEN}✅ Auto-start scan completed!$${RESET}"
log_message "$${YELLOW}💡 Check running processes with 'ps aux | grep -E \"(npm|rails|python|java|go|php|cargo|dotnet)\"'$${RESET}"
log_message "$${YELLOW}💡 View logs: tail -f ${LOG_PATH}$${RESET}"
# Set default port if no projects were detected
if [ "$FIRST_PORT_DETECTED" = false ]; then
echo "3000" > "$DETECTED_PORT_FILE"
log_message "$${YELLOW}⚠️ No projects detected - Preview app will default to port 3000$${RESET}"
fi
@@ -0,0 +1,149 @@
---
display_name: Nexus Repository
description: Configure package managers to use Sonatype Nexus Repository for Maven, npm, PyPI, and Docker registries.
icon: ../../../../.icons/nexus-repository.svg
verified: false
tags: [integration, nexus-repository, maven, npm, pypi, docker]
---
# Sonatype Nexus Repository
Configure package managers (Maven, npm, Go, PyPI, Docker) to use [Sonatype Nexus Repository](https://help.sonatype.com/en/sonatype-nexus-repository.html) with API token authentication. This module provides secure credential handling, multiple repository support per package manager, and flexible username configuration.
```tf
module "nexus_repository" {
source = "registry.coder.com/mavrickrishi/nexus-repository/coder"
version = "1.0.1"
agent_id = coder_agent.example.id
nexus_url = "https://nexus.example.com"
nexus_password = var.nexus_api_token
package_managers = {
maven = ["maven-public", "maven-releases"]
npm = ["npm-public", "@scoped:npm-private"]
go = ["go-public", "go-private"]
pypi = ["pypi-public", "pypi-private"]
docker = ["docker-public", "docker-private"]
}
}
```
## Requirements
- Nexus Repository Manager 3.x
- Valid API token or user credentials
- Package managers installed on the workspace (Maven, npm, Go, pip, Docker as needed)
> [!NOTE]
> This module configures package managers but does not install them. You need to handle the installation of Maven, npm, Go, Python pip, and Docker yourself.
## Examples
### Configure Maven to use Nexus repositories
```tf
module "nexus_repository" {
source = "registry.coder.com/mavrickrishi/nexus-repository/coder"
version = "1.0.1"
agent_id = coder_agent.example.id
nexus_url = "https://nexus.example.com"
nexus_password = var.nexus_api_token
package_managers = {
maven = ["maven-public", "maven-releases", "maven-snapshots"]
}
}
```
### Configure npm with scoped packages
```tf
module "nexus_repository" {
source = "registry.coder.com/mavrickrishi/nexus-repository/coder"
version = "1.0.1"
agent_id = coder_agent.example.id
nexus_url = "https://nexus.example.com"
nexus_password = var.nexus_api_token
package_managers = {
npm = ["npm-public", "@mycompany:npm-private"]
}
}
```
### Configure Go module proxy
```tf
module "nexus_repository" {
source = "registry.coder.com/mavrickrishi/nexus-repository/coder"
version = "1.0.1"
agent_id = coder_agent.example.id
nexus_url = "https://nexus.example.com"
nexus_password = var.nexus_api_token
package_managers = {
go = ["go-public", "go-private"]
}
}
```
### Configure Python PyPI repositories
```tf
module "nexus_repository" {
source = "registry.coder.com/mavrickrishi/nexus-repository/coder"
version = "1.0.1"
agent_id = coder_agent.example.id
nexus_url = "https://nexus.example.com"
nexus_password = var.nexus_api_token
package_managers = {
pypi = ["pypi-public", "pypi-private"]
}
}
```
### Configure Docker registries
```tf
module "nexus_repository" {
source = "registry.coder.com/mavrickrishi/nexus-repository/coder"
version = "1.0.1"
agent_id = coder_agent.example.id
nexus_url = "https://nexus.example.com"
nexus_password = var.nexus_api_token
package_managers = {
docker = ["docker-public", "docker-private"]
}
}
```
### Use custom username
```tf
module "nexus_repository" {
source = "registry.coder.com/mavrickrishi/nexus-repository/coder"
version = "1.0.1"
agent_id = coder_agent.example.id
nexus_url = "https://nexus.example.com"
nexus_username = "custom-user"
nexus_password = var.nexus_api_token
package_managers = {
maven = ["maven-public"]
}
}
```
### Complete configuration for all package managers
```tf
module "nexus_repository" {
source = "registry.coder.com/mavrickrishi/nexus-repository/coder"
version = "1.0.1"
agent_id = coder_agent.example.id
nexus_url = "https://nexus.example.com"
nexus_password = var.nexus_api_token
package_managers = {
maven = ["maven-public", "maven-releases"]
npm = ["npm-public", "@company:npm-private"]
go = ["go-public", "go-private"]
pypi = ["pypi-public", "pypi-private"]
docker = ["docker-public", "docker-private"]
}
}
```
@@ -0,0 +1,147 @@
import { describe, expect, it } from "bun:test";
import {
executeScriptInContainer,
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "~test";
describe("nexus-repository", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {
agent_id: "test-agent",
nexus_url: "https://nexus.example.com",
nexus_password: "test-password",
});
it("configures Maven settings", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "test-agent",
nexus_url: "https://nexus.example.com",
nexus_password: "test-token",
package_managers: JSON.stringify({
maven: ["maven-public"],
}),
});
const output = await executeScriptInContainer(state, "ubuntu:20.04");
expect(output.stdout.join("\n")).toContain("☕ Configuring Maven...");
expect(output.stdout.join("\n")).toContain("🥳 Configuration complete!");
});
it("configures npm registry", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "test-agent",
nexus_url: "https://nexus.example.com",
nexus_password: "test-token",
package_managers: JSON.stringify({
npm: ["npm-public"],
}),
});
const output = await executeScriptInContainer(state, "ubuntu:20.04");
expect(output.stdout.join("\n")).toContain("📦 Configuring npm...");
expect(output.stdout.join("\n")).toContain("🥳 Configuration complete!");
});
it("configures PyPI repository", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "test-agent",
nexus_url: "https://nexus.example.com",
nexus_password: "test-token",
package_managers: JSON.stringify({
pypi: ["pypi-public"],
}),
});
const output = await executeScriptInContainer(state, "ubuntu:20.04");
expect(output.stdout.join("\n")).toContain("🐍 Configuring pip...");
expect(output.stdout.join("\n")).toContain("🥳 Configuration complete!");
});
it("configures multiple package managers", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "test-agent",
nexus_url: "https://nexus.example.com",
nexus_password: "test-token",
package_managers: JSON.stringify({
maven: ["maven-public"],
npm: ["npm-public"],
pypi: ["pypi-public"],
}),
});
const output = await executeScriptInContainer(state, "ubuntu:20.04");
expect(output.stdout.join("\n")).toContain("☕ Configuring Maven...");
expect(output.stdout.join("\n")).toContain("📦 Configuring npm...");
expect(output.stdout.join("\n")).toContain("🐍 Configuring pip...");
expect(output.stdout.join("\n")).toContain(
"✅ Nexus repository configuration completed!",
);
});
it("handles empty package managers", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "test-agent",
nexus_url: "https://nexus.example.com",
nexus_password: "test-token",
package_managers: JSON.stringify({}),
});
const output = await executeScriptInContainer(state, "ubuntu:20.04");
expect(output.stdout.join("\n")).toContain(
"🤔 no maven repository is set, skipping maven configuration.",
);
expect(output.stdout.join("\n")).toContain(
"🤔 no npm repository is set, skipping npm configuration.",
);
expect(output.stdout.join("\n")).toContain(
"🤔 no pypi repository is set, skipping pypi configuration.",
);
expect(output.stdout.join("\n")).toContain(
"🤔 no docker repository is set, skipping docker configuration.",
);
});
it("configures Go module proxy", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "test-agent",
nexus_url: "https://nexus.example.com",
nexus_password: "test-token",
package_managers: JSON.stringify({
go: ["go-public", "go-private"],
}),
});
const output = await executeScriptInContainer(state, "ubuntu:20.04");
expect(output.stdout.join("\n")).toContain("🐹 Configuring Go...");
expect(output.stdout.join("\n")).toContain(
"Go proxy configured via GOPROXY environment variable",
);
expect(output.stdout.join("\n")).toContain("🥳 Configuration complete!");
});
it("validates nexus_url format", async () => {
await expect(
runTerraformApply(import.meta.dir, {
agent_id: "test-agent",
nexus_url: "invalid-url",
nexus_password: "test-token",
package_managers: JSON.stringify({}),
}),
).rejects.toThrow();
});
it("validates username_field values", async () => {
await expect(
runTerraformApply(import.meta.dir, {
agent_id: "test-agent",
nexus_url: "https://nexus.example.com",
nexus_password: "test-token",
username_field: "invalid",
package_managers: JSON.stringify({}),
}),
).rejects.toThrow();
});
});
@@ -0,0 +1,137 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 2.5"
}
}
}
variable "nexus_url" {
type = string
description = "The base URL of your Nexus repository manager (e.g. https://nexus.example.com)"
validation {
condition = can(regex("^(https|http)://", var.nexus_url))
error_message = "nexus_url must be a valid URL starting with either 'https://' or 'http://'"
}
}
variable "nexus_username" {
type = string
description = "Custom username for Nexus authentication. If not provided, defaults to the Coder username based on the username_field setting"
default = null
}
variable "nexus_password" {
type = string
description = "API token or password for Nexus authentication. This value is sensitive and should be stored securely"
sensitive = true
}
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
variable "package_managers" {
type = object({
maven = optional(list(string), [])
npm = optional(list(string), [])
go = optional(list(string), [])
pypi = optional(list(string), [])
docker = optional(list(string), [])
})
default = {
maven = []
npm = []
go = []
pypi = []
docker = []
}
description = <<-EOF
Configuration for package managers. Each key maps to a list of Nexus repository names:
- maven: List of Maven repository names
- npm: List of npm repository names (supports scoped packages with "@scope:repo-name")
- go: List of Go proxy repository names
- pypi: List of PyPI repository names
- docker: List of Docker registry names
Unused package managers can be omitted.
Example:
{
maven = ["maven-public", "maven-releases"]
npm = ["npm-public", "@scoped:npm-private"]
go = ["go-public", "go-private"]
pypi = ["pypi-public", "pypi-private"]
docker = ["docker-public", "docker-private"]
}
EOF
}
variable "username_field" {
type = string
description = "Field to use for username (\"username\" or \"email\"). Defaults to \"username\". Only used when nexus_username is not provided"
default = "username"
validation {
condition = can(regex("^(email|username)$", var.username_field))
error_message = "username_field must be either 'email' or 'username'"
}
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
locals {
username = coalesce(var.nexus_username, var.username_field == "email" ? data.coder_workspace_owner.me.email : data.coder_workspace_owner.me.name)
nexus_host = split("/", replace(replace(var.nexus_url, "https://", ""), "http://", ""))[0]
}
locals {
# Get first repository name or use default
maven_repo = length(var.package_managers.maven) > 0 ? var.package_managers.maven[0] : "maven-public"
npm_repo = length(var.package_managers.npm) > 0 ? var.package_managers.npm[0] : "npm-public"
go_repo = length(var.package_managers.go) > 0 ? var.package_managers.go[0] : "go-public"
pypi_repo = length(var.package_managers.pypi) > 0 ? var.package_managers.pypi[0] : "pypi-public"
npmrc = <<-EOF
registry=${var.nexus_url}/repository/${local.npm_repo}/
//${local.nexus_host}/repository/${local.npm_repo}/:username=${local.username}
//${local.nexus_host}/repository/${local.npm_repo}/:_password=${base64encode(var.nexus_password)}
//${local.nexus_host}/repository/${local.npm_repo}/:always-auth=true
EOF
}
resource "coder_script" "nexus" {
agent_id = var.agent_id
display_name = "nexus-repository"
icon = "/icon/nexus-repository.svg"
script = templatefile("${path.module}/run.sh", {
NEXUS_URL = var.nexus_url
NEXUS_HOST = local.nexus_host
NEXUS_USERNAME = local.username
NEXUS_PASSWORD = var.nexus_password
HAS_MAVEN = length(var.package_managers.maven) == 0 ? "" : "YES"
MAVEN_REPO = local.maven_repo
HAS_NPM = length(var.package_managers.npm) == 0 ? "" : "YES"
NPMRC = local.npmrc
HAS_GO = length(var.package_managers.go) == 0 ? "" : "YES"
GO_REPO = local.go_repo
HAS_PYPI = length(var.package_managers.pypi) == 0 ? "" : "YES"
PYPI_REPO = local.pypi_repo
HAS_DOCKER = length(var.package_managers.docker) == 0 ? "" : "YES"
REGISTER_DOCKER = join("\n ", formatlist("register_docker \"%s\"", var.package_managers.docker))
})
run_on_start = true
}
resource "coder_env" "goproxy" {
count = length(var.package_managers.go) == 0 ? 0 : 1
agent_id = var.agent_id
name = "GOPROXY"
value = join(",", [
for repo in var.package_managers.go :
"https://${local.username}:${var.nexus_password}@${local.nexus_host}/repository/${repo}"
])
}
@@ -0,0 +1,105 @@
#!/usr/bin/env bash
not_configured() {
type=$1
echo "🤔 no $type repository is set, skipping $type configuration."
}
config_complete() {
echo "🥳 Configuration complete!"
}
register_docker() {
repo=$1
echo -n "${NEXUS_PASSWORD}" | docker login "${NEXUS_HOST}/repository/$${repo}" --username "${NEXUS_USERNAME}" --password-stdin
}
echo "🚀 Configuring Nexus repository access..."
# Configure Maven
if [ -n "${HAS_MAVEN}" ]; then
echo "☕ Configuring Maven..."
mkdir -p ~/.m2
cat > ~/.m2/settings.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0">
<servers>
<server>
<id>nexus</id>
<username>${NEXUS_USERNAME}</username>
<password>${NEXUS_PASSWORD}</password>
</server>
</servers>
<mirrors>
<mirror>
<id>nexus-mirror</id>
<mirrorOf>*</mirrorOf>
<url>${NEXUS_URL}/repository/${MAVEN_REPO}</url>
</mirror>
</mirrors>
</settings>
EOF
config_complete
else
not_configured maven
fi
# Configure npm
if [ -n "${HAS_NPM}" ]; then
echo "📦 Configuring npm..."
cat > ~/.npmrc << 'EOF'
${NPMRC}
EOF
config_complete
else
not_configured npm
fi
# Configure Go
if [ -n "${HAS_GO}" ]; then
echo "🐹 Configuring Go..."
# Go configuration is handled via GOPROXY environment variable
# which is set by the Terraform configuration
echo "Go proxy configured via GOPROXY environment variable"
config_complete
else
not_configured go
fi
# Configure pip
if [ -n "${HAS_PYPI}" ]; then
echo "🐍 Configuring pip..."
mkdir -p ~/.pip
# Create .netrc file for secure credential storage
cat > ~/.netrc << EOF
machine ${NEXUS_HOST}
login ${NEXUS_USERNAME}
password ${NEXUS_PASSWORD}
EOF
chmod 600 ~/.netrc
# Update pip.conf to use index-url without embedded credentials
cat > ~/.pip/pip.conf << 'EOF'
[global]
index-url = https://${NEXUS_HOST}/repository/${PYPI_REPO}/simple
EOF
config_complete
else
not_configured pypi
fi
# Configure Docker
if [ -n "${HAS_DOCKER}" ]; then
if command -v docker > /dev/null 2>&1; then
echo "🐳 Configuring Docker credentials..."
mkdir -p ~/.docker
${REGISTER_DOCKER}
config_complete
else
echo "🤔 Docker is not installed, skipping Docker configuration."
fi
else
not_configured docker
fi
echo "✅ Nexus repository configuration completed!"