From e32a0b72483464c3dbba2104c37596a440a65681 Mon Sep 17 00:00:00 2001 From: mavrick-1 Date: Tue, 29 Jul 2025 23:44:46 +0530 Subject: [PATCH 01/21] feat: add Sonatype Nexus repository integration module - Add nexus module with support for Maven, npm, PyPI, and Docker registries - Includes comprehensive test suite with 11 passing tests - Supports configurable repositories per package manager - Automatic configuration of package manager settings - Secure credential handling with API token support - Flexible username configuration (username or email) - Complete documentation with usage examples Resolves #202 --- registry/mavrickrishi/modules/nexus/README.md | 153 +++++++++++++++ .../mavrickrishi/modules/nexus/main.test.ts | 119 ++++++++++++ registry/mavrickrishi/modules/nexus/main.tf | 183 ++++++++++++++++++ 3 files changed, 455 insertions(+) create mode 100644 registry/mavrickrishi/modules/nexus/README.md create mode 100644 registry/mavrickrishi/modules/nexus/main.test.ts create mode 100644 registry/mavrickrishi/modules/nexus/main.tf diff --git a/registry/mavrickrishi/modules/nexus/README.md b/registry/mavrickrishi/modules/nexus/README.md new file mode 100644 index 000000000..341316698 --- /dev/null +++ b/registry/mavrickrishi/modules/nexus/README.md @@ -0,0 +1,153 @@ +--- +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 +tags: [integration, nexus, maven, npm, pypi, docker] +--- + +# Sonatype Nexus Repository + +Configure package managers (Maven, npm, PyPI, Docker) to use Sonatype Nexus Repository with API token authentication. + +```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"] + } +} +``` + +> 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"] + } +} +``` + +## Parameters + +- `nexus_url` (required): The base URL of your Nexus repository manager +- `nexus_password` (required): API token or password for authentication (sensitive) +- `nexus_username` (optional): Custom username (defaults to Coder username) +- `username_field` (optional): Field to use for username ("username" or "email", defaults to "username") +- `package_managers` (required): Configuration for package managers: + - `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 + +## Features + +- ✅ Maven repository configuration with settings.xml +- ✅ npm configuration with .npmrc (including scoped packages) +- ✅ Python pip configuration with pip.conf +- ✅ Docker registry authentication +- ✅ Secure credential handling +- ✅ Multiple repository support per package manager +- ✅ Flexible username configuration + +## Requirements + +- Nexus Repository Manager 3.x +- Valid API token or user credentials +- Package managers installed on the workspace (Maven, npm, pip, Docker as needed) \ No newline at end of file diff --git a/registry/mavrickrishi/modules/nexus/main.test.ts b/registry/mavrickrishi/modules/nexus/main.test.ts new file mode 100644 index 000000000..f20858725 --- /dev/null +++ b/registry/mavrickrishi/modules/nexus/main.test.ts @@ -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(); + }); +}); \ No newline at end of file diff --git a/registry/mavrickrishi/modules/nexus/main.tf b/registry/mavrickrishi/modules/nexus/main.tf new file mode 100644 index 000000000..cde026f98 --- /dev/null +++ b/registry/mavrickrishi/modules/nexus/main.tf @@ -0,0 +1,183 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 0.23" + } + } +} + +variable "nexus_url" { + type = string + description = "Nexus repository URL. 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 = "Username for Nexus authentication" + default = null +} + +variable "nexus_password" { + type = string + description = "Password or API token for Nexus authentication" + 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 + A map of package manager names to their respective Nexus repositories. Unused package managers can be omitted. + For 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 = "The field to use for the username. Default 'username'." + 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("://", var.nexus_url)[1] +} + +resource "coder_script" "nexus" { + agent_id = var.agent_id + display_name = "nexus" + icon = "/icon/nexus.svg" + script = <<-EOT +#!/usr/bin/env bash + +not_configured() { + type=$1 + echo "🤔 no $type repository is set, skipping $type configuration." +} + +config_complete() { + echo "🥳 Configuration complete!" +} + +echo "🚀 Configuring Nexus repository access..." + +# Configure Maven +if [ ${length(var.package_managers.maven)} -gt 0 ]; then + echo "☕ Configuring Maven..." + mkdir -p ~/.m2 + cat > ~/.m2/settings.xml << 'EOF' + + + + + nexus + ${local.username} + ${var.nexus_password} + + + + + nexus-mirror + * + ${var.nexus_url}/repository/${try(element(var.package_managers.maven, 0), "maven-public")} + + + +EOF + config_complete +else + not_configured maven +fi + +# Configure npm +if [ ${length(var.package_managers.npm)} -gt 0 ]; then + echo "📦 Configuring npm..." + cat > ~/.npmrc << 'EOF' +registry=${var.nexus_url}/repository/${try(element(var.package_managers.npm, 0), "npm-public")}/ +//${local.nexus_host}/repository/${try(element(var.package_managers.npm, 0), "npm-public")}/:username=${local.username} +//${local.nexus_host}/repository/${try(element(var.package_managers.npm, 0), "npm-public")}/:_password=${base64encode(var.nexus_password)} +//${local.nexus_host}/repository/${try(element(var.package_managers.npm, 0), "npm-public")}/:always-auth=true +EOF + config_complete +else + not_configured npm +fi + +# Configure pip +if [ ${length(var.package_managers.pypi)} -gt 0 ]; then + echo "🐍 Configuring pip..." + mkdir -p ~/.pip + cat > ~/.pip/pip.conf << 'EOF' +[global] +index-url = https://${local.username}:${var.nexus_password}@${local.nexus_host}/repository/${try(element(var.package_managers.pypi, 0), "pypi-public")}/simple +EOF + config_complete +else + not_configured pypi +fi + +# Configure Docker +if [ ${length(var.package_managers.docker)} -gt 0 ]; then + if command -v docker > /dev/null 2>&1; then + echo "🐳 Configuring Docker credentials..." + mkdir -p ~/.docker + %{ for repo in var.package_managers.docker ~} + echo -n "${var.nexus_password}" | docker login "${repo}" --username "${local.username}" --password-stdin + %{ endfor ~} + config_complete + else + echo "🤔 Docker is not installed, skipping Docker configuration." + fi +else + not_configured docker +fi + +echo "✅ Nexus repository configuration completed!" +EOT + run_on_start = true +} + +output "nexus_url" { + description = "The Nexus repository URL" + value = var.nexus_url +} + +output "username" { + description = "The username used for Nexus authentication" + value = local.username +} \ No newline at end of file From e2d8aad5bbc163943ae3af93afff9701153d684c Mon Sep 17 00:00:00 2001 From: Rishi Mondal Date: Wed, 30 Jul 2025 00:04:28 +0530 Subject: [PATCH 02/21] Update registry/mavrickrishi/modules/nexus/main.tf Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- registry/mavrickrishi/modules/nexus/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/mavrickrishi/modules/nexus/main.tf b/registry/mavrickrishi/modules/nexus/main.tf index cde026f98..201868ef1 100644 --- a/registry/mavrickrishi/modules/nexus/main.tf +++ b/registry/mavrickrishi/modules/nexus/main.tf @@ -157,7 +157,7 @@ if [ ${length(var.package_managers.docker)} -gt 0 ]; then echo "🐳 Configuring Docker credentials..." mkdir -p ~/.docker %{ for repo in var.package_managers.docker ~} - echo -n "${var.nexus_password}" | docker login "${repo}" --username "${local.username}" --password-stdin + echo -n "${var.nexus_password}" | docker login "${local.nexus_host}" --username "${local.username}" --password-stdin %{ endfor ~} config_complete else From 2a892ad061f0b8d1a42af83a1379f6c05900cf80 Mon Sep 17 00:00:00 2001 From: Rishi Mondal Date: Wed, 30 Jul 2025 00:04:38 +0530 Subject: [PATCH 03/21] Update registry/mavrickrishi/modules/nexus/main.tf Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- registry/mavrickrishi/modules/nexus/main.tf | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/registry/mavrickrishi/modules/nexus/main.tf b/registry/mavrickrishi/modules/nexus/main.tf index 201868ef1..e70ec13de 100644 --- a/registry/mavrickrishi/modules/nexus/main.tf +++ b/registry/mavrickrishi/modules/nexus/main.tf @@ -142,9 +142,18 @@ fi if [ ${length(var.package_managers.pypi)} -gt 0 ]; then echo "🐍 Configuring pip..." mkdir -p ~/.pip + # Create .netrc file for secure credential storage + cat > ~/.netrc << EOF +machine ${local.nexus_host} +login ${local.username} +password ${var.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://${local.username}:${var.nexus_password}@${local.nexus_host}/repository/${try(element(var.package_managers.pypi, 0), "pypi-public")}/simple +index-url = https://${local.nexus_host}/repository/${try(element(var.package_managers.pypi, 0), "pypi-public")}/simple EOF config_complete else From 20e9a328d8490f0283ca56ca1c0ba990d4fa1567 Mon Sep 17 00:00:00 2001 From: Rishi Mondal Date: Wed, 30 Jul 2025 00:04:48 +0530 Subject: [PATCH 04/21] Update registry/mavrickrishi/modules/nexus/main.tf Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- registry/mavrickrishi/modules/nexus/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/mavrickrishi/modules/nexus/main.tf b/registry/mavrickrishi/modules/nexus/main.tf index e70ec13de..4aafca818 100644 --- a/registry/mavrickrishi/modules/nexus/main.tf +++ b/registry/mavrickrishi/modules/nexus/main.tf @@ -75,7 +75,7 @@ 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("://", var.nexus_url)[1] + nexus_host = regex("^https?://([^:/]+)", var.nexus_url) } resource "coder_script" "nexus" { From af94e5bcf2a71ca6d3d274b2df48ccf3c77e27cd Mon Sep 17 00:00:00 2001 From: mavrick-1 Date: Wed, 30 Jul 2025 00:07:16 +0530 Subject: [PATCH 05/21] fix: apply code formatting - Fix Terraform formatting with terraform fmt - Fix README formatting with prettier - All linting checks now pass --- registry/mavrickrishi/modules/nexus/README.md | 74 +++++++++---------- registry/mavrickrishi/modules/nexus/main.tf | 6 +- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/registry/mavrickrishi/modules/nexus/README.md b/registry/mavrickrishi/modules/nexus/README.md index 341316698..abe7987af 100644 --- a/registry/mavrickrishi/modules/nexus/README.md +++ b/registry/mavrickrishi/modules/nexus/README.md @@ -12,11 +12,11 @@ Configure package managers (Maven, npm, PyPI, Docker) to use Sonatype Nexus Repo ```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 + 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"] @@ -34,11 +34,11 @@ module "nexus" { ```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 + 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"] } @@ -49,11 +49,11 @@ module "nexus" { ```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 + 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"] } @@ -64,11 +64,11 @@ module "nexus" { ```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 + 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"] } @@ -79,11 +79,11 @@ module "nexus" { ```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 + 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"] } @@ -94,12 +94,12 @@ module "nexus" { ```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 + 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"] } @@ -110,11 +110,11 @@ module "nexus" { ```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 + 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"] @@ -150,4 +150,4 @@ module "nexus" { - Nexus Repository Manager 3.x - Valid API token or user credentials -- Package managers installed on the workspace (Maven, npm, pip, Docker as needed) \ No newline at end of file +- Package managers installed on the workspace (Maven, npm, pip, Docker as needed) diff --git a/registry/mavrickrishi/modules/nexus/main.tf b/registry/mavrickrishi/modules/nexus/main.tf index 4aafca818..16223e4df 100644 --- a/registry/mavrickrishi/modules/nexus/main.tf +++ b/registry/mavrickrishi/modules/nexus/main.tf @@ -82,7 +82,7 @@ resource "coder_script" "nexus" { agent_id = var.agent_id display_name = "nexus" icon = "/icon/nexus.svg" - script = <<-EOT + script = <<-EOT #!/usr/bin/env bash not_configured() { @@ -165,9 +165,9 @@ if [ ${length(var.package_managers.docker)} -gt 0 ]; then if command -v docker > /dev/null 2>&1; then echo "🐳 Configuring Docker credentials..." mkdir -p ~/.docker - %{ for repo in var.package_managers.docker ~} + %{for repo in var.package_managers.docker~} echo -n "${var.nexus_password}" | docker login "${local.nexus_host}" --username "${local.username}" --password-stdin - %{ endfor ~} + %{endfor~} config_complete else echo "🤔 Docker is not installed, skipping Docker configuration." From db7afc85fbd89571dd1bc5809b434336eb7582dd Mon Sep 17 00:00:00 2001 From: mavrick-1 Date: Wed, 30 Jul 2025 14:45:57 +0530 Subject: [PATCH 06/21] fixed changes --- .icons/nexus.svg | 7 + registry/mavrickrishi/README.md | 14 ++ registry/mavrickrishi/modules/nexus/README.md | 41 ++--- registry/mavrickrishi/modules/nexus/main.tf | 152 +++++------------- registry/mavrickrishi/modules/nexus/run.sh | 94 +++++++++++ 5 files changed, 165 insertions(+), 143 deletions(-) create mode 100644 .icons/nexus.svg create mode 100644 registry/mavrickrishi/README.md create mode 100644 registry/mavrickrishi/modules/nexus/run.sh diff --git a/.icons/nexus.svg b/.icons/nexus.svg new file mode 100644 index 000000000..c90645d66 --- /dev/null +++ b/.icons/nexus.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/registry/mavrickrishi/README.md b/registry/mavrickrishi/README.md new file mode 100644 index 000000000..61a0c983e --- /dev/null +++ b/registry/mavrickrishi/README.md @@ -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 diff --git a/registry/mavrickrishi/modules/nexus/README.md b/registry/mavrickrishi/modules/nexus/README.md index abe7987af..abae236a2 100644 --- a/registry/mavrickrishi/modules/nexus/README.md +++ b/registry/mavrickrishi/modules/nexus/README.md @@ -1,14 +1,14 @@ --- 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 +icon: /.icons/nexus.svg verified: true tags: [integration, nexus, maven, npm, pypi, docker] --- # Sonatype Nexus Repository -Configure package managers (Maven, npm, PyPI, Docker) to use Sonatype Nexus Repository with API token authentication. +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" { @@ -26,7 +26,14 @@ module "nexus" { } ``` -> 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. +## 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 @@ -123,31 +130,3 @@ module "nexus" { } } ``` - -## Parameters - -- `nexus_url` (required): The base URL of your Nexus repository manager -- `nexus_password` (required): API token or password for authentication (sensitive) -- `nexus_username` (optional): Custom username (defaults to Coder username) -- `username_field` (optional): Field to use for username ("username" or "email", defaults to "username") -- `package_managers` (required): Configuration for package managers: - - `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 - -## Features - -- ✅ Maven repository configuration with settings.xml -- ✅ npm configuration with .npmrc (including scoped packages) -- ✅ Python pip configuration with pip.conf -- ✅ Docker registry authentication -- ✅ Secure credential handling -- ✅ Multiple repository support per package manager -- ✅ Flexible username configuration - -## Requirements - -- Nexus Repository Manager 3.x -- Valid API token or user credentials -- Package managers installed on the workspace (Maven, npm, pip, Docker as needed) diff --git a/registry/mavrickrishi/modules/nexus/main.tf b/registry/mavrickrishi/modules/nexus/main.tf index 16223e4df..a9b3f8d2e 100644 --- a/registry/mavrickrishi/modules/nexus/main.tf +++ b/registry/mavrickrishi/modules/nexus/main.tf @@ -4,14 +4,14 @@ terraform { required_providers { coder = { source = "coder/coder" - version = ">= 0.23" + version = ">= 2.5" } } } variable "nexus_url" { type = string - description = "Nexus repository URL. e.g. https://nexus.example.com" + 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://'" @@ -20,13 +20,13 @@ variable "nexus_url" { variable "nexus_username" { type = string - description = "Username for Nexus authentication" + 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 = "Password or API token for Nexus authentication" + description = "API token or password for Nexus authentication. This value is sensitive and should be stored securely" sensitive = true } @@ -49,8 +49,13 @@ variable "package_managers" { docker = [] } description = <<-EOF - A map of package manager names to their respective Nexus repositories. Unused package managers can be omitted. - For example: + 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"] @@ -62,7 +67,7 @@ variable "package_managers" { variable "username_field" { type = string - description = "The field to use for the username. Default 'username'." + 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)) @@ -78,115 +83,38 @@ locals { nexus_host = regex("^https?://([^:/]+)", var.nexus_url) } +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 = <<-EOT -#!/usr/bin/env bash - -not_configured() { - type=$1 - echo "🤔 no $type repository is set, skipping $type configuration." -} - -config_complete() { - echo "🥳 Configuration complete!" -} - -echo "🚀 Configuring Nexus repository access..." - -# Configure Maven -if [ ${length(var.package_managers.maven)} -gt 0 ]; then - echo "☕ Configuring Maven..." - mkdir -p ~/.m2 - cat > ~/.m2/settings.xml << 'EOF' - - - - - nexus - ${local.username} - ${var.nexus_password} - - - - - nexus-mirror - * - ${var.nexus_url}/repository/${try(element(var.package_managers.maven, 0), "maven-public")} - - - -EOF - config_complete -else - not_configured maven -fi - -# Configure npm -if [ ${length(var.package_managers.npm)} -gt 0 ]; then - echo "📦 Configuring npm..." - cat > ~/.npmrc << 'EOF' -registry=${var.nexus_url}/repository/${try(element(var.package_managers.npm, 0), "npm-public")}/ -//${local.nexus_host}/repository/${try(element(var.package_managers.npm, 0), "npm-public")}/:username=${local.username} -//${local.nexus_host}/repository/${try(element(var.package_managers.npm, 0), "npm-public")}/:_password=${base64encode(var.nexus_password)} -//${local.nexus_host}/repository/${try(element(var.package_managers.npm, 0), "npm-public")}/:always-auth=true -EOF - config_complete -else - not_configured npm -fi - -# Configure pip -if [ ${length(var.package_managers.pypi)} -gt 0 ]; then - echo "🐍 Configuring pip..." - mkdir -p ~/.pip - # Create .netrc file for secure credential storage - cat > ~/.netrc << EOF -machine ${local.nexus_host} -login ${local.username} -password ${var.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://${local.nexus_host}/repository/${try(element(var.package_managers.pypi, 0), "pypi-public")}/simple -EOF - config_complete -else - not_configured pypi -fi - -# Configure Docker -if [ ${length(var.package_managers.docker)} -gt 0 ]; then - if command -v docker > /dev/null 2>&1; then - echo "🐳 Configuring Docker credentials..." - mkdir -p ~/.docker - %{for repo in var.package_managers.docker~} - echo -n "${var.nexus_password}" | docker login "${local.nexus_host}" --username "${local.username}" --password-stdin - %{endfor~} - config_complete - else - echo "🤔 Docker is not installed, skipping Docker configuration." - fi -else - not_configured docker -fi - -echo "✅ Nexus repository configuration completed!" -EOT + script = templatefile("${path.module}/run.sh", { + NEXUS_URL = var.nexus_url + NEXUS_HOST = local.nexus_host[1] + 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 } -output "nexus_url" { - description = "The Nexus repository URL" - value = var.nexus_url -} - -output "username" { - description = "The username used for Nexus authentication" - value = local.username -} \ No newline at end of file diff --git a/registry/mavrickrishi/modules/nexus/run.sh b/registry/mavrickrishi/modules/nexus/run.sh new file mode 100644 index 000000000..4f1d71701 --- /dev/null +++ b/registry/mavrickrishi/modules/nexus/run.sh @@ -0,0 +1,94 @@ +#!/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}" --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' + + + + + nexus + ${NEXUS_USERNAME} + ${NEXUS_PASSWORD} + + + + + nexus-mirror + * + ${NEXUS_URL}/repository/${MAVEN_REPO} + + + +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 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!" \ No newline at end of file From 09905c3a7bd03ef4026cd5123ca15deb41a8f635 Mon Sep 17 00:00:00 2001 From: Rishi Mondal Date: Wed, 30 Jul 2025 15:14:27 +0530 Subject: [PATCH 07/21] Update registry/mavrickrishi/modules/nexus/README.md Co-authored-by: Atif Ali --- registry/mavrickrishi/modules/nexus/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/mavrickrishi/modules/nexus/README.md b/registry/mavrickrishi/modules/nexus/README.md index abae236a2..eaa71fad6 100644 --- a/registry/mavrickrishi/modules/nexus/README.md +++ b/registry/mavrickrishi/modules/nexus/README.md @@ -2,7 +2,7 @@ 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 +verified: false tags: [integration, nexus, maven, npm, pypi, docker] --- From 56f59905abd78620180986636e479b6204f45501 Mon Sep 17 00:00:00 2001 From: Rishi Mondal Date: Wed, 30 Jul 2025 15:19:56 +0530 Subject: [PATCH 08/21] Update registry/mavrickrishi/modules/nexus/main.tf Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- registry/mavrickrishi/modules/nexus/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/mavrickrishi/modules/nexus/main.tf b/registry/mavrickrishi/modules/nexus/main.tf index a9b3f8d2e..912510d62 100644 --- a/registry/mavrickrishi/modules/nexus/main.tf +++ b/registry/mavrickrishi/modules/nexus/main.tf @@ -80,7 +80,7 @@ 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) + nexus_host = regex("^https?://([^:/]+)", var.nexus_url)[1] } locals { From 3c890d0e4946aed2ed48d131975f11279f297277 Mon Sep 17 00:00:00 2001 From: Rishi Mondal Date: Wed, 30 Jul 2025 15:20:05 +0530 Subject: [PATCH 09/21] Update registry/mavrickrishi/modules/nexus/main.tf Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- registry/mavrickrishi/modules/nexus/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/mavrickrishi/modules/nexus/main.tf b/registry/mavrickrishi/modules/nexus/main.tf index 912510d62..41fdad1fd 100644 --- a/registry/mavrickrishi/modules/nexus/main.tf +++ b/registry/mavrickrishi/modules/nexus/main.tf @@ -103,7 +103,7 @@ resource "coder_script" "nexus" { icon = "/icon/nexus.svg" script = templatefile("${path.module}/run.sh", { NEXUS_URL = var.nexus_url - NEXUS_HOST = local.nexus_host[1] + NEXUS_HOST = local.nexus_host NEXUS_USERNAME = local.username NEXUS_PASSWORD = var.nexus_password HAS_MAVEN = length(var.package_managers.maven) == 0 ? "" : "YES" From 88f828488f89ca744e9a260381dd00acd89caaba Mon Sep 17 00:00:00 2001 From: mavrick-1 Date: Wed, 30 Jul 2025 15:46:03 +0530 Subject: [PATCH 10/21] fix: update Nexus module configurations and README details --- registry/mavrickrishi/README.md | 2 +- registry/mavrickrishi/modules/nexus/README.md | 8 ++++---- registry/mavrickrishi/modules/nexus/main.tf | 2 +- registry/mavrickrishi/modules/nexus/run.sh | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/registry/mavrickrishi/README.md b/registry/mavrickrishi/README.md index 61a0c983e..c105b0b6f 100644 --- a/registry/mavrickrishi/README.md +++ b/registry/mavrickrishi/README.md @@ -1,7 +1,7 @@ --- display_name: mavrickrishi bio: Coder module contributor -github: MAVRICK-1 +github: mavrick-1 status: community --- diff --git a/registry/mavrickrishi/modules/nexus/README.md b/registry/mavrickrishi/modules/nexus/README.md index eaa71fad6..de7c7f372 100644 --- a/registry/mavrickrishi/modules/nexus/README.md +++ b/registry/mavrickrishi/modules/nexus/README.md @@ -1,9 +1,9 @@ --- -display_name: Sonatype Nexus Repository +display_name: Nexus Repository description: Configure package managers to use Sonatype Nexus Repository for Maven, npm, PyPI, and Docker registries. -icon: /.icons/nexus.svg -verified: false -tags: [integration, nexus, maven, npm, pypi, docker] +icon: ../../../.icons/nexus.svg +verified: true +tags: [integration, nexus-repository, maven, npm, pypi, docker] --- # Sonatype Nexus Repository diff --git a/registry/mavrickrishi/modules/nexus/main.tf b/registry/mavrickrishi/modules/nexus/main.tf index 41fdad1fd..e212ae0fb 100644 --- a/registry/mavrickrishi/modules/nexus/main.tf +++ b/registry/mavrickrishi/modules/nexus/main.tf @@ -80,7 +80,7 @@ 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)[1] + nexus_host = split("/", replace(replace(var.nexus_url, "https://", ""), "http://", ""))[0] } locals { diff --git a/registry/mavrickrishi/modules/nexus/run.sh b/registry/mavrickrishi/modules/nexus/run.sh index 4f1d71701..cf7a9ef45 100644 --- a/registry/mavrickrishi/modules/nexus/run.sh +++ b/registry/mavrickrishi/modules/nexus/run.sh @@ -11,7 +11,7 @@ config_complete() { register_docker() { repo=$1 - echo -n "${NEXUS_PASSWORD}" | docker login "${NEXUS_HOST}" --username "${NEXUS_USERNAME}" --password-stdin + echo -n "${NEXUS_PASSWORD}" | docker login "${NEXUS_HOST}/repository/$${repo}" --username "${NEXUS_USERNAME}" --password-stdin } echo "🚀 Configuring Nexus repository access..." From f375e5f55cbf52732fb80cdfb08e1bd2ef46fe38 Mon Sep 17 00:00:00 2001 From: mavrick-1 Date: Wed, 30 Jul 2025 16:12:01 +0530 Subject: [PATCH 11/21] feat: add support for Go package manager in Nexus module and update README --- registry/mavrickrishi/modules/nexus/README.md | 23 ++++++++++++++++--- registry/mavrickrishi/modules/nexus/main.tf | 17 ++++++++++++++ registry/mavrickrishi/modules/nexus/run.sh | 11 +++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/registry/mavrickrishi/modules/nexus/README.md b/registry/mavrickrishi/modules/nexus/README.md index de7c7f372..33d7ca95b 100644 --- a/registry/mavrickrishi/modules/nexus/README.md +++ b/registry/mavrickrishi/modules/nexus/README.md @@ -8,7 +8,7 @@ tags: [integration, nexus-repository, 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. +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" { @@ -20,6 +20,7 @@ module "nexus" { 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"] } @@ -30,10 +31,10 @@ module "nexus" { - Nexus Repository Manager 3.x - Valid API token or user credentials -- Package managers installed on the workspace (Maven, npm, pip, Docker as needed) +- 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, Python pip, and Docker yourself. +> 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 @@ -67,6 +68,21 @@ module "nexus" { } ``` +### Configure Go module proxy + +```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 = { + go = ["go-public", "go-private"] + } +} +``` + ### Configure Python PyPI repositories ```tf @@ -125,6 +141,7 @@ module "nexus" { 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"] } diff --git a/registry/mavrickrishi/modules/nexus/main.tf b/registry/mavrickrishi/modules/nexus/main.tf index e212ae0fb..e246a24a1 100644 --- a/registry/mavrickrishi/modules/nexus/main.tf +++ b/registry/mavrickrishi/modules/nexus/main.tf @@ -39,12 +39,14 @@ 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 = [] } @@ -52,6 +54,7 @@ variable "package_managers" { 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. @@ -59,6 +62,7 @@ variable "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"] } @@ -87,6 +91,7 @@ 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 @@ -110,6 +115,8 @@ resource "coder_script" "nexus" { 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" @@ -118,3 +125,13 @@ resource "coder_script" "nexus" { 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}" + ]) +} + diff --git a/registry/mavrickrishi/modules/nexus/run.sh b/registry/mavrickrishi/modules/nexus/run.sh index cf7a9ef45..3539374b1 100644 --- a/registry/mavrickrishi/modules/nexus/run.sh +++ b/registry/mavrickrishi/modules/nexus/run.sh @@ -55,6 +55,17 @@ 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..." From 97d144b96deaa39d469b1aab8231abd8790b2f76 Mon Sep 17 00:00:00 2001 From: mavrick-1 Date: Wed, 30 Jul 2025 16:13:08 +0530 Subject: [PATCH 12/21] feat: add test for configuring Go module proxy in Nexus module --- registry/mavrickrishi/modules/nexus/main.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/registry/mavrickrishi/modules/nexus/main.test.ts b/registry/mavrickrishi/modules/nexus/main.test.ts index f20858725..3cec881e8 100644 --- a/registry/mavrickrishi/modules/nexus/main.test.ts +++ b/registry/mavrickrishi/modules/nexus/main.test.ts @@ -94,6 +94,22 @@ describe("nexus", async () => { 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, { From 1bd0085b9a549f5e1596389fbba78c5dfdb02fad Mon Sep 17 00:00:00 2001 From: mavrick-1 Date: Wed, 30 Jul 2025 19:13:04 +0530 Subject: [PATCH 13/21] feat: Add Nexus Repository module and related configurations - Updated .github/typos.toml to exclude false positives for mavrickrishi's README.md. - Added nexus-repository.svg icon for the Nexus Repository module. - Updated README.md for mavrickrishi to reflect the new nexus-repository module. - Created README.md for the nexus-repository module with detailed configuration instructions. - Implemented main.tf for the nexus-repository module, defining necessary variables and resources. - Added main.test.ts for testing the nexus-repository module functionalities. - Created run.sh script to configure Nexus repository access for various package managers. --- .github/typos.toml | 5 +- .icons/nexus-repository.svg | 84 +++++++++++++++++++ registry/mavrickrishi/README.md | 2 +- .../{nexus => nexus-repository}/README.md | 36 ++++---- .../{nexus => nexus-repository}/main.test.ts | 2 +- .../{nexus => nexus-repository}/main.tf | 4 +- .../{nexus => nexus-repository}/run.sh | 0 7 files changed, 110 insertions(+), 23 deletions(-) create mode 100644 .icons/nexus-repository.svg rename registry/mavrickrishi/modules/{nexus => nexus-repository}/README.md (79%) rename registry/mavrickrishi/modules/{nexus => nexus-repository}/main.test.ts (99%) rename registry/mavrickrishi/modules/{nexus => nexus-repository}/main.tf (98%) rename registry/mavrickrishi/modules/{nexus => nexus-repository}/run.sh (100%) diff --git a/.github/typos.toml b/.github/typos.toml index f27257a23..4411e4a8c 100644 --- a/.github/typos.toml +++ b/.github/typos.toml @@ -4,4 +4,7 @@ Hashi = "Hashi" HashiCorp = "HashiCorp" [files] -extend-exclude = ["registry/coder/templates/aws-devcontainer/architecture.svg"] #False positive \ No newline at end of file +extend-exclude = [ + "registry/coder/templates/aws-devcontainer/architecture.svg", #False positive + "registry/mavrickrishi/README.md" # False positive - mavrickrishi is a username +] \ No newline at end of file diff --git a/.icons/nexus-repository.svg b/.icons/nexus-repository.svg new file mode 100644 index 000000000..42875c847 --- /dev/null +++ b/.icons/nexus-repository.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/registry/mavrickrishi/README.md b/registry/mavrickrishi/README.md index c105b0b6f..c240f3bcc 100644 --- a/registry/mavrickrishi/README.md +++ b/registry/mavrickrishi/README.md @@ -11,4 +11,4 @@ This directory contains Coder modules and templates created by mavrickrishi. ## Modules -- [nexus](./modules/nexus/) - Configure package managers to use Sonatype Nexus Repository +- [nexus-repository](./modules/nexus-repository/) - Configure package managers to use Sonatype Nexus Repository diff --git a/registry/mavrickrishi/modules/nexus/README.md b/registry/mavrickrishi/modules/nexus-repository/README.md similarity index 79% rename from registry/mavrickrishi/modules/nexus/README.md rename to registry/mavrickrishi/modules/nexus-repository/README.md index 33d7ca95b..6c2c1926c 100644 --- a/registry/mavrickrishi/modules/nexus/README.md +++ b/registry/mavrickrishi/modules/nexus-repository/README.md @@ -1,7 +1,7 @@ --- -display_name: Nexus Repository +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 +icon: ../../../../.icons/nexus-repository.svg verified: true tags: [integration, nexus-repository, maven, npm, pypi, docker] --- @@ -11,8 +11,8 @@ tags: [integration, nexus-repository, maven, npm, pypi, docker] 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" { - source = "registry.coder.com/mavrickrishi/nexus/coder" +module "nexus_repository" { + source = "registry.coder.com/mavrickrishi/nexus-repository/coder" version = "1.0.0" agent_id = coder_agent.example.id nexus_url = "https://nexus.example.com" @@ -41,8 +41,8 @@ module "nexus" { ### Configure Maven to use Nexus repositories ```tf -module "nexus" { - source = "registry.coder.com/mavrickrishi/nexus/coder" +module "nexus_repository" { + source = "registry.coder.com/mavrickrishi/nexus-repository/coder" version = "1.0.0" agent_id = coder_agent.example.id nexus_url = "https://nexus.example.com" @@ -56,8 +56,8 @@ module "nexus" { ### Configure npm with scoped packages ```tf -module "nexus" { - source = "registry.coder.com/mavrickrishi/nexus/coder" +module "nexus_repository" { + source = "registry.coder.com/mavrickrishi/nexus-repository/coder" version = "1.0.0" agent_id = coder_agent.example.id nexus_url = "https://nexus.example.com" @@ -71,8 +71,8 @@ module "nexus" { ### Configure Go module proxy ```tf -module "nexus" { - source = "registry.coder.com/mavrickrishi/nexus/coder" +module "nexus_repository" { + source = "registry.coder.com/mavrickrishi/nexus-repository/coder" version = "1.0.0" agent_id = coder_agent.example.id nexus_url = "https://nexus.example.com" @@ -86,8 +86,8 @@ module "nexus" { ### Configure Python PyPI repositories ```tf -module "nexus" { - source = "registry.coder.com/mavrickrishi/nexus/coder" +module "nexus_repository" { + source = "registry.coder.com/mavrickrishi/nexus-repository/coder" version = "1.0.0" agent_id = coder_agent.example.id nexus_url = "https://nexus.example.com" @@ -101,8 +101,8 @@ module "nexus" { ### Configure Docker registries ```tf -module "nexus" { - source = "registry.coder.com/mavrickrishi/nexus/coder" +module "nexus_repository" { + source = "registry.coder.com/mavrickrishi/nexus-repository/coder" version = "1.0.0" agent_id = coder_agent.example.id nexus_url = "https://nexus.example.com" @@ -116,8 +116,8 @@ module "nexus" { ### Use custom username ```tf -module "nexus" { - source = "registry.coder.com/mavrickrishi/nexus/coder" +module "nexus_repository" { + source = "registry.coder.com/mavrickrishi/nexus-repository/coder" version = "1.0.0" agent_id = coder_agent.example.id nexus_url = "https://nexus.example.com" @@ -132,8 +132,8 @@ module "nexus" { ### Complete configuration for all package managers ```tf -module "nexus" { - source = "registry.coder.com/mavrickrishi/nexus/coder" +module "nexus_repository" { + source = "registry.coder.com/mavrickrishi/nexus-repository/coder" version = "1.0.0" agent_id = coder_agent.example.id nexus_url = "https://nexus.example.com" diff --git a/registry/mavrickrishi/modules/nexus/main.test.ts b/registry/mavrickrishi/modules/nexus-repository/main.test.ts similarity index 99% rename from registry/mavrickrishi/modules/nexus/main.test.ts rename to registry/mavrickrishi/modules/nexus-repository/main.test.ts index 3cec881e8..a9e7b484d 100644 --- a/registry/mavrickrishi/modules/nexus/main.test.ts +++ b/registry/mavrickrishi/modules/nexus-repository/main.test.ts @@ -6,7 +6,7 @@ import { testRequiredVariables, } from "~test"; -describe("nexus", async () => { +describe("nexus-repository", async () => { await runTerraformInit(import.meta.dir); testRequiredVariables(import.meta.dir, { diff --git a/registry/mavrickrishi/modules/nexus/main.tf b/registry/mavrickrishi/modules/nexus-repository/main.tf similarity index 98% rename from registry/mavrickrishi/modules/nexus/main.tf rename to registry/mavrickrishi/modules/nexus-repository/main.tf index e246a24a1..be573121f 100644 --- a/registry/mavrickrishi/modules/nexus/main.tf +++ b/registry/mavrickrishi/modules/nexus-repository/main.tf @@ -104,8 +104,8 @@ EOF resource "coder_script" "nexus" { agent_id = var.agent_id - display_name = "nexus" - icon = "/icon/nexus.svg" + 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 diff --git a/registry/mavrickrishi/modules/nexus/run.sh b/registry/mavrickrishi/modules/nexus-repository/run.sh similarity index 100% rename from registry/mavrickrishi/modules/nexus/run.sh rename to registry/mavrickrishi/modules/nexus-repository/run.sh From 5534564f4ca9157af6b85a0ed2a3cc88e40a1f52 Mon Sep 17 00:00:00 2001 From: Rishi Mondal Date: Wed, 30 Jul 2025 19:16:00 +0530 Subject: [PATCH 14/21] Delete .icons/nexus.svg --- .icons/nexus.svg | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .icons/nexus.svg diff --git a/.icons/nexus.svg b/.icons/nexus.svg deleted file mode 100644 index c90645d66..000000000 --- a/.icons/nexus.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file From 0854c5e92a6e0354d79da5f61873dfd89ce7e1ab Mon Sep 17 00:00:00 2001 From: mavrick-1 Date: Wed, 30 Jul 2025 19:24:32 +0530 Subject: [PATCH 15/21] fix: remove false positive entries from typos.toml and adjust username entry --- .github/typos.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/typos.toml b/.github/typos.toml index 4411e4a8c..834f587b3 100644 --- a/.github/typos.toml +++ b/.github/typos.toml @@ -2,9 +2,7 @@ muc = "muc" # For Munich location code Hashi = "Hashi" HashiCorp = "HashiCorp" +mavrickrishi = "mavrick" # Username [files] -extend-exclude = [ - "registry/coder/templates/aws-devcontainer/architecture.svg", #False positive - "registry/mavrickrishi/README.md" # False positive - mavrickrishi is a username -] \ No newline at end of file +extend-exclude = ["registry/coder/templates/aws-devcontainer/architecture.svg"] #False positive \ No newline at end of file From c520a191741b6169032f3ecaf1a220369a92a801 Mon Sep 17 00:00:00 2001 From: mavrick-1 Date: Wed, 30 Jul 2025 19:27:33 +0530 Subject: [PATCH 16/21] fix: correct username entry for mavrickrishi in typos.toml --- .github/typos.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/typos.toml b/.github/typos.toml index 834f587b3..7d53f9bf1 100644 --- a/.github/typos.toml +++ b/.github/typos.toml @@ -2,7 +2,8 @@ muc = "muc" # For Munich location code Hashi = "Hashi" HashiCorp = "HashiCorp" -mavrickrishi = "mavrick" # Username +mavrickrishi = "mavrickrishi" # Username +mavrick = "mavrick" # Username [files] extend-exclude = ["registry/coder/templates/aws-devcontainer/architecture.svg"] #False positive \ No newline at end of file From 9f2f3a41901a924823a766082a52e5449d202084 Mon Sep 17 00:00:00 2001 From: Rishi Mondal Date: Wed, 13 Aug 2025 20:08:37 +0530 Subject: [PATCH 17/21] Update registry/mavrickrishi/modules/nexus-repository/README.md Co-authored-by: DevCats --- registry/mavrickrishi/modules/nexus-repository/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/mavrickrishi/modules/nexus-repository/README.md b/registry/mavrickrishi/modules/nexus-repository/README.md index 6c2c1926c..4694e0f8e 100644 --- a/registry/mavrickrishi/modules/nexus-repository/README.md +++ b/registry/mavrickrishi/modules/nexus-repository/README.md @@ -1,5 +1,5 @@ --- -display_name: Sonatype Nexus Repository +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: true From 3bdcac7712656802ab7972796bf54f408342dd68 Mon Sep 17 00:00:00 2001 From: mavrick-1 Date: Tue, 26 Aug 2025 22:58:31 +0530 Subject: [PATCH 18/21] chore: remove trailing newline from README.md --- registry/mavrickrishi/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/registry/mavrickrishi/README.md b/registry/mavrickrishi/README.md index b2df263a2..fbe55716a 100644 --- a/registry/mavrickrishi/README.md +++ b/registry/mavrickrishi/README.md @@ -20,4 +20,3 @@ participating in LFX CNCF programs, and helping the developer community grow. - **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 - From 013f756a8fd94829874982ac5f43b4fa5894be0c Mon Sep 17 00:00:00 2001 From: mavrick-1 Date: Wed, 8 Oct 2025 01:53:23 +0530 Subject: [PATCH 19/21] fix: ensure consistent formatting in nexus repository tests and scripts --- .../modules/nexus-repository/main.test.ts | 68 +++++++++++-------- .../modules/nexus-repository/run.sh | 2 +- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/registry/mavrickrishi/modules/nexus-repository/main.test.ts b/registry/mavrickrishi/modules/nexus-repository/main.test.ts index a9e7b484d..1d5724bb6 100644 --- a/registry/mavrickrishi/modules/nexus-repository/main.test.ts +++ b/registry/mavrickrishi/modules/nexus-repository/main.test.ts @@ -11,8 +11,8 @@ describe("nexus-repository", async () => { testRequiredVariables(import.meta.dir, { agent_id: "test-agent", - nexus_url: "https://nexus.example.com", - nexus_password: "test-password" + nexus_url: "https://nexus.example.com", + nexus_password: "test-password", }); it("configures Maven settings", async () => { @@ -21,8 +21,8 @@ describe("nexus-repository", async () => { nexus_url: "https://nexus.example.com", nexus_password: "test-token", package_managers: JSON.stringify({ - maven: ["maven-public"] - }) + maven: ["maven-public"], + }), }); const output = await executeScriptInContainer(state, "ubuntu:20.04"); @@ -33,11 +33,11 @@ describe("nexus-repository", async () => { it("configures npm registry", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "test-agent", - nexus_url: "https://nexus.example.com", + nexus_url: "https://nexus.example.com", nexus_password: "test-token", package_managers: JSON.stringify({ - npm: ["npm-public"] - }) + npm: ["npm-public"], + }), }); const output = await executeScriptInContainer(state, "ubuntu:20.04"); @@ -49,10 +49,10 @@ describe("nexus-repository", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "test-agent", nexus_url: "https://nexus.example.com", - nexus_password: "test-token", + nexus_password: "test-token", package_managers: JSON.stringify({ - pypi: ["pypi-public"] - }) + pypi: ["pypi-public"], + }), }); const output = await executeScriptInContainer(state, "ubuntu:20.04"); @@ -67,16 +67,18 @@ describe("nexus-repository", async () => { nexus_password: "test-token", package_managers: JSON.stringify({ maven: ["maven-public"], - npm: ["npm-public"], - pypi: ["pypi-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!"); + expect(output.stdout.join("\n")).toContain( + "✅ Nexus repository configuration completed!", + ); }); it("handles empty package managers", async () => { @@ -84,14 +86,22 @@ describe("nexus-repository", async () => { agent_id: "test-agent", nexus_url: "https://nexus.example.com", nexus_password: "test-token", - package_managers: JSON.stringify({}) + 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."); + 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 () => { @@ -100,13 +110,15 @@ describe("nexus-repository", async () => { nexus_url: "https://nexus.example.com", nexus_password: "test-token", package_managers: JSON.stringify({ - go: ["go-public", "go-private"] - }) + 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( + "Go proxy configured via GOPROXY environment variable", + ); expect(output.stdout.join("\n")).toContain("🥳 Configuration complete!"); }); @@ -116,20 +128,20 @@ describe("nexus-repository", async () => { agent_id: "test-agent", nexus_url: "invalid-url", nexus_password: "test-token", - package_managers: JSON.stringify({}) - }) + package_managers: JSON.stringify({}), + }), ).rejects.toThrow(); }); it("validates username_field values", async () => { await expect( runTerraformApply(import.meta.dir, { - agent_id: "test-agent", + agent_id: "test-agent", nexus_url: "https://nexus.example.com", nexus_password: "test-token", username_field: "invalid", - package_managers: JSON.stringify({}) - }) + package_managers: JSON.stringify({}), + }), ).rejects.toThrow(); }); -}); \ No newline at end of file +}); diff --git a/registry/mavrickrishi/modules/nexus-repository/run.sh b/registry/mavrickrishi/modules/nexus-repository/run.sh index 3539374b1..b860c4bc5 100644 --- a/registry/mavrickrishi/modules/nexus-repository/run.sh +++ b/registry/mavrickrishi/modules/nexus-repository/run.sh @@ -102,4 +102,4 @@ else not_configured docker fi -echo "✅ Nexus repository configuration completed!" \ No newline at end of file +echo "✅ Nexus repository configuration completed!" From cce7b7c14afe0ba2d66541164cc426c05f91113f Mon Sep 17 00:00:00 2001 From: mavrick-1 Date: Thu, 9 Oct 2025 00:34:49 +0530 Subject: [PATCH 20/21] fix: update nexus repository icon --- .icons/nexus-repository.svg | Bin 8526 -> 3508 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/.icons/nexus-repository.svg b/.icons/nexus-repository.svg index 42875c847f8d60172d44eac11bcb8c771049b203..ca17a69cf8af268ce8a49fd95b6e7da2214ab1f0 100644 GIT binary patch literal 3508 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 D^>BV& literal 8526 zcmeI2PjB4F5ykKR6wK_!KCspQn+zl`kN^R4%OUvyvMftNMzSF(V`D#key^G%E7{d( zq+|{Y*axb}CcCSvURAy7@$t{!uGhon{PynZ^UcXBIKLXsZ_Yk{yt=tKS^eYhe;KFM z&+p$H|6v@5Ki{68-k*OQzFys54u8G*=iS-qm-FH6<^BDaKOP-@{rc5iEf3x2+l!-j z!#MKg-R6QXKHgt4d#kRL>+98Ud46?qd4IA>lk=tO$s-j4cUR|M|MdCW$;uCY=${S! z-PM4?H+LtiN6UUqwqW$$AF#Hw+J4NHGk0-0`P4KhcV)%=O z?r%?T?mm6Ky*^o)xMrVk$C}*~(=e7;T%O*o1}3=9#nrhCC3qL(Wtgk;VUwea<#OzI zzpRdI;iK(hJJbl_c{P?5$D&mZq(*vPjapntGu9~4<$4%vl;_o$i<@ddjTBuChf$+F zuSW2}B~v4~sU1d*Js=ta&@_n7VD%ts#62Jy;_OU|=KO)Q*aM*oyB2u^Itq`iB&KAfM+wpcf-qDIPQ*?^}ZA`C(k-lwFQ( z2zma_G#KABYF#|@kg<927&Q#RHIiI_7Y&izuLp?uJ#*!6^?CHvq>MZTliLOZOzmu> zfX{94BOlh8x290WCB#dH=aM#KVCFpivKqwdMzO||)xU1nZ-3gdJSZhr z{dBcnpR9h0^M}l)7AE-L9P{a&FNO40Kvc(o~*a8tF*S1xyUe-ZmH;(|x*Fjr~c(}&2%L75U{!I^DY#-Ai@Nam6;63e_ z8ePIXA!DX(^q}n--3j3&_{{Jym~omgeX7EvWZ@Ag@+{ZDTWyMf%$J#w8aC{2PDvp1 zb>4t6c6-@xc%jq;+FPnZ?_8NOdfcrA2mG1hBC}xT9iEjc^hS);W#PGLl{V9#Fmc;y zKSI@dmtVIxyG&riEI@ykX~TTnuw21_z`RWv(*4 zyu>$VF#su6l}I&rshRoaHfuhD(4xCNABBcBy+leJ|Nn*wCK@a?>Ye$b|G94C|pU?EUt`2E}&+0 z8Py0WK`oOWQNco}68@Mi=3QYCsW66s4#E7;FB+jADM(Wj+PMEVJUc}l66gnh~VKUCqxfR@##zNRh(jwG_^J%D>um&C}G zVFVbGw-FyS_ZH?g!NJ8CRn`^FeA@)dDOe$f3w?i8DH=XySe89%+svZxs)u2E#ak>>auJ)O@bWbTp720grIC2ZKL85eSFI)!~pvvnGwEb!`)fe z%<)IaL=@4sHBZM}X;-!yVAG{ZGO8A)b|w#d&4pue9TKO`Jw znTI-gP3lwoKrA$pA<3aPh@DyVboc$bj1pBm<4bi+UAF68e?-ixDSH?QK^49Q@fUl@ zDxDlHGxNLEj%avQxu(MwH_Eh=k8;nZ((UrgntEPNWTsFryG*uE)+)oSw=$d`nr$yB zC+V(oYyyR6GZUK;``3mzkyazOh&oVG{t?CJB4UHZVbewc=>kh!Xr8Phn#+~LBAY^H zN=hlcy+?-L>GVknD2w>bw6$GUHm|!3_o`~z#5#W@?UyI*S5?Ty2^n?&_erGltLoIm z`2nS>5Lu7u8p!9dxCYn{|^MA0j^ z$r?>zP2uGLRRk3D$iA=WlEC2yO26L&| From 5c1982b6b2f2ada4f2dfdcb8b30fdc3be8706ecb Mon Sep 17 00:00:00 2001 From: mavrick-1 Date: Thu, 9 Oct 2025 01:18:40 +0530 Subject: [PATCH 21/21] Update nexus-repository.svg with new design changes --- .icons/nexus-repository.svg | Bin 3508 -> 27290 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/.icons/nexus-repository.svg b/.icons/nexus-repository.svg index ca17a69cf8af268ce8a49fd95b6e7da2214ab1f0..ca135cd5ebac95250d4c5674c2e34435f6b7e11c 100644 GIT binary patch literal 27290 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 D^>BV&