diff --git a/src/cmd/cli/command/compose.go b/src/cmd/cli/command/compose.go index abbb9054d..132cef803 100644 --- a/src/cmd/cli/command/compose.go +++ b/src/cmd/cli/command/compose.go @@ -61,6 +61,11 @@ func makeComposeUpCmd() *cobra.Command { return loadErr } + // Don't trigger the debugger if the operation was canceled by the user (e.g., Ctrl-C) + if errors.Is(ctx.Err(), context.Canceled) { + return loadErr + } + term.Error("Cannot load project:", loadErr) project, err := loader.CreateProjectForDebug() if err != nil { @@ -224,12 +229,22 @@ func handleComposeUpErr(ctx context.Context, err error, project *compose.Project return err } + // Don't trigger the debugger if the operation was canceled by the user (e.g., Ctrl-C) + if errors.Is(ctx.Err(), context.Canceled) { + return err + } + term.Error("Error:", cliClient.PrettyError(err)) track.Evt("Debug Prompted", P("composeErr", err)) return cli.InteractiveDebugForClientError(ctx, global.Client, project, err) } func handleTailAndMonitorErr(ctx context.Context, err error, client *cliClient.GrpcClient, debugConfig cli.DebugConfig) { + // Don't trigger the debugger if the operation was canceled by the user (e.g., Ctrl-C) + if errors.Is(ctx.Err(), context.Canceled) { + return + } + var errDeploymentFailed cliClient.ErrDeploymentFailed if errors.As(err, &errDeploymentFailed) { // Tail got canceled because of deployment failure: prompt to show the debugger @@ -442,6 +457,11 @@ func makeComposeConfigCmd() *cobra.Command { return loadErr } + // Don't trigger the debugger if the operation was canceled by the user (e.g., Ctrl-C) + if errors.Is(ctx.Err(), context.Canceled) { + return loadErr + } + term.Error("Cannot load project:", loadErr) project, err := loader.CreateProjectForDebug() if err != nil { diff --git a/src/cmd/cli/command/compose_test.go b/src/cmd/cli/command/compose_test.go index 8568c3ec1..1ff61da16 100644 --- a/src/cmd/cli/command/compose_test.go +++ b/src/cmd/cli/command/compose_test.go @@ -1,7 +1,13 @@ package command import ( + "context" + "errors" "testing" + + "github.com/DefangLabs/defang/src/pkg/cli" + cliClient "github.com/DefangLabs/defang/src/pkg/cli/client" + "github.com/DefangLabs/defang/src/pkg/cli/compose" ) func TestInitializeTailCmd(t *testing.T) { @@ -14,3 +20,68 @@ func TestInitializeTailCmd(t *testing.T) { } }) } + +func TestHandleTailAndMonitorErr_ContextCanceled(t *testing.T) { + // Create a canceled context to simulate Ctrl-C + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + // Create a deployment failure error + err := cliClient.ErrDeploymentFailed{ + Service: "test-service", + Message: "deployment failed", + } + + // Create a debug config + debugConfig := cli.DebugConfig{ + Deployment: "test-deployment", + Project: &compose.Project{Name: "test-project"}, + } + + // This should not panic or prompt for debugging + handleTailAndMonitorErr(ctx, err, nil, debugConfig) +} + +func TestHandleTailAndMonitorErr_NoContextCancellation(t *testing.T) { + // Create a normal context (not canceled) + ctx := context.Background() + + // Create a deployment failure error + err := cliClient.ErrDeploymentFailed{ + Service: "test-service", + Message: "deployment failed", + } + + // Create a debug config + debugConfig := cli.DebugConfig{ + Deployment: "test-deployment", + Project: &compose.Project{Name: "test-project"}, + } + + // Set NonInteractive to true to avoid actually prompting for debugging + oldNonInteractive := global.NonInteractive + global.NonInteractive = true + defer func() { global.NonInteractive = oldNonInteractive }() + + // This should print a hint but not prompt for interactive debugging + handleTailAndMonitorErr(ctx, err, nil, debugConfig) +} + +func TestHandleComposeUpErr_ContextCanceled(t *testing.T) { + // Create a canceled context to simulate Ctrl-C + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + // Create a generic error + err := errors.New("some error during compose up") + + // Create a test project + project := &compose.Project{Name: "test-project"} + + // This should return the error without prompting for debugging + result := handleComposeUpErr(ctx, err, project, nil) + + if result != err { + t.Errorf("Expected error to be returned as-is, got: %v", result) + } +}