diff --git a/cli/azd/cmd/middleware/middleware.go b/cli/azd/cmd/middleware/middleware.go index ab413c50052..bd54052e71f 100644 --- a/cli/azd/cmd/middleware/middleware.go +++ b/cli/azd/cmd/middleware/middleware.go @@ -10,6 +10,7 @@ import ( "log" "slices" + "github.com/AlecAivazis/survey/v2/terminal" "github.com/azure/azure-dev/cli/azd/cmd/actions" "github.com/azure/azure-dev/cli/azd/pkg/ioc" "github.com/spf13/pflag" @@ -99,6 +100,11 @@ func (r *MiddlewareRunner) RunAction( var middleware Middleware if err := actionContainer.ResolveNamed(middlewareName, &middleware); err != nil { + // Check if the error is an interrupt error (user cancelled a prompt) + // In this case, we should not continue and return the error immediately + if errors.Is(err, terminal.InterruptErr) { + return nil, err + } log.Printf("failed resolving middleware '%s' : %v\n", middlewareName, err) } diff --git a/cli/azd/cmd/middleware/middleware_test.go b/cli/azd/cmd/middleware/middleware_test.go index cc7da658414..009dafc7df5 100644 --- a/cli/azd/cmd/middleware/middleware_test.go +++ b/cli/azd/cmd/middleware/middleware_test.go @@ -8,6 +8,7 @@ import ( "fmt" "testing" + "github.com/AlecAivazis/survey/v2/terminal" "github.com/azure/azure-dev/cli/azd/cmd/actions" "github.com/azure/azure-dev/cli/azd/test/mocks" "github.com/stretchr/testify/require" @@ -161,6 +162,41 @@ func Test_Middleware_RunAction(t *testing.T) { require.Nil(t, result) require.NoError(t, err) }) + + // Test that interrupt errors during middleware resolution are propagated and not ignored + t.Run("interrupt error during middleware resolution", func(t *testing.T) { + mockContext := mocks.NewMockContext(context.Background()) + middlewareRunner := NewMiddlewareRunner(mockContext.Container) + + // Register a middleware constructor that returns an interrupt error + // This simulates what happens when a user cancels a prompt during dependency initialization + err := middlewareRunner.Use("interrupt-middleware", func() (Middleware, error) { + return nil, terminal.InterruptErr + }) + require.NoError(t, err) + + // Register an action that should not run if interrupt occurs + actionRan := false + err = mockContext.Container.RegisterNamedTransient("test-action", func() actions.Action { + return &testAction{ + runFunc: func(ctx context.Context) (*actions.ActionResult, error) { + actionRan = true + return &actions.ActionResult{ + Message: &actions.ResultMessage{Header: "Action"}, + }, nil + }, + } + }) + require.NoError(t, err) + + result, err := middlewareRunner.RunAction(*mockContext.Context, &Options{Name: "test"}, "test-action") + + // Verify that the interrupt error is returned and the action does not run + require.Nil(t, result) + require.Error(t, err) + require.ErrorIs(t, err, terminal.InterruptErr) + require.False(t, actionRan) + }) } func registerAction(t *testing.T, mockContext *mocks.MockContext, name string, runLog *[]string) *bool { diff --git a/cli/azd/extensions/azure.ai.finetune/go.mod b/cli/azd/extensions/azure.ai.finetune/go.mod index 245759a75d1..ab3a8b87aec 100644 --- a/cli/azd/extensions/azure.ai.finetune/go.mod +++ b/cli/azd/extensions/azure.ai.finetune/go.mod @@ -10,7 +10,9 @@ require ( github.com/braydonk/yaml v0.9.0 github.com/fatih/color v1.18.0 github.com/openai/openai-go/v3 v3.2.0 + github.com/sethvargo/go-retry v0.3.0 github.com/spf13/cobra v1.10.1 + gopkg.in/yaml.v3 v3.0.1 ) require ( @@ -61,7 +63,6 @@ require ( github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/sethvargo/go-retry v0.3.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/testify v1.11.1 // indirect github.com/theckman/yacspin v0.13.12 // indirect @@ -86,5 +87,4 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20251007200510-49b9836ed3ff // indirect google.golang.org/grpc v1.76.0 // indirect google.golang.org/protobuf v1.36.10 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect )