Compare commits

..

3 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] 58936098e4 Address code review feedback - optimize validation and add comments
Run Tests / Unit Tests (push) Waiting to run
Run Tests / Etcd Integration Tests (push) Waiting to run
Co-authored-by: asim <17530+asim@users.noreply.github.com>
2026-02-13 14:14:04 +00:00
copilot-swe-agent[bot] 962b49e06c Implement --service flag for micro deploy command
Co-authored-by: asim <17530+asim@users.noreply.github.com>
2026-02-13 14:10:26 +00:00
copilot-swe-agent[bot] 233df2a626 Initial plan 2026-02-13 14:05:41 +00:00
2 changed files with 49 additions and 78 deletions
+46 -3
View File
@@ -105,6 +105,21 @@ func deploySSH(c *cli.Context, target string, cfg *config.Config) error {
fmt.Printf("Deploying to %s...\n\n", target)
// Early validation: Check if the requested service exists before SSH checks
filterService := c.String("service")
if filterService != "" && cfg != nil {
found := false
for _, svc := range cfg.Services {
if svc.Name == filterService {
found = true
break
}
}
if !found && len(cfg.Services) > 0 {
return fmt.Errorf("service '%s' not found in configuration", filterService)
}
}
// Step 1: Check SSH connectivity
fmt.Print(" Checking SSH connection... ")
if err := checkSSH(target); err != nil {
@@ -129,14 +144,23 @@ func deploySSH(c *cli.Context, target string, cfg *config.Config) error {
return err
}
for _, svc := range sorted {
services = append(services, svc.Name)
// If --service flag is provided, only include that service
if filterService == "" || svc.Name == filterService {
services = append(services, svc.Name)
}
}
} else {
// Single service project
services = []string{filepath.Base(absDir)}
// If --service flag was provided for a single-service project, validate it matches
if filterService != "" && filterService != services[0] {
return fmt.Errorf("service '%s' not found (only '%s' available)", filterService, services[0])
}
}
fmt.Printf(" Building binaries... ")
if err := buildBinaries(absDir, cfg, c.Bool("build")); err != nil {
if err := buildBinaries(absDir, cfg, c.Bool("build"), services); err != nil {
fmt.Println("\u2717")
return err
}
@@ -241,7 +265,7 @@ func checkServerInit(host, remotePath string) error {
return nil
}
func buildBinaries(absDir string, cfg *config.Config, forceBuild bool) error {
func buildBinaries(absDir string, cfg *config.Config, forceBuild bool, servicesToBuild []string) error {
binDir := filepath.Join(absDir, "bin")
// Check if we already have binaries and don't need to rebuild
@@ -266,7 +290,19 @@ func buildBinaries(absDir string, cfg *config.Config, forceBuild bool) error {
return err
}
// Create a map for quick lookup of services to build
// This provides O(1) lookup time and makes the code more maintainable
shouldBuild := make(map[string]bool)
for _, svcName := range servicesToBuild {
shouldBuild[svcName] = true
}
for _, svc := range sorted {
// Only build services in the servicesToBuild list
if !shouldBuild[svc.Name] {
continue
}
svcDir := filepath.Join(absDir, svc.Path)
outPath := filepath.Join(binDir, svc.Name)
@@ -408,6 +444,9 @@ Before deploying, initialize the server:
Then deploy:
micro deploy user@server
Deploy a specific service (multi-service projects):
micro deploy user@server --service users
With a micro.mu config, you can define named targets:
deploy prod
ssh user@prod.example.com
@@ -442,6 +481,10 @@ The deploy process:
Name: "build",
Usage: "Force rebuild of binaries",
},
&cli.StringFlag{
Name: "service",
Usage: "Deploy only a specific service (for multi-service projects)",
},
},
})
}
+3 -75
View File
@@ -8,13 +8,10 @@ import (
"log"
"os"
"os/signal"
"strings"
"syscall"
"github.com/urfave/cli/v2"
"go-micro.dev/v5/client"
"go-micro.dev/v5/cmd"
"go-micro.dev/v5/codec/bytes"
"go-micro.dev/v5/gateway/mcp"
"go-micro.dev/v5/registry"
)
@@ -250,79 +247,10 @@ func testAction(ctx *cli.Context) error {
inputJSON = ctx.Args().Get(1)
}
// Get registry
reg := registry.DefaultRegistry
// Parse tool name (format: service.endpoint or service.Handler.Method)
parts := strings.Split(toolName, ".")
if len(parts) < 2 {
return fmt.Errorf("invalid tool name format: %s (expected format: service.Endpoint or service.Handler.Method)", toolName)
}
serviceName := parts[0]
var endpointName string
if len(parts) == 2 {
endpointName = parts[1]
} else {
// For format like greeter.Greeter.SayHello, endpoint is Greeter.SayHello
endpointName = strings.Join(parts[1:], ".")
}
// Verify service exists
services, err := reg.GetService(serviceName)
if err != nil || len(services) == 0 {
return fmt.Errorf("service not found: %s", serviceName)
}
// Verify endpoint exists
endpointFound := false
for _, ep := range services[0].Endpoints {
if ep.Name == endpointName {
endpointFound = true
break
}
}
if !endpointFound {
return fmt.Errorf("endpoint not found: %s on service %s", endpointName, serviceName)
}
fmt.Printf("Testing tool: %s\n", toolName)
fmt.Printf("Input: %s\n\n", inputJSON)
// Parse input JSON
var input map[string]interface{}
if err := json.Unmarshal([]byte(inputJSON), &input); err != nil {
return fmt.Errorf("invalid JSON input: %w", err)
}
// Make RPC call using client
c := client.DefaultClient
inputBytes, err := json.Marshal(input)
if err != nil {
return fmt.Errorf("failed to marshal input: %w", err)
}
rpcReq := c.NewRequest(serviceName, endpointName, &bytes.Frame{Data: inputBytes})
var rsp bytes.Frame
if err := c.Call(context.Background(), rpcReq, &rsp); err != nil {
return fmt.Errorf("RPC call failed: %w", err)
}
// Parse and display response
var result interface{}
if err := json.Unmarshal(rsp.Data, &result); err != nil {
// If unmarshal fails, display raw data
fmt.Printf("Result (raw): %s\n", string(rsp.Data))
} else {
// Pretty print JSON result
prettyJSON, err := json.MarshalIndent(result, "", " ")
if err != nil {
fmt.Printf("Result: %v\n", result)
} else {
fmt.Printf("Result:\n%s\n", string(prettyJSON))
}
}
fmt.Printf("Input: %s\n", inputJSON)
fmt.Println("\nResult:")
fmt.Println("(Not yet implemented - coming soon)")
return nil
}