Skip to content
Open
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
15 changes: 13 additions & 2 deletions internal/cli/agent/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"strings"

cliCommon "github.com/agentregistry-dev/agentregistry/internal/cli/common"
cliUtils "github.com/agentregistry-dev/agentregistry/internal/cli/utils"
"github.com/agentregistry-dev/agentregistry/pkg/models"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -34,6 +35,7 @@ func runDeploy(cmd *cobra.Command, args []string) error {
providerID, _ := cmd.Flags().GetString("provider-id")
namespace, _ := cmd.Flags().GetString("namespace")
envFlags, _ := cmd.Flags().GetStringArray("env")
wait, _ := cmd.Flags().GetBool("wait")

if version == "" {
version = "latest"
Expand Down Expand Up @@ -81,7 +83,7 @@ func runDeploy(cmd *cobra.Command, args []string) error {
if providerID == "local" {
return deployLocal(name, version, config, providerID)
}
return deployToProvider(name, version, config, namespace, providerID)
return deployToProvider(name, version, config, namespace, providerID, wait)
}

// buildDeployConfig creates the configuration map with all necessary environment variables.
Expand Down Expand Up @@ -127,7 +129,7 @@ func deployLocal(name, version string, config map[string]string, providerID stri
}

// deployToProvider deploys an agent to a non-local provider.
func deployToProvider(name, version string, config map[string]string, namespace string, providerID string) error {
func deployToProvider(name, version string, config map[string]string, namespace string, providerID string, wait bool) error {
deployment, err := apiClient.DeployAgent(name, version, config, providerID)
if err != nil {
return fmt.Errorf("failed to deploy agent: %w", err)
Expand All @@ -137,6 +139,14 @@ func deployToProvider(name, version string, config map[string]string, namespace
if ns == "" {
ns = "(default)"
}

if wait {
fmt.Printf("Waiting for agent '%s' to become ready...\n", deployment.ServerName)
if err := cliCommon.WaitForDeploymentReady(apiClient, deployment.ID); err != nil {
return err
}
}

fmt.Printf("Agent '%s' version '%s' deployed to providerId=%s in namespace '%s'\n", deployment.ServerName, deployment.Version, providerID, ns)
return nil
}
Expand All @@ -146,5 +156,6 @@ func init() {
DeployCmd.Flags().String("provider-id", "", "Deployment target provider ID (defaults to local when omitted)")
DeployCmd.Flags().Bool("prefer-remote", false, "Prefer using a remote source when available")
DeployCmd.Flags().String("namespace", "", "Kubernetes namespace for agent deployment (defaults to current kubeconfig context)")
DeployCmd.Flags().Bool("wait", true, "Wait for the deployment to become ready before returning")
DeployCmd.Flags().StringArrayP("env", "e", []string{}, "Environment variables to set on the deployed agent (KEY=VALUE)")
}
45 changes: 45 additions & 0 deletions internal/cli/common/wait.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package common

import (
"fmt"
"time"

"github.com/agentregistry-dev/agentregistry/internal/client"
)

const (
defaultWaitTimeout = 5 * time.Minute
defaultPollInterval = 2 * time.Second
)

// WaitForDeploymentReady polls a deployment until it reaches a terminal state
// (deployed or failed). Returns an error if the deployment fails or the timeout
// is exceeded.
func WaitForDeploymentReady(c *client.Client, deploymentID string) error {
deadline := time.Now().Add(defaultWaitTimeout)

for {
dep, err := c.GetDeploymentByID(deploymentID)
if err != nil {
return fmt.Errorf("polling deployment status: %w", err)
}
if dep == nil {
return fmt.Errorf("deployment %s not found", deploymentID)
}

switch dep.Status {
case "deployed":
return nil
case "failed":
return fmt.Errorf("deployment failed")
case "cancelled":
return fmt.Errorf("deployment was cancelled")
}

if time.Now().After(deadline) {
return fmt.Errorf("timed out waiting for deployment to become ready (current status: %s)", dep.Status)
}

time.Sleep(defaultPollInterval)
}
}
9 changes: 9 additions & 0 deletions internal/cli/mcp/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var (
deployYes bool
deployProviderID string
deployNamespace string
deployWait bool
)

var DeployCmd = &cobra.Command{
Expand All @@ -38,6 +39,7 @@ func init() {
DeployCmd.Flags().BoolVarP(&deployYes, "yes", "y", false, "Automatically accept all prompts (use default/latest version)")
DeployCmd.Flags().StringVar(&deployProviderID, "provider-id", "", "Deployment target provider ID (defaults to local when omitted)")
DeployCmd.Flags().StringVar(&deployNamespace, "namespace", "", "Kubernetes namespace for deployment (if provider targets Kubernetes)")
DeployCmd.Flags().BoolVar(&deployWait, "wait", true, "Wait for the deployment to become ready before returning")
}

func runDeploy(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -93,6 +95,13 @@ func runDeploy(cmd *cobra.Command, args []string) error {
return fmt.Errorf("failed to deploy server: %w", err)
}

if deployProviderID != "local" && deployWait {
fmt.Printf("Waiting for server '%s' to become ready...\n", deployment.ServerName)
if err := common.WaitForDeploymentReady(apiClient, deployment.ID); err != nil {
return err
}
}

fmt.Printf("\n✓ Deployed %s (%s) with providerId=%s\n", deployment.ServerName, common.FormatVersionForDisplay(deployment.Version), deployProviderID)
if deployNamespace != "" {
ns := deployNamespace
Expand Down
Loading