From 7051004f1e893b0feefe4ad197a5fee4fe1e5f39 Mon Sep 17 00:00:00 2001 From: Jian Wu Date: Fri, 22 May 2026 09:27:19 +0800 Subject: [PATCH 1/2] fix: correct end-of-init message logic for existing project + existing deployment The end message incorrectly recommended 'azd up' when the user selected an existing project with an existing deployment (no provisioning needed). Root cause: len(a.deploymentDetails) == 0 was used to detect whether new provisioning is needed, but existing deployments also populate this slice. Fix: Replace with an explicit 'needsProvision' boolean that is only set when a new model deployment is created (deploy-from-catalog path). This aligns init.go with the pattern already used in init_from_code.go. Follow-up to #8292. --- cli/azd/extensions/azure.ai.agents/internal/cmd/init.go | 3 ++- cli/azd/extensions/azure.ai.agents/internal/cmd/init_models.go | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/init.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/init.go index 8a8a70c8e1a..573255c1eda 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/init.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/init.go @@ -81,6 +81,7 @@ type InitAction struct { models *modelSelector deploymentDetails []project.Deployment + needsProvision bool containerSettings *project.ContainerSettings isCodeDeploy bool // true when user selects code deploy mode; skips ACR config httpClient *http.Client @@ -2089,7 +2090,7 @@ func (a *InitAction) addToProject(ctx context.Context, targetDir string, agentMa if projectID, _ := a.azdClient.Environment().GetValue(ctx, &azdext.GetEnvRequest{ EnvName: a.environment.Name, Key: "AZURE_AI_PROJECT_ID", - }); projectID != nil && projectID.Value != "" && len(a.deploymentDetails) == 0 { + }); projectID != nil && projectID.Value != "" && !a.needsProvision { fmt.Printf("To deploy your agent, use %s.\n", color.HiBlueString("azd deploy %s", a.serviceNameOverride)) } else { diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/init_models.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/init_models.go index a607d516cb4..dea778cc50d 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/init_models.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/init_models.go @@ -291,6 +291,9 @@ func (a *InitAction) getModelDeploymentDetails(ctx context.Context, model agent_ return nil, fmt.Errorf("failed to get model details: %w", err) } + // This path creates a new model deployment that needs provisioning + a.needsProvision = true + message := fmt.Sprintf("Enter model deployment name for model '%s' (defaults to model name)", modelDetails.ModelName) modelDeploymentInput, err := a.azdClient.Prompt().Prompt(ctx, &azdext.PromptRequest{ From 1d72f726e64498a379a141fbbbad0a5b0bdcb4f4 Mon Sep 17 00:00:00 2001 From: Jian Wu Date: Fri, 22 May 2026 13:31:13 +0800 Subject: [PATCH 2/2] Add unit tests for end-of-init message logic Extract initCompletionNeedsDeploy() helper and add TestInitCompletionNeedsDeploy covering the truth table: - existing project + no new deployments -> azd deploy - existing project + new model deployed -> azd up - no project set -> azd up --- .../azure.ai.agents/internal/cmd/init.go | 20 +++++-- .../azure.ai.agents/internal/cmd/init_test.go | 59 +++++++++++++++++++ 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/init.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/init.go index 573255c1eda..2b7bbf8ef01 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/init.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/init.go @@ -2087,10 +2087,7 @@ func (a *InitAction) addToProject(ctx context.Context, targetDir string, agentMa "\nAdded your agent as a service entry named '%s' under the file azure.yaml.\n", a.serviceNameOverride, ) - if projectID, _ := a.azdClient.Environment().GetValue(ctx, &azdext.GetEnvRequest{ - EnvName: a.environment.Name, - Key: "AZURE_AI_PROJECT_ID", - }); projectID != nil && projectID.Value != "" && !a.needsProvision { + if a.initCompletionNeedsDeploy(ctx) { fmt.Printf("To deploy your agent, use %s.\n", color.HiBlueString("azd deploy %s", a.serviceNameOverride)) } else { @@ -2102,6 +2099,21 @@ func (a *InitAction) addToProject(ctx context.Context, targetDir string, agentMa return nil } +// initCompletionNeedsDeploy returns true when the user only needs to run +// "azd deploy " (i.e. infrastructure is already provisioned and no +// new model deployments were requested). When false, the user should run +// "azd up" to provision and deploy. +func (a *InitAction) initCompletionNeedsDeploy(ctx context.Context) bool { + if a.needsProvision { + return false + } + projectID, _ := a.azdClient.Environment().GetValue(ctx, &azdext.GetEnvRequest{ + EnvName: a.environment.Name, + Key: "AZURE_AI_PROJECT_ID", + }) + return projectID != nil && projectID.Value != "" +} + // resolveCollisions checks whether the auto-computed target directory or // service name already exist. When a collision is detected, the user is // prompted for a new name (or a numeric suffix is appended in no-prompt diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/init_test.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/init_test.go index c37af2b2eb2..526d6b0bbfe 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/init_test.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/init_test.go @@ -2282,3 +2282,62 @@ func TestDownloadAgentYaml_NoPromptManifestInSrcWithoutForce(t *testing.T) { t.Errorf("suggestion should mention --force, got: %s", localErr.Suggestion) } } + +func TestInitCompletionNeedsDeploy(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + needsProvision bool + projectID string // empty means AZURE_AI_PROJECT_ID is not set + want bool + }{ + { + name: "existing project, no new deployments → deploy only", + needsProvision: false, + projectID: "/subscriptions/sub/resourceGroups/rg/providers/Microsoft.CognitiveServices/accounts/acct/projects/proj", + want: true, + }, + { + name: "existing project, new model deployed → needs provision", + needsProvision: true, + projectID: "/subscriptions/sub/resourceGroups/rg/providers/Microsoft.CognitiveServices/accounts/acct/projects/proj", + want: false, + }, + { + name: "no project set → needs provision", + needsProvision: false, + projectID: "", + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + envName := "test-env" + envServer := &testEnvironmentServiceServer{ + values: map[string]map[string]string{}, + } + if tt.projectID != "" { + envServer.values[envName] = map[string]string{ + "AZURE_AI_PROJECT_ID": tt.projectID, + } + } + + azdClient := newTestAzdClient(t, envServer, &testWorkflowServiceServer{}) + + action := &InitAction{ + azdClient: azdClient, + needsProvision: tt.needsProvision, + environment: &azdext.Environment{Name: envName}, + } + + got := action.initCompletionNeedsDeploy(t.Context()) + if got != tt.want { + t.Errorf("initCompletionNeedsDeploy() = %v, want %v", got, tt.want) + } + }) + } +}