Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,19 @@ func isHostedAgentService(svc *azdext.ServiceConfig, proj *azdext.ProjectConfig)
}

func postdeployHandler(ctx context.Context, azdClient *azdext.AzdClient, args *azdext.ProjectEventArgs) error {
// 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.
var hostedAgents []*azdext.ServiceConfig
for _, svc := range args.Project.Services {
if svc.Host == AiAgentHost && isHostedAgentService(svc, args.Project) {
hostedAgents = append(hostedAgents, svc)
}
}
if len(hostedAgents) == 0 {
return nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium] DRY: extract shared hasHostedAgentService helper

predeployHandler already has an identical loop scanning svc.Host == AiAgentHost && isHostedAgentService(svc, args.Project). Extracting a shared helper like func hasHostedAgentService(proj *azdext.ProjectConfig) bool would eliminate this duplication and simplify both callers.

Not a blocker — fine as a follow-up.

}

// 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,
Expand Down Expand Up @@ -209,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{
Expand Down
25 changes: 25 additions & 0 deletions cli/azd/extensions/azure.ai.agents/internal/cmd/listen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,33 @@ import (
"testing"

"azureaiagent/internal/project"

"github.com/azure/azure-dev/cli/azd/pkg/azdext"
)

// 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()

// 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", RelativePath: "."},
},
},
}

// 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)
}
}

func TestParseConnectionIDs(t *testing.T) {
t.Parallel()

Expand Down
Loading