feat(agents): validate .NET runtime vs TargetFramework after init#8222
feat(agents): validate .NET runtime vs TargetFramework after init#8222v1212 wants to merge 1 commit into
Conversation
…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
b9efd83 to
5011eaf
Compare
There was a problem hiding this comment.
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.gowithvalidatePostInitframework andvalidateDotnetRuntimeVsCsprojcheck, including a simple<TargetFramework>netX.Y</TargetFramework>regex parser. - New
init_validate_test.gocovering match, mismatch, higher-runtime, no-csproj, unreadable-dir, Python-skip, andnilcode-config cases. init.goandinit_from_code.goinvokevalidatePostInitafter 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. |
v1212
left a comment
There was a problem hiding this comment.
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):
-
Compiled regex per call:
regexp.MustCompileinextractTargetFrameworkVersionis called each time. Could be a package-levelvarfor slight efficiency, but since this runs once per init, negligible. -
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. -
Wired correctly in both flows: Template flow (init.go) checks
agentManifest.Template.(agent_yaml.ContainerAgent)type assertion, from-code flow (init_from_code.go) useslocalDefinition.CodeConfigurationdirectly. Both correct.
E2E Test Results (already in PR description):
7/7 tests passed runtime validation works correctly in real deployments.
Ship it! ���
jongio
left a comment
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
[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>`) |
There was a problem hiding this comment.
[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.
Summary
validatePostInit) that runs afterazd ai agent initcompletesTargetFrameworkprints a clear ERROR (red) with fix instructions if target > runtime (which will fail at build/deploy time)init.go) and from-code flow (init_from_code.go)Changes
init_validate.goinit_validate_test.goinit.govalidatePostInitafter template init completesinit_from_code.govalidatePostInitafter from-code init completesCloses #8202
E2E Test Results (Manual Interactive Testing)
All tests run interactively using only
azd ai agent init+azd deploy(no file modifications).7/7 passed PR feature (runtime validation) works correctly, no regressions in init/deploy/invoke flows.