Conversation
…ar error azd's Aspire integration is C#-only (MSBuild IsAspireHost + `dotnet run --publisher manifest`). A polyglot AppHost (TypeScript/Python) is never recognized as Aspire, so azd falls through to a generic Oryx/buildpacks source build that produces confusing Docker/buildpack failures (issue #9335). Enhance the .NET AppHost detector to recognize polyglot (non-C#) Aspire AppHosts during app detection (init/up) and return an actionable ErrorWithSuggestion referencing #7138 instead of falling through. Detection prefers the explicit signal from aspire.config.json (appHost.language/path), and otherwise trusts well-known AppHost file names conservatively to avoid false positives (TypeScript apphost.mts/apphost.ts on their own; apphost.py only with a companion marker; go/java/rust only via aspire.config.json). Add telemetry (event aspire.apphost.unsupported + field aspire.apphost.language) to count how often users hit this, so we can size demand for polyglot support before implementing it. Update telemetry docs and schema/matrix/privacy-review specs accordingly. Refs #9335, #7138 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4ff538e7-5f90-4c60-a339-1a02257f6c5b
|
Azure Pipelines: Successfully started running 1 pipeline(s). 21 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Detects unsupported Aspire polyglot AppHosts early, replacing misleading build failures with actionable guidance and telemetry.
Changes:
- Adds conservative polyglot AppHost detection and error handling.
- Emits language telemetry for unsupported AppHosts.
- Adds tests, telemetry documentation, and spelling configuration.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
docs/specs/metrics-audit/telemetry-schema.md |
Documents the event and language field. |
docs/specs/metrics-audit/privacy-review-checklist.md |
Adds the language enum example. |
docs/specs/metrics-audit/feature-telemetry-matrix.md |
Maps app detection telemetry. |
docs/reference/telemetry-data.md |
Adds public telemetry references. |
cli/azd/internal/tracing/fields/fields.go |
Defines the language attribute. |
cli/azd/internal/tracing/events/events.go |
Defines the unsupported-AppHost event. |
cli/azd/internal/appdetect/dotnet_apphost.go |
Returns guidance and emits telemetry. |
cli/azd/internal/appdetect/aspire_polyglot.go |
Implements polyglot detection. |
cli/azd/internal/appdetect/aspire_polyglot_test.go |
Tests detection and error behavior. |
cli/azd/cmd/telemetry_test.go |
Checks the telemetry field constant. |
cli/azd/.vscode/cspell.yaml |
Adds Go spelling vocabulary. |
.vscode/cspell.misc.yaml |
Adds documentation vocabulary. |
- aspire_polyglot.go: preserve the relative apphost path from aspire.config.json (e.g. src/apphost.mts) instead of collapsing to filepath.Base; only case-resolve immediate children against the listing. - appdetect tests: assert the full relative path and add a subdirectory case; add an in-memory span-recorder test verifying the aspire.apphost.unsupported span and aspire.apphost.language attribute. - docs/reference/telemetry-data.md: add an App Detection row to the Feature -> Telemetry Mapping table documenting init/fresh-up-only scope. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
cli/azd/internal/appdetect/aspire_polyglot.go:85
- azd-code-reviewer: This only recognizes the new
aspire.config.jsonformat. The current Aspire CLI still loads and migrates valid legacy.aspire/settings.jsonconfigurations (whereappHostPathis relative to the.aspiredirectory), so a legacy Python AppHost withapphost.pyplus that explicit settings file—but nopylock.apphost.tomlorapphost_requirements.txt—still falls through to a generic source build; legacy Go/Java/Rust hosts are also missed. Read the legacy settings signal as a fallback and cover its path rebasing in tests.
// Strongest signal: aspire.config.json explicitly declares the AppHost language/path.
if configName, has := present[aspirePolyglotConfigFile]; has {
if lang, file := languageFromAspireConfig(filepath.Join(dir, configName), present); lang != "" {
return lang, filepath.Join(dir, file), true
}
}
CI cspell-lint flagged "buildpack" and "upvote" in the polyglot AppHost error message. Add file-scoped cspell overrides so the Go spell check passes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
📋 Prioritization NoteThanks for the contribution! The linked issue isn't in the current milestone yet. |
|
Azure Pipelines: Successfully started running 1 pipeline(s). 21 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (3)
cli/azd/internal/appdetect/aspire_polyglot.go:167
- azd-code-reviewer: Treating any
aspire.config.jsonas Python corroboration creates a false positive. A supported C# config such as theCSharpConfigIsNotPolyglotcase, combined with an unrelated root-levelapphost.py, falls through config parsing and is then reported as unsupported Python. A valid Python config already returns earlier, so only Python-specific companion files should count here.
if _, has := present[aspirePolyglotConfigFile]; has {
return true
}
cli/azd/internal/appdetect/aspire_polyglot.go:85
- azd-code-reviewer: This reads only the unified config, but the Aspire CLI still supports and migrates legacy
.aspire/settings.jsonfiles containingappHostPathandlanguage(dotnet/aspire,AspireConfigFile.LoadOrCreate). Those projects are also explicitly in #7138's detection signals. TypeScript happens to hit the filename fallback, but legacy Go/Java/Rust AppHosts always fall through to generic source detection because their filenames are intentionally excluded below. Read the legacy config when the unified file is absent, resolvingappHostPathrelative to the.aspiredirectory, and add coverage for that format.
// Strongest signal: aspire.config.json explicitly declares the AppHost language/path.
if configName, has := present[aspirePolyglotConfigFile]; has {
if lang, file := languageFromAspireConfig(filepath.Join(dir, configName), present); lang != "" {
return lang, filepath.Join(dir, file), true
}
}
docs/specs/metrics-audit/telemetry-schema.md:165
- azd-code-reviewer: The authoritative schema records the enum and hashing decision but omits the required measurement status.
cli/azd/AGENTS.md:277-279requires every new field row to state whether it is a measurement; mark this string enum explicitly as not a measurement.
| AppHost language | `aspire.apphost.language` | SystemMetadata | FeatureInsight | Fixed enum (`typescript`/`python`/`go`/`java`/`rust`); not hashed. Emitted on `aspire.apphost.unsupported`. |
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
jongio
left a comment
There was a problem hiding this comment.
Ran this locally against the PR branch. CI is green and the four earlier review comments are all genuinely addressed. Four things I hit while poking at the detector, one of which blocks a supported scenario.
hasPythonAppHostCompaniontreats anyaspire.config.jsonas Python corroboration, so a C# project plus a strayapphost.pyfailsazd init. Details inline.aspire.apphost.unsupportedfires twice perazd initwhen the AppHost sits undersrc/, which skews the very counts this event exists to produce.- The schema row is missing the measurement status that
cli/azd/AGENTS.md:277-279asks for. - JavaScript AppHosts get reported as
typescript.
One more that has no changed line to hang it on: javaDetector sits ahead of dotNetAppHostDetector in allDetectors (cli/azd/internal/appdetect/appdetect.go:186), and it claims any directory containing a pom.xml. An Aspire Java AppHost with a pom.xml gets picked up as a plain Java project before the polyglot check ever runs, so it still falls through to a source build. Worth confirming before you count on the java enum value showing up in telemetry.
- aspire_polyglot.go: when aspire.config.json authoritatively declares an AppHost that resolves to C# (or a non-polyglot target), treat it as authoritative and skip the file-name fallback. This prevents a supported C# Aspire layout with a sibling apphost.py/apphost.ts from being misreported as polyglot (which would hard-fail azd init/up). Also drop the redundant aspire.config.json clause from hasPythonAppHostCompanion, since a real Python config is resolved earlier. - Add regression tests for a C# config with sibling apphost.py / apphost.ts. - telemetry-schema.md: note the aspire.apphost.language field is not a measurement (per AGENTS.md schema-row requirements). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (3)
cli/azd/internal/appdetect/dotnet_apphost.go:81
- azd-code-reviewer: The prioritized
srcscan discards detection errors (internal/repository/app_init.go:51-56). For a polyglot AppHost undersrc, this error/span is therefore ignored; the root fallback either emits the event a second time, or detects a root project such aspackage.json, skipssrc, and never surfaces this actionable error. Propagate this typed error from thesrcscan (retry only a successful no-project result) soinit/freshupreliably fail once.
return nil, &errorhandler.ErrorWithSuggestion{
docs/reference/telemetry-data.md:719
- azd-code-reviewer: The “only” scope is inaccurate. This detector is part of global
allDetectors, andazd addcallsDetectDirectory(internal/cmd/add/add_configure_host.go:96); Python service packaging also calls it (pkg/project/container_helper.go:1203). Those paths emit the same event, including for some initialized projects. Document the shared-detector scope rather than limiting this toinitand freshup.
| **App Detection (Aspire polyglot)** | `aspire.apphost.unsupported` | `aspire.apphost.language` (`typescript`/`python`/`go`/`java`/`rust`) | How often an unsupported Aspire polyglot (non-C#) AppHost is encountered, by language. **Emitted only during app detection for `init` and fresh `up` (no existing `azure.yaml`)** — not for already-initialized projects, so absence does not mean zero unsupported AppHosts. |
docs/specs/metrics-audit/feature-telemetry-matrix.md:170
- azd-code-reviewer: This command inventory omits other call sites of the global detector. In particular,
azd addinvokesDetectDirectoryatinternal/cmd/add/add_configure_host.go:96, and Python service packaging invokes it atpkg/project/container_helper.go:1203, so this event is not restricted toinit/freshup. Include those scopes to keep the audit matrix aligned with code.
| **App detection** | `init`, `up` (fresh projects without `azure.yaml`, via `appdetect.Detect`) | `aspire.apphost.unsupported` | `aspire.apphost.language` (fixed enum — `typescript` / `python` / `go` / `java` / `rust`; not hashed) | Emitted from `internal/appdetect/dotnet_apphost.go` when an Aspire polyglot (non-C#) AppHost is detected; azd surfaces an actionable error referencing [#7138](https://github.com/Azure/azure-dev/issues/7138) instead of falling through to a generic source build |
jongio
left a comment
There was a problem hiding this comment.
cspell-lint is failing on this commit, which is what's holding up checkenforcer.
cli/azd/internal/appdetect/aspire_polyglot.go:126- unknown wordparseable
| // .NET path) or when no language could be resolved. | ||
| // - appHostFile: the relative AppHost path declared in the config (may include subdirectories, | ||
| // e.g. "src/apphost.mts"). | ||
| // - declared: whether the config authoritatively declares an AppHost (a readable, parseable |
There was a problem hiding this comment.
cspell-lint fails here, which is the only thing keeping CI red on 30e5f85:
internal/appdetect/aspire_polyglot.go:126:86 - Unknown word (parseable)
parseable isn't in cli/azd/.vscode/cspell.yaml, and the existing hits elsewhere in the tree don't cover it because ignorePaths skips **/*_test.go. Every non-test occurrence under cli/azd spells it parsable (pkg/tool/update_checker.go, pkg/templates/gh_source.go, pkg/extensions/registry_resolver.go, internal/telemetry/storage.go, pkg/infra/provisioning/bicep/interrupt.go), so matching that is a smaller change than adding a dictionary entry.
| // - declared: whether the config authoritatively declares an AppHost (a readable, parseable | |
| // - declared: whether the config authoritatively declares an AppHost (a readable, parsable |
Ran cspell@8.13.1 with the repo config both ways: parseable gives 1 issue, parsable gives 0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0d4eb6b8-1965-45c0-9b6c-649b2293320a
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (1)
cli/azd/internal/appdetect/aspire_polyglot.go:115
- azd-code-reviewer: Ranging over
presentmakes fallback selection nondeterministic when a directory contains multiple recognized AppHost files (for example,apphost.tsplusapphost.pyand a Python marker). This can change both the user-facing error andaspire.apphost.languagebetween runs, undermining the counting signal; Aspire'sDefaultLanguageDiscoverychecks TypeScript before Python deterministically. Check fallback filenames in a fixed priority order instead.
for fileName, original := range present {
lang, isAppHostFile := polyglotAppHostFileLanguage[fileName]
jongio
left a comment
There was a problem hiding this comment.
The cspell fix in 00a8a06 clears the last red check, and my earlier comments are all either fixed or answered. Approving.
Two non-blocking things I hit running the package locally, neither of which is red in CI today:
TestDotNetAppHostDetector_EmitsUnsupportedTelemetryonly passes on the first run in a process.-count=3fails runs 2 and 3.- The filename fallback ranges over a map, so it's order-dependent when a directory has two recognized AppHost files.
Details inline.
One thing to flag so nobody chases it: the BuildAndTest BuildCLI Linux failure on the previous commit isn't from this PR. That job is failing on main too (20e887d, 980665a, f2449f7).
|
Heads up on the red check: the only failing leg is
So a re-run of the Linux leg should clear checkenforcer. My approval still stands. |
Summary
Fixes the confusing failure in #9335, where
azd upon an Aspire TypeScript AppHost fails with obscure Docker/buildpack errors.Root cause: azd's Aspire integration is C#-only (it relies on MSBuild's
IsAspireHostanddotnet run --publisher manifest). A polyglot (non-C#) AppHost is never recognized as Aspire, so azd falls through to a generic Oryx/buildpacks source build — which then fails (e.g. on Docker Engine 29). The real problem isn't Docker or buildpacks; azd is simply ignoring the non-C# AppHost.This PR implements step 1 of the plan: detect polyglot AppHosts early and surface an actionable error, plus emit telemetry so we can size demand before investing in full polyglot support (#7138).
What changed
internal/appdetect/aspire_polyglot.go): the .NET AppHost detector now recognizes Aspire polyglot (non-C#) AppHosts during app detection (covers bothazd initandazd upfor fresh projects). It returns anErrorWithSuggestionreferencing Support Aspire polyglot (non-C#) AppHost projects in azd #7138 instead of falling through to a source build.aspire.config.json(appHost.language/appHost.path), mirroring the Aspire CLI's language discovery.apphost.mts/apphost.ts) is trusted on its own;apphost.pyrequires a companion marker (aspire.config.json,pylock.apphost.toml, orapphost_requirements.txt); go/java/rust are only detected viaaspire.config.json. A C# AppHost (.csprojpath, no language) is never misreported as polyglot.aspire.apphost.unsupportedwith fieldaspire.apphost.language(fixed enum, not hashed). Lets us count how many users hit this and which languages they want.telemetry-data.md,telemetry-schema.md,feature-telemetry-matrix.md, andprivacy-review-checklist.mdper AGENTS.md.ErrorWithSuggestion, and a telemetry field-constant subtest.Notes / scope
azure.yamlwith a scaffolded containerapp service) won't re-run app detection onazd up, so this primarily helps freshinit/up. That's sufficient to close [Issue] azd up fails with Docker Desktop / Docker Engine 29 when packaging Aspire TypeScript AppHost using Oryx/buildpacks #9335.Refs #9335, #7138
Telemetry Change Checklist
This PR adds one telemetry event (
aspire.apphost.unsupported) and one field(
aspire.apphost.language). Perdocs/specs/metrics-audit/privacy-review-checklist.md, aprivacy review is required for any new field/event.
New Fields
fields/fields.gowith correct classification and purpose (SystemMetadata/FeatureInsight)docs/specs/metrics-audit/telemetry-schema.mdtypescript/python/go/java/rust), not user-derivedNew Events
events/events.go(AspireUnsupportedAppHostEvent)docs/specs/metrics-audit/telemetry-schema.mdaspire.apphost.unsupported=prefix.noun.verb)Privacy
SystemMetadata)CustomerContentemitted in telemetryTesting
TestDotNetAppHostDetector_EmitsUnsupportedTelemetryuses an in-memory span recorder)TestTelemetryFieldConstants→AspireFieldssubtest)TestDetectAspirePolyglotAppHost,TestDotNetAppHostDetector_PolyglotReturnsSuggestionError)Downstream
Documentation
feature-telemetry-matrix.md— App detection cross-cutting subsystem)telemetry-schema.md— Aspire field + event rows)docs/reference/telemetry-data.md— event, field, and Feature → Telemetry Mapping row with command scope)privacy-review-checklist.md)