Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -456,8 +456,8 @@ When working with this codebase, AI assistants should:
| Format Code | `make fmt` |
| Build UI | `make build-ui` |
| Dev UI | `make dev-ui` |
| Docker Up | `make docker-compose-up` |
| Docker Down | `make docker-compose-down` |
| Daemon Start | `make daemon-start` |
| Daemon Stop | `make daemon-stop` |

---

Expand Down
56 changes: 31 additions & 25 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,14 @@ dev-ui: ## Run the Next.js UI in development mode
@echo "Starting Next.js development server..."
cd ui && npm run dev

# Start local development environment (docker-compose only, no Kind)
# Start local development environment (docker backend only, no Kind)
.PHONY: run-docker
run-docker: local-registry docker-compose-up build-cli ## Start local development environment (docker-compose only, no Kind)
run-docker: local-registry docker docker-tag-as-dev daemon-start ## Start local development environment (docker backend only, no Kind)
@echo ""
@echo "agentregistry is running (docker backend):"
@echo " UI: http://localhost:12121"
@echo " API: http://localhost:12121/v0"
@echo " CLI: ./bin/arctl"
@echo " CLI: $(ARCTL)"
@echo ""
@echo "To stop: make down"

Expand All @@ -146,7 +146,7 @@ run-k8s: local-registry create-kind-cluster build-cli ## Start local development
@echo "agentregistry is running (k8s backend):"
@echo " UI: http://localhost:12121"
@echo " API: http://localhost:12121/v0"
@echo " CLI: ./bin/arctl"
@echo " CLI: $(ARCTL)"
@echo ""
@echo "To stop: make down"

Expand All @@ -156,32 +156,50 @@ run: run-k8s # Start local development environment (default: k8s)

# Stop local development environment
.PHONY: down
down: docker-compose-down delete-kind-cluster ## Stop the local development environment
down: daemon-stop delete-kind-cluster ## Stop the local development environment
@echo "agentregistry stopped"

GOTESTSUM ?= go tool gotestsum
ARCTL ?= ./bin/arctl

# Manage local daemon lifecycle via CLI helpers.
.PHONY: daemon-start
daemon-start: build-cli ## Start local daemon via CLI
$(ARCTL) daemon start

.PHONY: daemon-stop
daemon-stop: ## Stop local daemon via CLI
$(ARCTL) daemon stop

.PHONY: daemon-stop-purge
daemon-stop-purge: ## Stop local daemon and purge volumes via CLI
$(ARCTL) daemon stop --purge

# Run Go tests (unit tests only)
.PHONY: test-unit
test-unit: ## Run Go unit tests
@echo "Running Go unit tests..."
go tool gotestsum --format testdox -- -tags=unit -timeout 5m ./...
$(GOTESTSUM) --format testdox -- -tags=unit -timeout 5m ./...

# Run Go tests with integration tests
.PHONY: test
test: ## Run Go integration tests
@echo "Running Go tests with integration..."
go tool gotestsum --format testdox -- -tags=integration -timeout 10m ./...
$(GOTESTSUM) --format testdox -- -tags=integration -timeout 10m ./...

# Run e2e tests against docker backend (skips Kind cluster setup and k8s tests)
.PHONY: test-e2e-docker
test-e2e-docker: local-registry docker-compose-up build-cli
ARCTL_API_BASE_URL=http://localhost:12121/v0 E2E_BACKEND=docker GOOGLE_API_KEY=$(GOOGLE_API_KEY) OPENAI_API_KEY=$(OPENAI_API_KEY) \
go tool gotestsum --format testdox -- -v -tags=e2e -timeout 45m ./e2e/...
test-e2e-docker: local-registry docker docker-tag-as-dev daemon-start
@set -e; \
trap '$(MAKE) --no-print-directory daemon-stop-purge >/dev/null 2>&1 || true' EXIT; \
ARCTL_API_BASE_URL=http://localhost:12121/v0 E2E_BACKEND=docker GOOGLE_API_KEY=$(GOOGLE_API_KEY) OPENAI_API_KEY=$(OPENAI_API_KEY) \
$(GOTESTSUM) --format testdox -- -v -tags=e2e -timeout 45m ./e2e/...

# Run e2e tests against k8s backend (full Kind cluster setup)
.PHONY: test-e2e-k8s
test-e2e-k8s: setup-kind-cluster build-cli
ARCTL_API_BASE_URL=http://localhost:12121/v0 KIND_CLUSTER_NAME=$(KIND_CLUSTER_NAME) E2E_BACKEND=k8s GOOGLE_API_KEY=$(GOOGLE_API_KEY) OPENAI_API_KEY=$(OPENAI_API_KEY) \
go tool gotestsum --format testdox -- -v -tags=e2e -timeout 45m ./e2e/...
KIND_CLUSTER_NAME=$(KIND_CLUSTER_NAME) E2E_BACKEND=k8s GOOGLE_API_KEY=$(GOOGLE_API_KEY) OPENAI_API_KEY=$(OPENAI_API_KEY) \
$(GOTESTSUM) --format testdox -- -v -tags=e2e -timeout 45m ./e2e/...

# Run e2e tests (default: k8s)
.PHONY: test-e2e
Expand Down Expand Up @@ -275,19 +293,6 @@ docker-tag-as-dev: ## Tag and push Docker images as :dev
docker push $(DOCKER_REGISTRY)/$(DOCKER_REPO)/arctl-agentgateway:dev
@echo "✓ Docker image pulled successfully"

.PHONY: docker-compose-up
docker-compose-up: docker docker-tag-as-dev ## Start services with Docker Compose
@echo "Starting services with Docker Compose..."
VERSION=$(VERSION) DOCKER_REGISTRY=$(DOCKER_REGISTRY) docker compose -p agentregistry -f internal/daemon/docker-compose.yml up -d --wait --pull always

.PHONY: docker-compose-down
docker-compose-down: ## Stop services managed by Docker Compose
VERSION=$(VERSION) DOCKER_REGISTRY=$(DOCKER_REGISTRY) docker compose -p agentregistry -f internal/daemon/docker-compose.yml down

.PHONY: docker-compose-rm
docker-compose-rm: ## Remove Docker Compose services and volumes
VERSION=$(VERSION) DOCKER_REGISTRY=$(DOCKER_REGISTRY) docker compose -p agentregistry -f internal/daemon/docker-compose.yml rm --volumes --force

KIND_CLUSTER_NAME ?= agentregistry
KIND_IMAGE_VERSION ?= 1.34.0
KIND_CLUSTER_CONTEXT ?= kind-$(KIND_CLUSTER_NAME)
Expand Down Expand Up @@ -367,6 +372,7 @@ endif
--set database.sslMode=disable \
--set config.jwtPrivateKey="$$JWT_KEY" \
--set config.enableAnonymousAuth="true" \
--set service.type=LoadBalancer \
--wait \
--timeout=5m;

Expand Down
2 changes: 1 addition & 1 deletion docs/governance/cncf/security-self-assessment.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ The rapid growth of AI agents, MCP servers, and skills has created a fragmented

**Registry Server**: The core Go service exposing the REST API for artifact management. Stores metadata in PostgreSQL with pgvector for semantic search. Handles authentication, authorization, artifact lifecycle, and deployment orchestration.

**CLI (arctl)**: A Go-based command-line interface that communicates with the registry server over HTTP. Supports artifact discovery, publishing, deployment, and configuration of AI-powered IDEs. Manages the server daemon lifecycle on first run for local development.
**CLI (arctl)**: A Go-based command-line interface that communicates with the registry server over HTTP. Supports artifact discovery, publishing, deployment, and configuration of AI-powered IDEs. Manages local daemon lifecycle explicitly via `arctl daemon start`, `arctl daemon stop`, and `arctl daemon status`.

**Web UI**: A TypeScript/React (Next.js 14) frontend served by the registry server. Provides a visual interface for browsing, managing, and publishing artifacts. Accessible at port 12121.

Expand Down
6 changes: 3 additions & 3 deletions docs/governance/cncf/technical-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ _Developers interact primarily through the CLI for day-to-day workflows._
```
curl -fsSL https://raw.githubusercontent.com/agentregistry-dev/agentregistry/main/scripts/get-arctl | bash
```
2. **Discover** — Run `arctl mcp list` or `arctl list` to browse available artifacts from the registry. On first run, this automatically starts the registry server daemon and imports built-in seed data, requiring no separate setup step.
2. **Discover** — Run `arctl daemon start` first to start the local registry daemon, then use `arctl mcp list` or `arctl list` to browse available artifacts from the registry.
3. **Configure IDEs** — Generate ready-to-use configuration files for AI-powered IDEs with a single command:
- `arctl configure claude-desktop`
- `arctl configure cursor`
Expand Down Expand Up @@ -135,7 +135,7 @@ See [`DEVELOPMENT.md`](https://github.com/agentregistry-dev/agentregistry/blob/m
**Describe the project's architecture requirements for PoC, Development, Test, and Production environments.**
| Environment | Configuration |
|---|---|
| **PoC / Local** | Docker Compose with bundled PostgreSQL/pgvector. Single node. `arctl mcp list` auto-starts daemon. |
| **PoC / Local** | Docker Compose with bundled PostgreSQL/pgvector. Single node. Daemon lifecycle is managed explicitly with `arctl daemon start` / `arctl daemon stop`. |
| **Development** | Docker Compose or Kind (local Kubernetes). See `scripts/kind/README.md`. |
| **Test** | Kubernetes (Kind) with Helm chart and an external PostgreSQL/pgvector instance. |
| **Production** | Kubernetes cluster with Helm chart (`oci://ghcr.io/agentregistry-dev/agentregistry/charts/agentregistry`). Requires an external, HA PostgreSQL instance with pgvector extension. |
Expand Down Expand Up @@ -237,4 +237,4 @@ The following gaps have been identified and are on the projects roadmap:
| SBOM generation | No Software Bill of Materials is published with releases | Add SBOM generation (e.g., syft) to the release pipeline |
| Provenance attestation | No SLSA provenance attestation for build artifacts | Integrate SLSA provenance generation |
| Automated vulnerability scanning | No govulncheck, Trivy, or Dependabot in CI | Add govulncheck for Go and npm audit for frontend dependencies |
| Dependency update automation | No Dependabot or Renovate configuration | Add automated dependency update tooling |
| Dependency update automation | No Dependabot or Renovate configuration | Add automated dependency update tooling |
61 changes: 40 additions & 21 deletions e2e/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import (
"time"
)

const localDeployComposeProject = "agentregistry_runtime"
const (
localDeployComposeProject = "agentregistry_runtime"
deployTargetLocal = "local"
deployTargetKubernetes = "kubernetes"
)

// deployTarget describes a deployment provider used by the table-driven deploy tests.
type deployTarget struct {
Expand All @@ -32,7 +36,7 @@ type deployTarget struct {

var agentDeployTargets = []deployTarget{
{
name: "local",
name: deployTargetLocal,
verify: func(t *testing.T, agentName string) {
waitForComposeService(t, agentName, 60*time.Second)
},
Expand All @@ -41,7 +45,7 @@ var agentDeployTargets = []deployTarget{
},
},
{
name: "kubernetes",
name: deployTargetKubernetes,
deplArgs: []string{"--provider-id", "kubernetes-default", "--namespace", "default"},
verify: func(t *testing.T, agentName string) {
verifyKubernetesAgentDeploymentHealthy(t, agentName)
Expand All @@ -62,7 +66,7 @@ var agentDeployTargets = []deployTarget{

var mcpDeployTargets = []deployTarget{
{
name: "local",
name: deployTargetLocal,
verify: func(t *testing.T, _ string) {
waitForComposeService(t, "agent_gateway", 60*time.Second)
},
Expand All @@ -71,7 +75,7 @@ var mcpDeployTargets = []deployTarget{
},
},
{
name: "kubernetes",
name: deployTargetKubernetes,
deplArgs: []string{"--provider-id", "kubernetes-default", "--namespace", "default"},
cleanup: func(t *testing.T, mcpName string) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
Expand All @@ -90,12 +94,23 @@ var mcpDeployTargets = []deployTarget{
},
}

// skipUnsupportedDeployTarget skips deploy targets that do not match the active
// E2E backend mode.
func skipUnsupportedDeployTarget(t *testing.T, targetName string) {
t.Helper()
if targetName == deployTargetLocal && IsK8sBackend() {
t.Skip("skipping local deploy target: E2E_BACKEND=k8s")
}
if targetName == deployTargetKubernetes && !IsK8sBackend() {
t.Skip("skipping kubernetes deploy target: E2E_BACKEND=docker")
}
}

func TestAgentDeployCreate(t *testing.T) {
for _, target := range agentDeployTargets {
t.Run(target.name, func(t *testing.T) {
if target.name == "kubernetes" && !IsK8sBackend() {
t.Skip("skipping kubernetes deploy target: E2E_BACKEND=docker")
}
skipUnsupportedDeployTarget(t, target.name)

regURL := RegistryURL(t)
tmpDir := t.TempDir()
agentName := UniqueAgentName("e2edpl" + target.name[:3])
Expand All @@ -120,7 +135,7 @@ func TestAgentDeployCreate(t *testing.T) {
result = RunArctl(t, tmpDir, "agent", "build", agentName,
"--image", agentImage)
RequireSuccess(t, result)
if target.name == "kubernetes" {
if target.name == deployTargetKubernetes {
t.Log("Loading image into Kind cluster...")
loadDockerImageToKind(t, agentImage)
}
Expand Down Expand Up @@ -165,9 +180,8 @@ func TestAgentDeployCreate(t *testing.T) {
func TestMCPDeployCreate(t *testing.T) {
for _, target := range mcpDeployTargets {
t.Run(target.name, func(t *testing.T) {
if target.name == "kubernetes" && !IsK8sBackend() {
t.Skip("skipping kubernetes deploy target: E2E_BACKEND=docker")
}
skipUnsupportedDeployTarget(t, target.name)

regURL := RegistryURL(t)
tmpDir := t.TempDir()
mcpName := UniqueNameWithPrefix("e2e-dpl-" + target.name[:3])
Expand Down Expand Up @@ -200,7 +214,7 @@ func TestMCPDeployCreate(t *testing.T) {
result = RunArctl(t, tmpDir, "mcp", "build", mcpDir,
"--image", defaultImage)
RequireSuccess(t, result)
if target.name == "kubernetes" {
if target.name == deployTargetKubernetes {
t.Log("Loading image into Kind cluster...")
loadDockerImageToKind(t, defaultImage)
}
Expand Down Expand Up @@ -246,11 +260,15 @@ func TestMCPDeployCreate(t *testing.T) {
}
}

// TestDeployDeleteLifecycle exercises the full CLI lifecycle:
// TestDeployDeleteLifecycleLocal exercises the local-provider CLI lifecycle:
// create a deployment, extract its ID via "deploy list", delete it via
// "arctl deploy delete <prefix>" (testing ID-prefix resolution), and verify
// it no longer appears in "deploy list".
func TestDeployDeleteLifecycle(t *testing.T) {
func TestDeployDeleteLifecycleLocal(t *testing.T) {
if IsK8sBackend() {
t.Skip("skipping local deploy lifecycle test: E2E_BACKEND=k8s")
}

regURL := RegistryURL(t)
tmpDir := t.TempDir()
agentName := UniqueAgentName("e2edeldpl")
Expand Down Expand Up @@ -926,9 +944,8 @@ func dumpKagentAgentDebug(t *testing.T, kubeContext, namespace, deploymentID str
func TestAgentDeployWithPrompts(t *testing.T) {
for _, target := range agentDeployTargets {
t.Run(target.name, func(t *testing.T) {
if target.name == "kubernetes" && !IsK8sBackend() {
t.Skip("skipping kubernetes deploy target: E2E_BACKEND=docker")
}
skipUnsupportedDeployTarget(t, target.name)

regURL := RegistryURL(t)
tmpDir := t.TempDir()
agentName := UniqueAgentName("e2eprm" + target.name[:3])
Expand Down Expand Up @@ -990,7 +1007,7 @@ func TestAgentDeployWithPrompts(t *testing.T) {
result := RunArctl(t, tmpDir, "agent", "build", agentName,
"--image", agentImage)
RequireSuccess(t, result)
if target.name == "kubernetes" {
if target.name == deployTargetKubernetes {
loadDockerImageToKind(t, agentImage)
}
})
Expand Down Expand Up @@ -1023,10 +1040,12 @@ func TestAgentDeployWithPrompts(t *testing.T) {

t.Run("verify_prompts", func(t *testing.T) {
switch target.name {
case "kubernetes":
case deployTargetKubernetes:
verifyKubernetesPromptsConfig(t, agentName, promptContent)
case "local":
case deployTargetLocal:
verifyLocalPromptsConfig(t, agentName, promptContent)
default:
t.Fatalf("unsupported deploy target: %s", target.name)
}
})
})
Expand Down
Loading
Loading