From 70752aec53d82b84c2e7f40aafaee29be20025dd Mon Sep 17 00:00:00 2001 From: Wallace Breza Date: Tue, 14 Apr 2026 12:14:48 -0700 Subject: [PATCH] feat: add telemetry instrumentation for hook executions Add four usage attributes to every hook execution in HooksRunner.execHook(): - hooks.event: lifecycle event name (e.g., preprovision, postdeploy) - hooks.level: command or service scope - hooks.kind: executor type (sh, pwsh, python, js, ts, dotnet) - hooks.result: success, error, or invalid (validation failure) This covers all execution paths uniformly: middleware (azd up/provision/deploy), azd hooks run, service lifecycle events, and layer provisioning hooks. HookLevel is a public field on HooksRunner (defaults to HookLevelCommand). Service-level callers set it to HookLevelService after construction. Uses AppendUsageAttributeUnique to accumulate attributes when multiple hooks fire per command. Resolves #7720 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- cli/azd/cmd/hooks.go | 19 ++- cli/azd/cmd/middleware/hooks.go | 4 +- cli/azd/internal/cmd/provision.go | 5 +- cli/azd/internal/tracing/events/events.go | 6 + cli/azd/internal/tracing/fields/fields.go | 6 + cli/azd/pkg/ext/hooks_runner.go | 69 ++++++-- cli/azd/pkg/ext/hooks_runner_test.go | 187 +++++++++++++++++++--- cli/azd/pkg/ext/js_hooks_e2e_test.go | 32 ++-- cli/azd/pkg/ext/python_hooks_e2e_test.go | 30 ++-- cli/azd/pkg/ext/ts_hooks_e2e_test.go | 30 ++-- 10 files changed, 300 insertions(+), 88 deletions(-) diff --git a/cli/azd/cmd/hooks.go b/cli/azd/cmd/hooks.go index 880b2376e00..682199260f6 100644 --- a/cli/azd/cmd/hooks.go +++ b/cli/azd/cmd/hooks.go @@ -115,7 +115,7 @@ func newHooksRunAction( type hookContextType string const ( - hookContextProject hookContextType = "command" + hookContextProject hookContextType = "project" hookContextLayer hookContextType = "layer" hookContextService hookContextType = "service" ) @@ -301,7 +301,7 @@ func (hra *hooksRunAction) processHooks( hra.console.Message(ctx, output.WithBold("%s hook %d/%d:", contextType, idx+1, len(hooks))) - err := hra.execHook(ctx, cwd, hookType, commandName, hook) + err := hra.execHook(ctx, cwd, hookType, commandName, hook, contextType) if err != nil { return fmt.Errorf("failed running hook %s, %w", hookName, err) } @@ -322,11 +322,12 @@ func (hra *hooksRunAction) processHooks( func (hra *hooksRunAction) execHook( ctx context.Context, cwd string, - hookType ext.HookType, + ht ext.HookType, commandName string, hook *ext.HookConfig, + contextType hookContextType, ) error { - hookName := string(hookType) + commandName + hookName := string(ht) + commandName hooksMap := map[string][]*ext.HookConfig{ hookName: {hook}, @@ -338,12 +339,20 @@ func (hra *hooksRunAction) execHook( hooksRunner := ext.NewHooksRunner( hooksManager, hra.commandRunner, hra.envManager, hra.console, cwd, hooksMap, hra.env, hra.serviceLocator) + hookType := "project" + switch contextType { + case hookContextLayer: + hookType = "layer" + case hookContextService: + hookType = "service" + } + // Always run in interactive mode for 'azd hooks run', to help with testing/debugging runOptions := &tools.ExecutionContext{ Interactive: new(true), } - err := hooksRunner.RunHooks(ctx, hookType, runOptions, commandName) + err := hooksRunner.RunHooks(ctx, ht, hookType, runOptions, commandName) if err != nil { return err } diff --git a/cli/azd/cmd/middleware/hooks.go b/cli/azd/cmd/middleware/hooks.go index f31ea8626b2..6c4ed532b8b 100644 --- a/cli/azd/cmd/middleware/hooks.go +++ b/cli/azd/cmd/middleware/hooks.go @@ -103,7 +103,7 @@ func (m *HooksMiddleware) registerCommandHooks( commandNames := []string{m.options.CommandPath} commandNames = append(commandNames, m.options.Aliases...) - err := hooksRunner.Invoke(ctx, commandNames, func() error { + err := hooksRunner.Invoke(ctx, commandNames, "project", func() error { result, err := next(ctx) if err != nil { return err @@ -198,7 +198,7 @@ func (m *HooksMiddleware) createServiceEventHandler( hooksRunner *ext.HooksRunner, ) ext.EventHandlerFn[project.ServiceLifecycleEventArgs] { return func(ctx context.Context, eventArgs project.ServiceLifecycleEventArgs) error { - return hooksRunner.RunHooks(ctx, hookType, nil, hookName) + return hooksRunner.RunHooks(ctx, hookType, "service", nil, hookName) } } diff --git a/cli/azd/internal/cmd/provision.go b/cli/azd/internal/cmd/provision.go index e646e2c4c27..1b948c1f947 100644 --- a/cli/azd/internal/cmd/provision.go +++ b/cli/azd/internal/cmd/provision.go @@ -551,7 +551,10 @@ func (p *ProvisionAction) runLayerProvisionWithHooks( p.validateAndWarnLayerHooks(ctx, hooksManager, layer.Hooks) - if err := hooksRunner.Invoke(ctx, []string{string(project.ProjectEventProvision)}, actionFn); err != nil { + err := hooksRunner.Invoke( + ctx, []string{string(project.ProjectEventProvision)}, "layer", actionFn, + ) + if err != nil { if layer.Name == "" { return err } diff --git a/cli/azd/internal/tracing/events/events.go b/cli/azd/internal/tracing/events/events.go index 23879cf3e28..4870b670635 100644 --- a/cli/azd/internal/tracing/events/events.go +++ b/cli/azd/internal/tracing/events/events.go @@ -46,6 +46,12 @@ const ( PreflightValidationEvent = "validation.preflight" ) +// Hook execution events. +const ( + // HooksExecEvent tracks the execution of a lifecycle hook. + HooksExecEvent = "hooks.exec" +) + // AKS service target events. const ( // AksPostprovisionSkipEvent tracks when the AKS postprovision hook diff --git a/cli/azd/internal/tracing/fields/fields.go b/cli/azd/internal/tracing/fields/fields.go index f3817711ee1..41e21340c5c 100644 --- a/cli/azd/internal/tracing/fields/fields.go +++ b/cli/azd/internal/tracing/fields/fields.go @@ -351,6 +351,12 @@ var ( Classification: SystemMetadata, Purpose: FeatureInsight, } + // The executor kind used to run the hook (e.g., "sh", "pwsh", "python", "js", "ts", "dotnet"). + HooksKindKey = AttributeKey{ + Key: attribute.Key("hooks.kind"), + Classification: SystemMetadata, + Purpose: FeatureInsight, + } ) // Pipeline command related fields diff --git a/cli/azd/pkg/ext/hooks_runner.go b/cli/azd/pkg/ext/hooks_runner.go index c100c89e32d..2e91da458d7 100644 --- a/cli/azd/pkg/ext/hooks_runner.go +++ b/cli/azd/pkg/ext/hooks_runner.go @@ -10,6 +10,9 @@ import ( "os" "strings" + "github.com/azure/azure-dev/cli/azd/internal/tracing" + "github.com/azure/azure-dev/cli/azd/internal/tracing/events" + "github.com/azure/azure-dev/cli/azd/internal/tracing/fields" "github.com/azure/azure-dev/cli/azd/pkg/environment" "github.com/azure/azure-dev/cli/azd/pkg/errorhandler" "github.com/azure/azure-dev/cli/azd/pkg/exec" @@ -18,6 +21,7 @@ import ( "github.com/azure/azure-dev/cli/azd/pkg/keyvault" "github.com/azure/azure-dev/cli/azd/pkg/output" "github.com/azure/azure-dev/cli/azd/pkg/tools" + "go.opentelemetry.io/otel/codes" ) // HooksRunner enables support to invoke lifecycle hooks before & after @@ -33,8 +37,8 @@ type HooksRunner struct { serviceLocator ioc.ServiceLocator } -// NewHooks creates a new instance of CommandHooks -// When `cwd` is empty defaults to current shell working directory +// NewHooksRunner creates a new instance of HooksRunner. +// When `cwd` is empty defaults to current shell working directory. func NewHooksRunner( hooksManager *HooksManager, commandRunner exec.CommandRunner, @@ -66,9 +70,12 @@ func NewHooksRunner( } } -// Invokes an action run runs any registered pre or post script hooks for the specified command. -func (h *HooksRunner) Invoke(ctx context.Context, commands []string, actionFn InvokeFn) error { - err := h.RunHooks(ctx, HookTypePre, nil, commands...) +// Invoke runs any registered pre and post script hooks around the specified action. +// The hookType parameter identifies the hook scope for telemetry (project, layer, or service). +func (h *HooksRunner) Invoke( + ctx context.Context, commands []string, hookType string, actionFn InvokeFn, +) error { + err := h.RunHooks(ctx, HookTypePre, hookType, nil, commands...) if err != nil { return fmt.Errorf("failed running pre hooks: %w", err) } @@ -78,7 +85,7 @@ func (h *HooksRunner) Invoke(ctx context.Context, commands []string, actionFn In return err } - err = h.RunHooks(ctx, HookTypePost, nil, commands...) + err = h.RunHooks(ctx, HookTypePost, hookType, nil, commands...) if err != nil { return fmt.Errorf("failed running post hooks: %w", err) } @@ -86,14 +93,16 @@ func (h *HooksRunner) Invoke(ctx context.Context, commands []string, actionFn In return nil } -// Invokes any registered script hooks for the specified hook type and command. +// RunHooks invokes any registered script hooks for the specified hook type and command. +// The hookType parameter identifies the hook scope for telemetry (project, layer, or service). func (h *HooksRunner) RunHooks( ctx context.Context, - hookType HookType, + ht HookType, + hookType string, options *tools.ExecutionContext, commands ...string, ) error { - hooks, err := h.hooksManager.GetByParams(h.hooks, hookType, commands...) + hooks, err := h.hooksManager.GetByParams(h.hooks, ht, commands...) if err != nil { return fmt.Errorf("failed running scripts for hooks '%s', %w", strings.Join(commands, ","), err) } @@ -103,7 +112,7 @@ func (h *HooksRunner) RunHooks( return fmt.Errorf("reloading environment before running hook: %w", err) } - err := h.execHook(ctx, hookConfig, options) + err := h.execHook(ctx, hookConfig, hookType, options) if err != nil { return err } @@ -117,8 +126,24 @@ func (h *HooksRunner) RunHooks( } func (h *HooksRunner) execHook( - ctx context.Context, hookConfig *HookConfig, options *tools.ExecutionContext, -) error { + ctx context.Context, hookConfig *HookConfig, hookType string, options *tools.ExecutionContext, +) (hookErr error) { + // Create a child span for this hook execution so each hook + // gets its own correlated set of telemetry attributes. + ctx, span := tracing.Start(ctx, events.HooksExecEvent) + statusCode := "" + defer func() { + if hookErr != nil { + if statusCode == "" { + statusCode = "hook.failed" + } + span.SetStatus(codes.Error, statusCode) + } else { + span.SetStatus(codes.Ok, "") + } + span.End() + }() + if options == nil { options = &tools.ExecutionContext{} } @@ -143,16 +168,27 @@ func (h *HooksRunner) execHook( return nil }) if err != nil { + statusCode = "hook.secrets_failed" return err } } + // Set name and type on span — known before validation. + span.SetAttributes( + fields.HooksNameKey.String(hookConfig.Name), + fields.HooksTypeKey.String(hookType), + ) + // validate() resolves the hook's kind, path, shell type, // and computes resolvedDir / resolvedScriptPath. if err := hookConfig.validate(); err != nil { + statusCode = "hook.validation_failed" return err } + span.SetAttributes( + fields.HooksKindKey.String(string(hookConfig.Kind))) + // Use pre-resolved paths from validate(). cwd := hookConfig.resolvedDir if cwd == "" { @@ -192,6 +228,7 @@ func (h *HooksRunner) execHook( // Resolve executor via IoC — hooks runner has NO knowledge of executor internals. var executor tools.HookExecutor if err := h.serviceLocator.ResolveNamed(string(hookConfig.Kind), &executor); err != nil { + statusCode = "hook.executor_not_found" return &errorhandler.ErrorWithSuggestion{ Err: fmt.Errorf( "no executor for kind '%s': %w", @@ -230,6 +267,7 @@ func (h *HooksRunner) execHook( ) if err := executor.Prepare(ctx, scriptPath, execCtx); err != nil { + statusCode = "hook.prepare_failed" return fmt.Errorf("preparing hook '%s': %w", hookConfig.Name, err) } @@ -246,11 +284,12 @@ func (h *HooksRunner) execHook( res, err := executor.Execute(ctx, scriptPath, execCtx) if err != nil { - hookErr := h.handleHookError( + execErr := h.handleHookError( ctx, hookConfig, res, scriptPath, err, ) - if hookErr != nil { - return hookErr + if execErr != nil { + statusCode = "hook.execution_failed" + return execErr } } diff --git a/cli/azd/pkg/ext/hooks_runner_test.go b/cli/azd/pkg/ext/hooks_runner_test.go index 510c9c10144..e492b17a7cc 100644 --- a/cli/azd/pkg/ext/hooks_runner_test.go +++ b/cli/azd/pkg/ext/hooks_runner_test.go @@ -105,7 +105,7 @@ func Test_Hooks_Execute(t *testing.T) { env, mockContext.Container, ) - err := runner.RunHooks(*mockContext.Context, HookTypePre, nil, "command") + err := runner.RunHooks(*mockContext.Context, HookTypePre, "project", nil, "command") require.True(t, ranPreHook) require.False(t, ranPostHook) @@ -143,7 +143,7 @@ func Test_Hooks_Execute(t *testing.T) { env, mockContext.Container, ) - err := runner.RunHooks(*mockContext.Context, HookTypePost, nil, "command") + err := runner.RunHooks(*mockContext.Context, HookTypePost, "project", nil, "command") require.False(t, ranPreHook) require.True(t, ranPostHook) @@ -181,7 +181,7 @@ func Test_Hooks_Execute(t *testing.T) { env, mockContext.Container, ) - err := runner.RunHooks(*mockContext.Context, HookTypePre, nil, "interactive") + err := runner.RunHooks(*mockContext.Context, HookTypePre, "project", nil, "interactive") require.False(t, ranPreHook) require.True(t, ranPostHook) @@ -213,7 +213,7 @@ func Test_Hooks_Execute(t *testing.T) { env, mockContext.Container, ) - err := runner.RunHooks(*mockContext.Context, HookTypePre, nil, "inline") + err := runner.RunHooks(*mockContext.Context, HookTypePre, "project", nil, "inline") require.False(t, ranPreHook) require.True(t, ranPostHook) @@ -264,7 +264,7 @@ func Test_Hooks_Execute(t *testing.T) { env, mockContext.Container, ) - err := runner.Invoke(*mockContext.Context, []string{"command"}, func() error { + err := runner.Invoke(*mockContext.Context, []string{"command"}, "project", func() error { ranAction = true hookLog = append(hookLog, "action") return nil @@ -337,7 +337,7 @@ func Test_Hooks_Validation(t *testing.T) { mockContext.Console, cwd, hooksMap, env, mockContext.Container, ) - err := runner.RunHooks(*mockContext.Context, HookTypePre, nil, "deploy") + err := runner.RunHooks(*mockContext.Context, HookTypePre, "project", nil, "deploy") require.NoError(t, err) require.True(t, shellRan) }) @@ -369,7 +369,7 @@ func Test_Hooks_Validation(t *testing.T) { mockContext.Console, cwd, hooksMap, env, mockContext.Container, ) - err := runner.RunHooks(*mockContext.Context, HookTypePre, nil, "deploy") + err := runner.RunHooks(*mockContext.Context, HookTypePre, "project", nil, "deploy") require.NoError(t, err) require.True(t, shellRan) }) @@ -399,7 +399,7 @@ func Test_Hooks_Validation(t *testing.T) { mockContext.Console, cwd, hooksMap, env, mockContext.Container, ) - err := runner.RunHooks(*mockContext.Context, HookTypePre, nil, "inline") + err := runner.RunHooks(*mockContext.Context, HookTypePre, "project", nil, "inline") require.NoError(t, err) require.True(t, inlineRan) }) @@ -420,7 +420,7 @@ func Test_Hooks_Validation(t *testing.T) { mockContext.Console, cwd, hooksMap, env, mockContext.Container, ) - err := runner.RunHooks(*mockContext.Context, HookTypePre, nil, "deploy") + err := runner.RunHooks(*mockContext.Context, HookTypePre, "project", nil, "deploy") require.Error(t, err) require.ErrorIs(t, err, ErrRunRequired) }) @@ -491,7 +491,7 @@ func Test_ExecHook_NonShellHooks(t *testing.T) { ) err := runner.RunHooks( - *mockContext.Context, HookTypePre, nil, "deploy", + *mockContext.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -547,7 +547,7 @@ func Test_ExecHook_NonShellHooks(t *testing.T) { ) err := runner.RunHooks( - *mockContext.Context, HookTypePre, nil, "deploy", + *mockContext.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -603,7 +603,7 @@ func Test_ExecHook_NonShellHooks(t *testing.T) { ) err := runner.RunHooks( - *mockContext.Context, HookTypePre, nil, "deploy", + *mockContext.Context, HookTypePre, "project", nil, "deploy", ) require.Error(t, err) @@ -665,7 +665,7 @@ func Test_ExecHook_NonShellHooks(t *testing.T) { ) err := runner.RunHooks( - *mockContext.Context, HookTypePre, nil, "deploy", + *mockContext.Context, HookTypePre, "project", nil, "deploy", ) require.Error(t, err) @@ -734,7 +734,7 @@ func Test_ExecHook_NonShellHooks(t *testing.T) { ) err := runner.RunHooks( - *mockContext.Context, HookTypePre, nil, "deploy", + *mockContext.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -848,7 +848,7 @@ func Test_ExecHook_DirRunResolution(t *testing.T) { ) err := runner.RunHooks( - *mockContext.Context, HookTypePre, nil, + *mockContext.Context, HookTypePre, "project", nil, "provision", ) @@ -937,7 +937,7 @@ func Test_ExecHook_DirRunResolution(t *testing.T) { ) err := runner.RunHooks( - *mockContext.Context, HookTypePre, nil, "deploy", + *mockContext.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -1034,7 +1034,7 @@ func Test_ExecHook_DirRunResolution(t *testing.T) { ) err := runner.RunHooks( - *mockContext.Context, HookTypePre, nil, + *mockContext.Context, HookTypePre, "project", nil, "provision", ) @@ -1157,7 +1157,8 @@ func Test_ExecHook_ConfigThreading(t *testing.T) { ) err := runner.RunHooks( - *mockContext.Context, HookTypePre, nil, + *mockContext.Context, HookTypePre, + "project", nil, "deploy", ) require.NoError(t, err) @@ -1232,7 +1233,8 @@ func Test_ExecHook_ConfigThreading(t *testing.T) { ) err := runner.RunHooks( - *mockContext.Context, HookTypePre, nil, + *mockContext.Context, HookTypePre, + "project", nil, "deploy", ) require.NoError(t, err) @@ -1240,3 +1242,150 @@ func Test_ExecHook_ConfigThreading(t *testing.T) { require.Nil(t, capturedCtx.Config) }) } + +// TestHooksRunner_Telemetry exercises the telemetry span creation and +// attribute-setting code paths in execHook. The tests verify that the +// telemetry instrumentation doesn't panic or error on success, error, +// and validation-failure paths. +func TestHooksRunner_Telemetry(t *testing.T) { + t.Run("Success", func(t *testing.T) { + cwd := t.TempDir() + ostest.Chdir(t, cwd) + + env := environment.NewWithValues("test", map[string]string{}) + + require.NoError(t, os.MkdirAll( + filepath.Join(cwd, "scripts"), osutil.PermissionDirectory, + )) + require.NoError(t, os.WriteFile( + filepath.Join(cwd, "scripts", "predeploy.sh"), + nil, osutil.PermissionExecutableFile, + )) + + hooksMap := map[string][]*HookConfig{ + "predeploy": {{ + Name: "predeploy", + Shell: string(language.HookKindBash), + Run: "scripts/predeploy.sh", + }}, + } + + envManager := &mockenv.MockEnvManager{} + envManager.On("Reload", mock.Anything, env).Return(nil) + + mockContext := mocks.NewMockContext(context.Background()) + registerHookExecutors(mockContext) + mockContext.CommandRunner.When(func(args exec.RunArgs, command string) bool { + return strings.Contains(command, "predeploy.sh") + }).RespondFn(func(args exec.RunArgs) (exec.RunResult, error) { + return exec.NewRunResult(0, "", ""), nil + }) + + hooksManager := NewHooksManager( + HooksManagerOptions{Cwd: cwd, ProjectDir: cwd}, + mockContext.CommandRunner, + ) + runner := NewHooksRunner( + hooksManager, mockContext.CommandRunner, envManager, + mockContext.Console, cwd, hooksMap, env, + mockContext.Container, + ) + + err := runner.RunHooks( + *mockContext.Context, HookTypePre, + "project", nil, "deploy", + ) + require.NoError(t, err) + }) + + t.Run("Error", func(t *testing.T) { + cwd := t.TempDir() + ostest.Chdir(t, cwd) + + env := environment.NewWithValues("test", map[string]string{}) + + require.NoError(t, os.MkdirAll( + filepath.Join(cwd, "scripts"), osutil.PermissionDirectory, + )) + require.NoError(t, os.WriteFile( + filepath.Join(cwd, "scripts", "predeploy.sh"), + nil, osutil.PermissionExecutableFile, + )) + + hooksMap := map[string][]*HookConfig{ + "predeploy": {{ + Name: "predeploy", + Shell: string(language.HookKindBash), + Run: "scripts/predeploy.sh", + }}, + } + + envManager := &mockenv.MockEnvManager{} + envManager.On("Reload", mock.Anything, env).Return(nil) + + mockContext := mocks.NewMockContext(context.Background()) + registerHookExecutors(mockContext) + mockContext.CommandRunner.When(func(args exec.RunArgs, command string) bool { + return strings.Contains(command, "predeploy.sh") + }).RespondFn(func(args exec.RunArgs) (exec.RunResult, error) { + return exec.NewRunResult(1, "", "boom"), + fmt.Errorf("script failed") + }) + + hooksManager := NewHooksManager( + HooksManagerOptions{Cwd: cwd, ProjectDir: cwd}, + mockContext.CommandRunner, + ) + runner := NewHooksRunner( + hooksManager, mockContext.CommandRunner, envManager, + mockContext.Console, cwd, hooksMap, env, + mockContext.Container, + ) + + err := runner.RunHooks( + *mockContext.Context, HookTypePre, + "project", nil, "deploy", + ) + require.Error(t, err) + require.Contains(t, err.Error(), "'predeploy' hook failed") + }) + + t.Run("ValidationFailure", func(t *testing.T) { + cwd := t.TempDir() + ostest.Chdir(t, cwd) + + env := environment.NewWithValues("test", map[string]string{}) + + // Hook with empty Run triggers ErrRunRequired in + // filterConfigs before execHook is called. + hooksMap := map[string][]*HookConfig{ + "predeploy": {{ + Name: "predeploy", + Shell: string(language.HookKindBash), + }}, + } + + envManager := &mockenv.MockEnvManager{} + envManager.On("Reload", mock.Anything, env).Return(nil) + + mockContext := mocks.NewMockContext(context.Background()) + registerHookExecutors(mockContext) + + hooksManager := NewHooksManager( + HooksManagerOptions{Cwd: cwd, ProjectDir: cwd}, + mockContext.CommandRunner, + ) + runner := NewHooksRunner( + hooksManager, mockContext.CommandRunner, envManager, + mockContext.Console, cwd, hooksMap, env, + mockContext.Container, + ) + + err := runner.RunHooks( + *mockContext.Context, HookTypePre, + "project", nil, "deploy", + ) + require.Error(t, err) + require.ErrorIs(t, err, ErrRunRequired) + }) +} diff --git a/cli/azd/pkg/ext/js_hooks_e2e_test.go b/cli/azd/pkg/ext/js_hooks_e2e_test.go index 86736217003..a2a1fde229b 100644 --- a/cli/azd/pkg/ext/js_hooks_e2e_test.go +++ b/cli/azd/pkg/ext/js_hooks_e2e_test.go @@ -137,7 +137,7 @@ func TestJsHook_AutoDetectFromExtension(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -189,7 +189,7 @@ func TestJsHook_ExplicitKind(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -236,7 +236,7 @@ func TestJsHook_WithPackageJSON(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -279,7 +279,7 @@ func TestJsHook_NodeBinaryResolution(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -321,7 +321,7 @@ func TestJsHook_NonZeroExitCode(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.Error(t, err) @@ -364,7 +364,7 @@ func TestJsHook_ContinueOnError(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -408,7 +408,7 @@ func TestJsHook_ServiceLevel(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePost, nil, "deploy", + *mockCtx.Context, HookTypePost, "project", nil, "deploy", ) require.NoError(t, err) @@ -454,7 +454,7 @@ func TestJsHook_EnvVarsPassthrough(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -519,7 +519,7 @@ func TestJsHook_NodeMissing(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.Error(t, err) @@ -624,7 +624,7 @@ func TestJsHook_ExecutionPipeline(t *testing.T) { ) err := runner.RunHooks( *mockCtx.Context, - HookTypePre, nil, "deploy", + HookTypePre, "project", nil, "deploy", ) if tt.wantErr { @@ -664,7 +664,7 @@ func TestJsHook_InlineScriptRejected(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.Error(t, err) @@ -711,7 +711,7 @@ func TestJsHook_StdoutCapture(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) } @@ -795,7 +795,7 @@ func TestJsHook_ShellHookUnaffected(t *testing.T) { // Run the shell hook. err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "build", + *mockCtx.Context, HookTypePre, "project", nil, "build", ) require.NoError(t, err) require.True( @@ -805,7 +805,7 @@ func TestJsHook_ShellHookUnaffected(t *testing.T) { // Run the JS hook. err = runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) require.True( @@ -868,7 +868,7 @@ func TestJsHook_ExplicitDirOverridesCwd(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -916,7 +916,7 @@ func TestJsHook_ProjectLevel(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "provision", + *mockCtx.Context, HookTypePre, "project", nil, "provision", ) require.NoError(t, err) diff --git a/cli/azd/pkg/ext/python_hooks_e2e_test.go b/cli/azd/pkg/ext/python_hooks_e2e_test.go index a0c02618ad9..6c943d2d58d 100644 --- a/cli/azd/pkg/ext/python_hooks_e2e_test.go +++ b/cli/azd/pkg/ext/python_hooks_e2e_test.go @@ -149,7 +149,7 @@ func TestPythonHook_AutoDetectFromExtension(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -205,7 +205,7 @@ func TestPythonHook_ExplicitKind(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -254,7 +254,7 @@ func TestPythonHook_EnvVarsPassthrough(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -340,7 +340,7 @@ func TestPythonHook_WithRequirementsTxt(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -392,7 +392,7 @@ func TestPythonHook_StdoutCapture(t *testing.T) { // the pipeline completes without error confirming the // execution path ran and stdout was handled. err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) } @@ -434,7 +434,7 @@ func TestPythonHook_NonZeroExitCode(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.Error(t, err) @@ -480,7 +480,7 @@ func TestPythonHook_ContinueOnError(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) // ContinueOnError should suppress the error. @@ -525,7 +525,7 @@ func TestPythonHook_ProjectLevel(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "provision", + *mockCtx.Context, HookTypePre, "project", nil, "provision", ) require.NoError(t, err) @@ -576,7 +576,7 @@ func TestPythonHook_ServiceLevel(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePost, nil, "deploy", + *mockCtx.Context, HookTypePost, "project", nil, "deploy", ) require.NoError(t, err) @@ -670,7 +670,7 @@ func TestPythonHook_ShellHookUnaffected(t *testing.T) { // Run the shell hook. err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "build", + *mockCtx.Context, HookTypePre, "project", nil, "build", ) require.NoError(t, err) require.True( @@ -680,7 +680,7 @@ func TestPythonHook_ShellHookUnaffected(t *testing.T) { // Run the Python hook. err = runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) require.True( @@ -787,7 +787,7 @@ func TestPythonHook_ExecutionPipeline(t *testing.T) { ) err := runner.RunHooks( *mockCtx.Context, - HookTypePre, nil, "deploy", + HookTypePre, "project", nil, "deploy", ) if tt.wantErr { @@ -841,7 +841,7 @@ func TestPythonHook_PythonBinaryResolution(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -915,7 +915,7 @@ func TestPythonHook_ExplicitDirOverridesCwd(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -951,7 +951,7 @@ func TestPythonHook_InlineScriptRejected(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.Error(t, err) diff --git a/cli/azd/pkg/ext/ts_hooks_e2e_test.go b/cli/azd/pkg/ext/ts_hooks_e2e_test.go index 13ac3c3c9b3..8bcb05fda9e 100644 --- a/cli/azd/pkg/ext/ts_hooks_e2e_test.go +++ b/cli/azd/pkg/ext/ts_hooks_e2e_test.go @@ -100,7 +100,7 @@ func TestTsHook_AutoDetectFromExtension(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -156,7 +156,7 @@ func TestTsHook_ExplicitKind(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -203,7 +203,7 @@ func TestTsHook_WithPackageJSON(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -245,7 +245,7 @@ func TestTsHook_NonZeroExitCode(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.Error(t, err) @@ -288,7 +288,7 @@ func TestTsHook_ContinueOnError(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -332,7 +332,7 @@ func TestTsHook_ServiceLevel(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePost, nil, "deploy", + *mockCtx.Context, HookTypePost, "project", nil, "deploy", ) require.NoError(t, err) @@ -391,7 +391,7 @@ func TestTsHook_NodeMissing(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.Error(t, err) @@ -437,7 +437,7 @@ func TestTsHook_EnvVarsPassthrough(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -543,7 +543,7 @@ func TestTsHook_ExecutionPipeline(t *testing.T) { ) err := runner.RunHooks( *mockCtx.Context, - HookTypePre, nil, "deploy", + HookTypePre, "project", nil, "deploy", ) if tt.wantErr { @@ -583,7 +583,7 @@ func TestTsHook_InlineScriptRejected(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.Error(t, err) @@ -630,7 +630,7 @@ func TestTsHook_StdoutCapture(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) } @@ -714,7 +714,7 @@ func TestTsHook_ShellHookUnaffected(t *testing.T) { // Run the shell hook. err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "build", + *mockCtx.Context, HookTypePre, "project", nil, "build", ) require.NoError(t, err) require.True( @@ -724,7 +724,7 @@ func TestTsHook_ShellHookUnaffected(t *testing.T) { // Run the TS hook. err = runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) require.True( @@ -787,7 +787,7 @@ func TestTsHook_ExplicitDirOverridesCwd(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "deploy", + *mockCtx.Context, HookTypePre, "project", nil, "deploy", ) require.NoError(t, err) @@ -835,7 +835,7 @@ func TestTsHook_ProjectLevel(t *testing.T) { t, mockCtx, cwd, hooksMap, env, ) err := runner.RunHooks( - *mockCtx.Context, HookTypePre, nil, "provision", + *mockCtx.Context, HookTypePre, "project", nil, "provision", ) require.NoError(t, err)