- Notifications
You must be signed in to change notification settings - Fork72
Add Sonatype Nexus repository integration module#262
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Uh oh!
There was an error while loading.Please reload this page.
Changes from6 commits
e32a0b7
e2d8aad
2a892ad
20e9a32
af94e5b
db7afc8
09905c3
56f5990
3c890d0
88f8284
f375e5f
97d144b
1bd0085
1b01b37
5534564
0854c5e
c520a19
f42da8f
9f2fe38
0a10b4d
8de8da8
0fd038a
9f2f3a4
4a45f09
3bdcac7
b25317a
013f756
5367304
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
--- | ||
display_name: mavrickrishi | ||
bio: Coder module contributor | ||
github: MAVRICK-1 | ||
status: community | ||
--- | ||
# mavrickrishi | ||
This directory contains Coder modules and templates created by mavrickrishi. | ||
## Modules | ||
- [nexus](./modules/nexus/) - Configure package managers to use Sonatype Nexus Repository |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
--- | ||
display_name: Sonatype Nexus Repository | ||
description: Configure package managers to use Sonatype Nexus Repository for Maven, npm, PyPI, and Docker registries. | ||
icon: /.icons/nexus.svg | ||
MAVRICK-1 marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
verified: true | ||
MAVRICK-1 marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
tags: [integration, nexus, maven, npm, pypi, docker] | ||
--- | ||
# Sonatype Nexus Repository | ||
Configure package managers (Maven, npm, 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" { | ||
source = "registry.coder.com/mavrickrishi/nexus/coder" | ||
version = "1.0.0" | ||
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"] | ||
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, pip, Docker as needed) | ||
> [!NOTE] | ||
> This module configures package managers but does not install them. You need to handle the installation of Maven, npm, Python pip, and Docker yourself. | ||
## Examples | ||
### Configure Maven to use Nexus repositories | ||
```tf | ||
module "nexus" { | ||
source = "registry.coder.com/mavrickrishi/nexus/coder" | ||
version = "1.0.0" | ||
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" { | ||
source = "registry.coder.com/mavrickrishi/nexus/coder" | ||
version = "1.0.0" | ||
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 Python PyPI repositories | ||
```tf | ||
module "nexus" { | ||
source = "registry.coder.com/mavrickrishi/nexus/coder" | ||
version = "1.0.0" | ||
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" { | ||
source = "registry.coder.com/mavrickrishi/nexus/coder" | ||
version = "1.0.0" | ||
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" { | ||
source = "registry.coder.com/mavrickrishi/nexus/coder" | ||
version = "1.0.0" | ||
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" { | ||
source = "registry.coder.com/mavrickrishi/nexus/coder" | ||
version = "1.0.0" | ||
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"] | ||
pypi = ["pypi-public", "pypi-private"] | ||
docker = ["docker-public", "docker-private"] | ||
} | ||
} | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import { describe, expect, it } from "bun:test"; | ||
import { | ||
executeScriptInContainer, | ||
runTerraformApply, | ||
runTerraformInit, | ||
testRequiredVariables, | ||
} from "~test"; | ||
describe("nexus", 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("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(); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
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), []) | ||
pypi = optional(list(string), []) | ||
docker = optional(list(string), []) | ||
}) | ||
default = { | ||
maven = [] | ||
npm = [] | ||
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") | ||
- 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"] | ||
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 = regex("^https?://([^:/]+)", var.nexus_url) | ||
MAVRICK-1 marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
} | ||
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" | ||
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" | ||
icon = "/icon/nexus.svg" | ||
script = templatefile("${path.module}/run.sh", { | ||
NEXUS_URL = var.nexus_url | ||
NEXUS_HOST = local.nexus_host[1] | ||
MAVRICK-1 marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
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_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 | ||
} | ||
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.