Skip to content

feat(agents): validate .NET runtime vs TargetFramework after init#8222

Open
v1212 wants to merge 1 commit into
Azure:mainfrom
v1212:feature/init-validate-dotnet-runtime
Open

feat(agents): validate .NET runtime vs TargetFramework after init#8222
v1212 wants to merge 1 commit into
Azure:mainfrom
v1212:feature/init-validate-dotnet-runtime

Conversation

@v1212
Copy link
Copy Markdown
Collaborator

@v1212 v1212 commented May 18, 2026

Summary

  • Adds a non-blocking post-init validation framework (validatePostInit) that runs after azd ai agent init completes
  • First validator: checks .NET runtime selection against .csproj TargetFramework prints a clear ERROR (red) with fix instructions if target > runtime (which will fail at build/deploy time)
  • Prints green OK message when validation passes
  • Prints ERROR when .csproj cannot be read, prompting user to verify manually
  • Does NOT block or alter the init flow in any way purely advisory output
  • Wired into both template flow (init.go) and from-code flow (init_from_code.go)
  • Python projects and container deploys are unaffected (skipped)

Changes

File Description
init_validate.go New: validation framework + .NET runtime vs TargetFramework check
init_validate_test.go New: comprehensive unit tests (mismatch, match, higher runtime, no csproj, unreadable dir, python skip)
init.go Call validatePostInit after template init completes
init_from_code.go Call validatePostInit after from-code init completes

Closes #8202

E2E Test Results (Manual Interactive Testing)

All tests run interactively using only azd ai agent init + azd deploy (no file modifications).

Test Scenario Result
V1-P1 C# Template Invocations, .NET 9 vs net10.0 csproj ERROR message PASS
V1-P2 C# Template Invocations, .NET 10 vs net10.0 csproj OK + deploy + invoke PASS
1 Python Template Invocations, remote_build deploy invoke PASS
3 Python Template Responses, remote_build deploy invoke PASS
6 Python From-code vNext Responses, remote_build deploy invoke PASS
A C# Template Invocations, .NET 10 remote_build deploy invoke PASS
B C# Template Responses AgentFW, .NET 10 remote_build deploy invoke PASS

7/7 passed PR feature (runtime validation) works correctly, no regressions in init/deploy/invoke flows.

…ework mismatch

Add a non-blocking validation framework that runs after init completes.
The first validator checks that the selected .NET runtime version is
compatible with the .csproj TargetFramework, printing a clear error
message with fix instructions when a mismatch is detected.

Closes Azure#8202
@v1212 v1212 force-pushed the feature/init-validate-dotnet-runtime branch from b9efd83 to 5011eaf Compare May 18, 2026 02:31
@v1212 v1212 marked this pull request as ready for review May 18, 2026 08:10
Copilot AI review requested due to automatic review settings May 18, 2026 08:10
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a non-blocking, advisory post-azd ai agent init validation that compares the selected .NET runtime (dotnet_N) against the TargetFramework in the project's .csproj. If the target framework is higher than the selected runtime (a known cause of confusing remote-build failures), a red ERROR with fix instructions is printed; matching configurations print a green OK; Python and missing-.csproj cases are skipped. The validator is wired into both the template-based init flow and the from-code init flow, and never alters or blocks init.

Changes:

  • New init_validate.go with validatePostInit framework and validateDotnetRuntimeVsCsproj check, including a simple <TargetFramework>netX.Y</TargetFramework> regex parser.
  • New init_validate_test.go covering match, mismatch, higher-runtime, no-csproj, unreadable-dir, Python-skip, and nil code-config cases.
  • init.go and init_from_code.go invoke validatePostInit after their respective init flows complete.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
cli/azd/extensions/azure.ai.agents/internal/cmd/init_validate.go New validation framework + .NET runtime vs TargetFramework check.
cli/azd/extensions/azure.ai.agents/internal/cmd/init_validate_test.go Unit tests for the new validator and its scenarios.
cli/azd/extensions/azure.ai.agents/internal/cmd/init.go Calls validatePostInit after template init completes (guarded by ContainerAgent assertion).
cli/azd/extensions/azure.ai.agents/internal/cmd/init_from_code.go Calls validatePostInit after from-code init completes.

Copy link
Copy Markdown
Collaborator Author

@v1212 v1212 left a comment

Choose a reason for hiding this comment

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

Code Review: LGTM

Clean, focused implementation of post-init .NET runtime validation.

Strengths:

  • Non-blocking design: Advisory-only output, never prevents init from completing
  • Defensive: Handles nil codeConfig, unreadable dirs, missing csproj, unparseable TargetFramework gracefully
  • Only checks first .csproj: Avoids confusion in multi-project dirs
  • Skips Python/non-dotnet: Early return on non-dotnet runtimes
  • Good regex: <TargetFramework>net(\d+)\.\d+</TargetFramework> correctly rejects netstandard/netcoreapp formats
  • Comprehensive tests: 9 test cases covering mismatch, match, higher runtime, no csproj, unreadable dir, python skip, nil config

Minor observations (non-blocking):

  1. Compiled regex per call: regexp.MustCompile in extractTargetFrameworkVersion is called each time. Could be a package-level var for slight efficiency, but since this runs once per init, negligible.

  2. TargetFrameworks (plural): If someone uses <TargetFrameworks>net9.0;net10.0</TargetFrameworks> (multi-target), the regex won't match. Acceptable since hosted agents always target a single TFM.

  3. Wired correctly in both flows: Template flow (init.go) checks agentManifest.Template.(agent_yaml.ContainerAgent) type assertion, from-code flow (init_from_code.go) uses localDefinition.CodeConfiguration directly. Both correct.

E2E Test Results (already in PR description):

7/7 tests passed runtime validation works correctly in real deployments.

Ship it! ���

Copy link
Copy Markdown
Member

@jongio jongio left a comment

Choose a reason for hiding this comment

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

Clean addition - advisory only, nil-safe, can't break existing flows. Two items below worth a look.

t.Fatal(err)
}

// Should not panic; prints ERROR to stdout (non-blocking)
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.

[MEDIUM] Tests verify no-panic but don't assert on output. A regression that silently drops or changes the ERROR/OK messages won't be caught.

Consider capturing stdout and checking it contains expected substrings (ERROR for mismatch, OK for match).

// e.g. "<TargetFramework>net10.0</TargetFramework>" -> 10
// Returns 0 if not found or not parsable.
func extractTargetFrameworkVersion(csprojContent string) int {
re := regexp.MustCompile(`<TargetFramework>net(\d+)\.\d+</TargetFramework>`)
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.

[LOW] Minor: this regex won't match TFMs with platform suffixes (net8.0-windows, net8.0-android). Unlikely for container-deployed agents, but if encountered, the user gets a "Could not parse" error instead of a graceful pass. net(\d+)\.\d+[^<]* would handle it.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

azd ai agent init: validate .NET runtime version against .csproj TargetFramework

3 participants