-
Notifications
You must be signed in to change notification settings - Fork 43
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
MAVRICK-1
wants to merge
20
commits into
coder:main
Choose a base branch
from
MAVRICK-1:feat/nexus-repository-module
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
e32a0b7
feat: add Sonatype Nexus repository integration module
MAVRICK-1 e2d8aad
Update registry/mavrickrishi/modules/nexus/main.tf
MAVRICK-1 2a892ad
Update registry/mavrickrishi/modules/nexus/main.tf
MAVRICK-1 20e9a32
Update registry/mavrickrishi/modules/nexus/main.tf
MAVRICK-1 af94e5b
fix: apply code formatting
MAVRICK-1 db7afc8
fixed changes
MAVRICK-1 09905c3
Update registry/mavrickrishi/modules/nexus/README.md
MAVRICK-1 56f5990
Update registry/mavrickrishi/modules/nexus/main.tf
MAVRICK-1 3c890d0
Update registry/mavrickrishi/modules/nexus/main.tf
MAVRICK-1 88f8284
fix: update Nexus module configurations and README details
MAVRICK-1 f375e5f
feat: add support for Go package manager in Nexus module and update R…
MAVRICK-1 97d144b
feat: add test for configuring Go module proxy in Nexus module
MAVRICK-1 1bd0085
feat: Add Nexus Repository module and related configurations
MAVRICK-1 1b01b37
Merge branch 'main' into feat/nexus-repository-module
MAVRICK-1 5534564
Delete .icons/nexus.svg
MAVRICK-1 0854c5e
fix: remove false positive entries from typos.toml and adjust usernam…
MAVRICK-1 c520a19
fix: correct username entry for mavrickrishi in typos.toml
MAVRICK-1 f42da8f
Merge branch 'main' into feat/nexus-repository-module
MAVRICK-1 9f2fe38
Merge branch 'main' into feat/nexus-repository-module
MAVRICK-1 0a10b4d
Merge branch 'main' into feat/nexus-repository-module
MAVRICK-1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
verified: true | ||
MAVRICK-1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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"] | ||
} | ||
} | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
Show resolved
Hide resolved
|
||
} | ||
|
||
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.
Show resolved
Hide resolved
|
||
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 | ||
} | ||
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the correct path. It should be the relative path to .icons/nexus.svg