mirror of
https://github.com/coder/registry.git
synced 2026-06-03 04:58:15 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fc071e0930 | |||
| d516aff908 | |||
| ccdca6daf5 | |||
| ce039f64df | |||
| 8acda84dd7 | |||
| 76c1299968 |
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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"]
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -19,4 +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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
display_name: Auto-Start Development Servers
|
||||
display_name: Auto-Start Dev Servers
|
||||
description: Automatically detect and start development servers for various project types
|
||||
icon: ../../../../.icons/server.svg
|
||||
icon: ../../../../.icons/auto-dev-server.svg
|
||||
verified: false
|
||||
tags: [development, automation, servers]
|
||||
---
|
||||
@@ -13,7 +13,7 @@ Automatically detect and start development servers for various project types whe
|
||||
```tf
|
||||
module "auto_start_dev_servers" {
|
||||
source = "registry.coder.com/mavrickrishi/auto-start-dev-server/coder"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
agent_id = coder_agent.main.id
|
||||
}
|
||||
```
|
||||
@@ -48,20 +48,20 @@ module "auto_start_dev_servers" {
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```hcl
|
||||
```tf
|
||||
module "auto_start" {
|
||||
source = "./modules/auto-start-dev-server"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
agent_id = coder_agent.main.id
|
||||
}
|
||||
```
|
||||
|
||||
### Advanced Usage
|
||||
|
||||
```hcl
|
||||
```tf
|
||||
module "auto_start_dev_servers" {
|
||||
source = "./modules/auto-start-dev-server"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
agent_id = coder_agent.main.id
|
||||
|
||||
# Optional: Configure which project types to detect
|
||||
@@ -70,10 +70,10 @@ module "auto_start_dev_servers" {
|
||||
enable_django = true
|
||||
enable_flask = true
|
||||
enable_spring_boot = true
|
||||
enable_go = true
|
||||
enable_php = true
|
||||
enable_rust = true
|
||||
enable_dotnet = true
|
||||
enable_go = true
|
||||
enable_php = true
|
||||
enable_rust = true
|
||||
enable_dotnet = true
|
||||
|
||||
# Optional: Enable devcontainer.json integration
|
||||
enable_devcontainer = true
|
||||
@@ -97,10 +97,10 @@ module "auto_start_dev_servers" {
|
||||
|
||||
### Disable Preview App
|
||||
|
||||
```hcl
|
||||
```tf
|
||||
module "auto_start" {
|
||||
source = "./modules/auto-start-dev-server"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
agent_id = coder_agent.main.id
|
||||
|
||||
# Disable automatic preview app creation
|
||||
@@ -110,10 +110,10 @@ module "auto_start" {
|
||||
|
||||
### Selective Project Types
|
||||
|
||||
```hcl
|
||||
```tf
|
||||
module "auto_start" {
|
||||
source = "./modules/auto-start-dev-server"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
agent_id = coder_agent.main.id
|
||||
|
||||
# Only enable web development projects
|
||||
@@ -124,25 +124,25 @@ module "auto_start" {
|
||||
|
||||
# Disable other project types
|
||||
enable_spring_boot = false
|
||||
enable_go = false
|
||||
enable_php = false
|
||||
enable_rust = false
|
||||
enable_dotnet = false
|
||||
enable_go = false
|
||||
enable_php = false
|
||||
enable_rust = false
|
||||
enable_dotnet = false
|
||||
}
|
||||
```
|
||||
|
||||
### Deep Workspace Scanning
|
||||
|
||||
```hcl
|
||||
```tf
|
||||
module "auto_start" {
|
||||
source = "./modules/auto-start-dev-server"
|
||||
version = "1.0.0"
|
||||
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"
|
||||
scan_depth = 3
|
||||
startup_delay = 5
|
||||
log_path = "/var/log/dev-servers.log"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ locals {
|
||||
resource "coder_script" "auto_start_dev_server" {
|
||||
agent_id = var.agent_id
|
||||
display_name = var.display_name
|
||||
icon = "/icon/server.svg"
|
||||
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)
|
||||
@@ -158,7 +158,7 @@ resource "coder_app" "preview" {
|
||||
slug = "dev-preview"
|
||||
display_name = "Live Preview"
|
||||
url = "http://localhost:${local.detected_port}"
|
||||
icon = "/icon/globe.svg"
|
||||
icon = "/icon/auto-dev-server.svg"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
}
|
||||
|
||||
@@ -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!"
|
||||
Reference in New Issue
Block a user