Skip to content

fix: apply --deploy-mode flags to adopted azure.yaml (#8923)#8926

Open
trangevi wants to merge 1 commit into
mainfrom
trangevi/fix-8923-adopt-azure-yaml-deploy-mode
Open

fix: apply --deploy-mode flags to adopted azure.yaml (#8923)#8926
trangevi wants to merge 1 commit into
mainfrom
trangevi/fix-8923-adopt-azure-yaml-deploy-mode

Conversation

@trangevi

@trangevi trangevi commented Jul 1, 2026

Copy link
Copy Markdown
Member

Why

When azd ai agent init -m <azure.yaml> routes to the unified Foundry adoption path, the --deploy-mode, --runtime, and --entry-point flags are silently ignored. The adopted azure.yaml is written as-is with no code_configuration applied to the agent service. This means users cannot select code deploy when adopting a sample.

Approach

After scaffoldProject succeeds, the new applyDeployModeToAdoptedProject function locates the azure.ai.agent service in the adopted project and applies deploy-mode configuration using the same promptDeployMode / promptCodeConfig functions the manifest path already uses.

Decision logic:

  • Explicit --deploy-mode flag -- always apply (user intent overrides sample)
  • No flag + service already has code_configuration or docker property -- no-op (respect pre-configured sample)
  • No flag + neither configured -- prompt interactively (or auto-select per noPrompt/userProvidedManifest rules)

For code deploy: writes code_configuration (runtime, entry_point, dependency_resolution) onto the service, updates language to python/csharp, clears any docker property.

For container deploy: sets the docker property (remoteBuild: true), updates language to docker, clears any code_configuration.

Testing

  • Added TestAdoptedServiceHasCodeConfig covering nil props, empty props, valid struct value, and null value cases
  • All existing adoption tests continue to pass
  • Full go test ./internal/cmd/... -short passes

Fixes: #8923

The unified azure.yaml adoption path (runInitFromAzureYaml) now processes
--deploy-mode, --runtime, and --entry-point flags after scaffolding the
project. For code deploy, code_configuration is written onto the agent
service and the language is updated. For container deploy, the docker
property is set.

When no flags are provided and the service already has code_configuration
or a docker property configured, the service is left unchanged (the
sample's pre-configured state is respected).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 1, 2026 17:18
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

📋 Prioritization Note

Thanks for the contribution! The linked issue isn't in the current milestone yet.
Thank you for logging this issue; our team is reviewing it. If you need urgent prioritization, tag @RickWinter and @kristenwomack to let us know.

Copilot AI left a comment

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.

Pull request overview

This PR fixes #8923, where azd ai agent init -m <azure.yaml> (the unified Foundry adoption path added in #8885) silently ignored --deploy-mode, --runtime, and --entry-point. After the sample's azure.yaml is adopted, a new applyDeployModeToAdoptedProject hook locates the azure.ai.agent service and applies code- or container-deploy configuration, reusing the same promptDeployMode/promptCodeConfig helpers as the manifest path. Configuration is written back to the service via SetServiceConfigValue.

Changes:

  • Add applyDeployModeToAdoptedProject and the applyCodeDeployToService/applyContainerDeployToService/adoptedServiceHasCodeConfig helpers, invoked after scaffolding/provider stamping in runInitFromAzureYaml.
  • Resolve deploy mode with explicit-flag precedence, no-op when the sample is already configured, and prompt/auto-select otherwise; write code_configuration/docker/language onto the agent service.
  • Add TestAdoptedServiceHasCodeConfig covering nil/empty/struct/null property cases.

The core issue with this PR is a key-casing mismatch: the adopted path reads and writes snake_case (code_configuration, entry_point, dependency_resolution), but the unified azure.yaml inline agent shape and the extension's deploy/read path use camelCase (codeConfiguration, entryPoint, dependencyResolution, per schemas/azure.ai.agent.json and AgentDefinitionInline). Because SetServiceConfigValue writes the path verbatim into azure.yaml, the code configuration this PR writes will not be recognized at deploy time, so --deploy-mode code still has no effect — the bug the PR intends to fix. Detection of already-configured samples and the code→container clear path share the same casing problem, and language/runtime detection runs against . instead of the agent's source subdirectory.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
cli/azd/extensions/azure.ai.agents/internal/cmd/init_adopt.go Adds the deploy-mode application hook and helpers for the adopted unified azure.yaml; uses snake_case config keys and . for language detection (both incorrect).
cli/azd/extensions/azure.ai.agents/internal/cmd/init_adopt_test.go Adds TestAdoptedServiceHasCodeConfig, whose fixtures encode the same snake_case key as the code under test.

Comment on lines +631 to +646
codeConfigMap := map[string]any{
"runtime": codeConfig.Runtime,
"entry_point": codeConfig.EntryPoint,
}
if codeConfig.DependencyResolution != nil {
codeConfigMap["dependency_resolution"] = *codeConfig.DependencyResolution
}

codeConfigValue, err := structpb.NewValue(codeConfigMap)
if err != nil {
return fmt.Errorf("encoding code_configuration: %w", err)
}

if _, err := azdClient.Project().SetServiceConfigValue(ctx, &azdext.SetServiceConfigValueRequest{
ServiceName: serviceName,
Path: "code_configuration",
if fields == nil {
return false
}
v, ok := fields["code_configuration"]
// Remove code_configuration if present (switching from code to container).
if _, err := azdClient.Project().SetServiceConfigValue(ctx, &azdext.SetServiceConfigValueRequest{
ServiceName: serviceName,
Path: "code_configuration",
Comment on lines +576 to +579
// The target directory (project root after adoption) is used for
// language detection.
targetDir := "."
showCodeDeploy := isPythonProject(targetDir) || isDotnetProject(targetDir)
Comment on lines +553 to +559
for name, svc := range resp.GetProject().GetServices() {
if svc.GetHost() == AiAgentHost {
agentServiceName = name
agentSvc = svc
break
}
}
Comment on lines +301 to +308
AdditionalProperties: &structpb.Struct{Fields: map[string]*structpb.Value{
"code_configuration": structpb.NewStructValue(&structpb.Struct{
Fields: map[string]*structpb.Value{
"runtime": structpb.NewStringValue("python_3_13"),
"entry_point": structpb.NewStringValue("app.py"),
},
}),
}},

@jongio jongio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Two verified issues that affect correctness, plus a robustness concern:

1. Language detection uses project root instead of service directory (HIGH)

\ argetDir := "."\ means \isPythonProject/\isDotnetProject\ scan the project root, but adopted samples declare agent services in subdirectories (e.g., \project: ./agents/assistant). The manifest path uses the downloaded service directory (\init.go:1388). Here, \�gentSvc.GetRelativePath()\ should be used so auto-detection finds the correct files.

2. Non-deterministic map iteration picks an arbitrary service (MEDIUM)

esp.GetProject().GetServices()\ is a map. With multi-agent samples (researcher, writer, triage), the \�reak\ on line 559 applies configuration to whichever service the runtime iterates first. Either apply to all matching services or collect them and let the user choose.

3. Silently discarded \structpb.NewValue\ errors (LOW)

Lines 665 and 697 discard the error from \structpb.NewValue(language)\ with _. While unlikely to fail for string literals, ignoring errors deviates from the extension's error handling conventions.

Note on the existing bot comments about snake_case vs camelCase: those comments cite \internal/project/agent_definition.go\ and \AgentDefinitionInline, neither of which exist in the codebase. The agent_yaml struct uses YAML tag \code_configuration\ and the service target detection (\service_target_agent.go:1122) also reads \code_configuration. The PR's use of snake_case appears internally consistent. I'd recommend verifying which key the deploy path actually reads from the azure.yaml inline properties to confirm end-to-end correctness.

// The target directory (project root after adoption) is used for
// language detection.
targetDir := "."
showCodeDeploy := isPythonProject(targetDir) || isDotnetProject(targetDir)

@jongio jongio Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

targetDir := "." scans the project root, but the sample's agent code lives in the service subdirectory (e.g., ./agents/assistant). The manifest path at init.go:1388 passes the service's source directory to isPythonProject/isDotnetProject. Use agentSvc.GetRelativePath() here so language detection finds the correct files. Without this, showCodeDeploy will be false for most adopted samples, and promptCodeConfig will auto-detect the wrong runtime/entry-point.

agentSvc = svc
break
}
}

@jongio jongio Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Map iteration order is non-deterministic in Go. If the adopted sample declares multiple azure.ai.agent services (e.g., researcher, writer, triage), this picks an arbitrary one and leaves the others unconfigured. Consider iterating all services and applying the deploy mode to each, or detecting the multi-service case and prompting the user.

}); err != nil {
return fmt.Errorf("updating service language to %s: %w", language, err)
}

@jongio jongio Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
langValue, err := structpb.NewValue(language)
if err != nil {
return fmt.Errorf("encoding language value: %%w", err)
}

@jongio

jongio commented Jul 1, 2026

Copy link
Copy Markdown
Member

Corrected review summary (the review body above has formatting issues from a shell escape bug):

Two verified issues that affect correctness, plus a robustness concern:

1. Language detection uses project root instead of service directory (HIGH)

targetDir := "." means isPythonProject/isDotnetProject scan the project root, but adopted samples declare agent services in subdirectories (e.g., project: ./agents/assistant). The manifest path uses the downloaded service directory (init.go:1388). Here, agentSvc.GetRelativePath() should be used so auto-detection finds the correct files.

2. Non-deterministic map iteration picks an arbitrary service (MEDIUM)

resp.GetProject().GetServices() is a map. With multi-agent samples (researcher, writer, triage), the break on line 559 applies configuration to whichever service the runtime iterates first. Either apply to all matching services or collect them and let the user choose.

3. Silently discarded structpb.NewValue errors (LOW)

Lines 665 and 697 discard the error from structpb.NewValue(language) with _. While unlikely to fail for string literals, ignoring errors deviates from the extension's error handling conventions.

Note on the existing bot comments about snake_case vs camelCase: those comments cite internal/project/agent_definition.go and AgentDefinitionInline, neither of which exist in the codebase. The agent_yaml struct uses YAML tag code_configuration and the service target detection (service_target_agent.go:1122) also reads code_configuration. The PR's use of snake_case appears internally consistent. I'd recommend verifying which key the deploy path actually reads from the azure.yaml inline properties to confirm end-to-end correctness.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ext-agents azure.ai.agents extension

Projects

None yet

Development

Successfully merging this pull request may close these issues.

azd ai agent init -m <azure.yaml> ignores --deploy-mode code and does not update the adopted azure.yaml

3 participants