From 30b5d402a7d27637aa361a292ed08d5dc3081ff1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Apr 2026 23:26:31 +0000 Subject: [PATCH 1/4] Initial plan From 46ff2d813c2221a259412388b862a49a529503d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Apr 2026 23:31:36 +0000 Subject: [PATCH 2/4] fix(azure.ai.agents): skip postdeploy handler when no hosted agent services present Agent-Logs-Url: https://github.com/Azure/azure-dev/sessions/1ed3924e-b0bb-458b-9d38-40bb3887400b Co-authored-by: JeffreyCA <9157833+JeffreyCA@users.noreply.github.com> --- .../azure.ai.agents/internal/cmd/listen.go | 19 ++++++++++++++ .../internal/cmd/listen_test.go | 25 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go index 5e7b6335985..83c8f37f3b3 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go @@ -160,6 +160,25 @@ func isHostedAgentService(svc *azdext.ServiceConfig, proj *azdext.ProjectConfig) } func postdeployHandler(ctx context.Context, azdClient *azdext.AzdClient, args *azdext.ProjectEventArgs) error { + // IMPORTANT: This guard must remain at the top of postdeployHandler. + // The extension subscribes to the global `postdeploy` project event, which fires for + // every `azd deploy`/`azd up` regardless of whether the project has any hosted agent + // services. Without this early return, projects that don't use the agents extension + // (e.g. plain container apps) hit the AZURE_AI_PROJECT_ENDPOINT/AZURE_TENANT_ID checks + // below and surface confusing errors. See https://github.com/Azure/azure-dev/pull/7373 + // (and the regression that brought this back) for context — do not remove without + // providing an equivalent no-op short-circuit for non-hosted-agent projects. + hasHostedAgent := false + for _, svc := range args.Project.Services { + if svc.Host == AiAgentHost && isHostedAgentService(svc, args.Project) { + hasHostedAgent = true + break + } + } + if !hasHostedAgent { + return nil + } + // Collect agent identities from hosted agent services that were deployed. // After deploy, each hosted agent's name/version is stored as AGENT_{SERVICE_KEY}_NAME/VERSION. // We fetch the full agent version object from the API to get the instance identity principal ID, diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen_test.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen_test.go index dfef64bec94..5e8775a3270 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen_test.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen_test.go @@ -7,8 +7,33 @@ import ( "testing" "azureaiagent/internal/project" + + "github.com/azure/azure-dev/cli/azd/pkg/azdext" ) +// TestPostdeployHandler_NoAgentService_NoOp verifies that postdeployHandler returns nil +// without making any azd RPC calls when the project does not include any hosted agent +// services. This guards against the regression described in +// https://github.com/Azure/azure-dev/pull/7373 where projects that don't use the agents +// extension would surface confusing errors (e.g. AZURE_AI_PROJECT_ENDPOINT not set) +// from the postdeploy hook. +func TestPostdeployHandler_NoAgentService_NoOp(t *testing.T) { + t.Parallel() + + args := &azdext.ProjectEventArgs{ + Project: &azdext.ProjectConfig{ + Services: map[string]*azdext.ServiceConfig{ + "teams-bot": {Name: "teams-bot", Host: "containerapp"}, + }, + }, + } + + // azdClient is intentionally nil — the early return must fire before any RPC call. + if err := postdeployHandler(t.Context(), nil, args); err != nil { + t.Fatalf("expected no error for project without agent services, got: %v", err) + } +} + func TestParseConnectionIDs(t *testing.T) { t.Parallel() From aa04c79edebd955c93da7cff2e2c0e50b1570a6a Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Wed, 29 Apr 2026 23:39:12 +0000 Subject: [PATCH 3/4] trim verbose comments on postdeploy guard and test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../extensions/azure.ai.agents/internal/cmd/listen.go | 11 +++-------- .../azure.ai.agents/internal/cmd/listen_test.go | 11 ++++------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go index 83c8f37f3b3..21989a54a8c 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go @@ -160,14 +160,9 @@ func isHostedAgentService(svc *azdext.ServiceConfig, proj *azdext.ProjectConfig) } func postdeployHandler(ctx context.Context, azdClient *azdext.AzdClient, args *azdext.ProjectEventArgs) error { - // IMPORTANT: This guard must remain at the top of postdeployHandler. - // The extension subscribes to the global `postdeploy` project event, which fires for - // every `azd deploy`/`azd up` regardless of whether the project has any hosted agent - // services. Without this early return, projects that don't use the agents extension - // (e.g. plain container apps) hit the AZURE_AI_PROJECT_ENDPOINT/AZURE_TENANT_ID checks - // below and surface confusing errors. See https://github.com/Azure/azure-dev/pull/7373 - // (and the regression that brought this back) for context — do not remove without - // providing an equivalent no-op short-circuit for non-hosted-agent projects. + // Skip when the project has no hosted agent services. `postdeploy` fires on every + // `azd deploy`, so without this guard the AZURE_AI_PROJECT_ENDPOINT/AZURE_TENANT_ID + // reads below would fail for projects that don't use this extension. See #7373. hasHostedAgent := false for _, svc := range args.Project.Services { if svc.Host == AiAgentHost && isHostedAgentService(svc, args.Project) { diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen_test.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen_test.go index 5e8775a3270..f8d27d366cf 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen_test.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen_test.go @@ -11,12 +11,9 @@ import ( "github.com/azure/azure-dev/cli/azd/pkg/azdext" ) -// TestPostdeployHandler_NoAgentService_NoOp verifies that postdeployHandler returns nil -// without making any azd RPC calls when the project does not include any hosted agent -// services. This guards against the regression described in -// https://github.com/Azure/azure-dev/pull/7373 where projects that don't use the agents -// extension would surface confusing errors (e.g. AZURE_AI_PROJECT_ENDPOINT not set) -// from the postdeploy hook. +// TestPostdeployHandler_NoAgentService_NoOp verifies postdeployHandler returns nil +// without any RPC calls when the project has no hosted agent services. Regression +// guard for #7373. func TestPostdeployHandler_NoAgentService_NoOp(t *testing.T) { t.Parallel() @@ -28,7 +25,7 @@ func TestPostdeployHandler_NoAgentService_NoOp(t *testing.T) { }, } - // azdClient is intentionally nil — the early return must fire before any RPC call. + // nil azdClient — the early return must fire before any RPC call. if err := postdeployHandler(t.Context(), nil, args); err != nil { t.Fatalf("expected no error for project without agent services, got: %v", err) } From 09f60a9cbed920783c01da17390b8ade5d8ce155 Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Wed, 29 Apr 2026 23:51:01 +0000 Subject: [PATCH 4/4] Address feedback --- .../azure.ai.agents/internal/cmd/listen.go | 12 ++++-------- .../azure.ai.agents/internal/cmd/listen_test.go | 5 ++++- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go index 21989a54a8c..70d139d5063 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go @@ -163,14 +163,13 @@ func postdeployHandler(ctx context.Context, azdClient *azdext.AzdClient, args *a // Skip when the project has no hosted agent services. `postdeploy` fires on every // `azd deploy`, so without this guard the AZURE_AI_PROJECT_ENDPOINT/AZURE_TENANT_ID // reads below would fail for projects that don't use this extension. See #7373. - hasHostedAgent := false + var hostedAgents []*azdext.ServiceConfig for _, svc := range args.Project.Services { if svc.Host == AiAgentHost && isHostedAgentService(svc, args.Project) { - hasHostedAgent = true - break + hostedAgents = append(hostedAgents, svc) } } - if !hasHostedAgent { + if len(hostedAgents) == 0 { return nil } @@ -223,10 +222,7 @@ func postdeployHandler(ctx context.Context, azdClient *azdext.AzdClient, args *a // Build name→principalID map by fetching the agent version for each hosted service. agentIdentities := make(map[string]string) - for _, svc := range args.Project.Services { - if svc.Host != AiAgentHost || !isHostedAgentService(svc, args.Project) { - continue - } + for _, svc := range hostedAgents { serviceKey := toServiceKey(svc.Name) versionResp, err := azdClient.Environment().GetValue(ctx, &azdext.GetEnvRequest{ diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen_test.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen_test.go index f8d27d366cf..df2ebef4c45 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen_test.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen_test.go @@ -17,10 +17,13 @@ import ( func TestPostdeployHandler_NoAgentService_NoOp(t *testing.T) { t.Parallel() + // Use a temp dir + explicit RelativePath so isHostedAgentService deterministically + // returns false (no agent.yaml present) regardless of the test working directory. args := &azdext.ProjectEventArgs{ Project: &azdext.ProjectConfig{ + Path: t.TempDir(), Services: map[string]*azdext.ServiceConfig{ - "teams-bot": {Name: "teams-bot", Host: "containerapp"}, + "teams-bot": {Name: "teams-bot", Host: "containerapp", RelativePath: "."}, }, }, }