Detect an outdated Aspire VS Code extension in aspire doctor - #19128
Detect an outdated Aspire VS Code extension in aspire doctor#19128Adam Ratzman (adamint) wants to merge 43 commits into
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 19128Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 19128" |
There was a problem hiding this comment.
Pull request overview
Extends aspire doctor to detect outdated Aspire VS Code extensions through a bounded, anonymous Marketplace lookup.
Changes:
- Detects active extension roots, versions, channels, sources, and obsolete installations.
- Compares gallery installations with stable or pre-release Marketplace versions.
- Adds diagnostics, localization, documentation, and comprehensive tests.
Show a summary per file
| File | Description |
|---|---|
src/Aspire.Cli/Program.cs |
Registers the Marketplace client. |
src/Aspire.Cli/Utils/EnvironmentChecker/IVsCodeExtensionMarketplaceClient.cs |
Defines the Marketplace abstraction. |
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionMarketplaceClient.cs |
Queries and parses Marketplace versions. |
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs |
Detects installations and reports updates. |
src/Aspire.Cli/Resources/DoctorCommandStrings.resx |
Adds user-facing messages. |
src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs |
Exposes generated resource properties. |
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.cs.xlf |
Updates Czech localization data. |
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.de.xlf |
Updates German localization data. |
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.es.xlf |
Updates Spanish localization data. |
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.fr.xlf |
Updates French localization data. |
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.it.xlf |
Updates Italian localization data. |
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.ja.xlf |
Updates Japanese localization data. |
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.ko.xlf |
Updates Korean localization data. |
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.pl.xlf |
Updates Polish localization data. |
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.pt-BR.xlf |
Updates Brazilian Portuguese localization data. |
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.ru.xlf |
Updates Russian localization data. |
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.tr.xlf |
Updates Turkish localization data. |
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.zh-Hans.xlf |
Updates Simplified Chinese localization data. |
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.zh-Hant.xlf |
Updates Traditional Chinese localization data. |
tests/Aspire.Cli.Tests/Utils/TestVsCodeExtensionMarketplaceClient.cs |
Adds a configurable test client. |
tests/Aspire.Cli.Tests/Utils/MockHttpMessageHandler.cs |
Supports asynchronous HTTP responses. |
tests/Aspire.Cli.Tests/Utils/CliTestHelper.cs |
Wires the test Marketplace service. |
tests/Aspire.Cli.Tests/Commands/VsCodeExtensionMarketplaceClientTests.cs |
Tests requests, parsing, limits, and cancellation. |
tests/Aspire.Cli.Tests/Commands/VsCodeExtensionCheckTests.cs |
Covers detection and update decisions. |
tests/Aspire.Cli.Tests/Commands/DoctorCommandTests.cs |
Verifies human-readable and JSON output. |
tests/Aspire.Cli.EndToEnd.Tests/DoctorCommandTests.cs |
Isolates ambient VS Code installations. |
docs/specs/cli-output-formats.md |
Documents the expanded JSON contract. |
Review details
Files not reviewed (1)
- src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file
Suppressed comments (4)
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:594
- A valid JSON array containing a non-object entry (or a non-object
identifier) throwsInvalidOperationExceptionhere. SinceReadExtensionsIndexdoes not catch that exception, one malformed VS Code index entry causes the entire environment check to disappear rather than falling back to folder metadata.
if (!entry.TryGetProperty("identifier", out var identifier) ||
!identifier.TryGetProperty("id", out var id) ||
!string.Equals(id.GetString(), ExtensionId, StringComparison.OrdinalIgnoreCase) ||
!entry.TryGetProperty("relativeLocation", out var relativeLocationElement) ||
relativeLocationElement.ValueKind != JsonValueKind.String)
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionMarketplaceClient.cs:220
- A syntactically valid response with an unexpected shape (for example
[]or a null item inresults) makes theseTryGetProperty/GetStringcalls throwInvalidOperationException. That exception is intentionally treated as an implementation failure byVsCodeExtensionCheck, so the doctor row disappears instead of reporting Marketplace unavailability. Validate object/string kinds throughout the response or translate schema-shape failures toInvalidDataException.
if (root.TryGetProperty("results", out var results) &&
results.ValueKind == JsonValueKind.Array)
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:676
- A syntactically valid but non-object
package.json(for example[]) makesTryGetPropertythrowInvalidOperationException, bypassing the intended fallback to the folder version and dropping the doctor check. Guard the root kind before readingversion.
return document.RootElement.TryGetProperty("version", out var versionElement) &&
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:534
- The index at the extension root is only the default VS Code profile's manifest; named profiles store their own
extensions.jsonunder profile user data. If Aspire is installed only in the active named profile, the folder is found but its source/release-channel metadata is absent, soShouldCheckMarketplacealways suppresses the new update check. Conversely, a copy belonging only to another profile can be reported as active.
var indexPath = Path.Combine(extensionsDirectory, "extensions.json");
- Files reviewed: 26/27 changed files
- Comments generated: 2
- Review effort level: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
There was a problem hiding this comment.
Review details
Files not reviewed (1)
- src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file
Suppressed comments (5)
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:558
extensionsDirectory/extensions.jsonis only VS Code's default-profile manifest; non-default profiles have separateextensions.jsonresources while sharing the same physical extension folders. In a custom profile this can therefore attach default-profile source/channel metadata—or report an extension as installed at all—even when the running profile does not load it, producing an incorrect Marketplace warning. Resolve the active profile's manifest before trusting this metadata, or suppress the comparison when the active profile cannot be identified.
var indexPath = Path.Combine(extensionsDirectory, "extensions.json");
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:632
- This uses
metadata.isPreReleaseVersion, which describes the currently installed artifact, not the update channel selected by the user. VS Code persists that selection inmetadata.preReleaseand usesextension.preReleasefor its own update queries. When a pre-release subscription temporarily has a stable artifact (for example, due to compatibility fallback), doctor compares against the stable feed and can miss the available pre-release update. ParsepreReleasefor channel selection, usingisPreReleaseVersiononly as an artifact property/fallback.
if (metadata.TryGetProperty("isPreReleaseVersion", out var preReleaseElement) &&
preReleaseElement.ValueKind is JsonValueKind.True or JsonValueKind.False)
{
isPreReleaseVersion = preReleaseElement.GetBoolean();
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionMarketplaceClient.cs:70
- A syntactically valid response with an unexpected JSON shape (for example,
[], or a non-object entry inresults) makes theTryGetProperty/GetStringcalls inParseVersionsthrowInvalidOperationException.VsCodeExtensionCheckdoes not classify that as Marketplace unavailability, soEnvironmentCheckersilently drops the entire check instead of emitting the documented warning. Translate malformed-shape failures toInvalidDataException(or validate everyValueKind) so they follow the unavailable path.
return ParseVersions(responseBytes);
tests/Aspire.Cli.Tests/Commands/DoctorCommandTests.cs:149
- Repository test guidance explicitly avoids
Assert.DoesNotContainfor generated/structured output because it can pass while the rest of the output regresses (AGENTS.md:267-268). Verify the complete rendered output, preferably with a Verify snapshot, so this test pins both the redaction and the surrounding doctor result.
Assert.DoesNotContain(rawFailure, rendered, StringComparison.Ordinal);
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:482
- Selecting the highest physical folder does not identify the version loaded by a VS Code profile. Profiles share the extension directory and their manifests reference a specific
relativeLocation/version, so another profile can legitimately keep a newer copy that is not active here. This can publish the wrong installed version or suppress the Marketplace check because the selected folder has no metadata in the current manifest. When a profile manifest is available, select its Aspire entry rather than the maximum folder version; only use folder ordering as a legacy fallback.
var selectedExtension = installedExtensions
.Where(extension => extension.Version is not null)
.OrderByDescending(
extension => extension.Version,
SemVersionPrecedenceComparer.Instance)
.FirstOrDefault()
- Files reviewed: 27/28 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
There was a problem hiding this comment.
Review details
Files not reviewed (1)
- src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file
Suppressed comments (5)
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:256
- Because this environment variable is introduced by this PR, every extension version that is already outdated lacks it. In that upgrade scenario detection only finds the on-disk folder,
CheckAsynctakes the no-version pass path, and the Marketplace is never queried, so the new CLI cannot flag the pre-feature versions this change is meant to detect. Retain a disk-version fallback when one active root can be identified, using this variable only as the higher-authority signal.
var reportedVersion = environment.GetEnvironmentVariable(ExtensionVersionEnvironmentVariable);
if (!string.IsNullOrWhiteSpace(reportedVersion))
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:144
- A VSIX or sideloaded build also contributes its
packageJSON.version, so this gate queries the Marketplace for those installs and can tell users of an older private/local build to replace it. That contradicts the stated contract that non-gallery installs have no actionable Marketplace counterpart. Carry install-source provenance from the extension or disk detection and requiregallerybefore querying.
if (!updateCheckEnabled ||
!SemVersion.TryParse(detection.ExtensionVersion, SemVersionStyles.Strict, out var installedVersion))
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionMarketplaceClient.cs:70
- Marketplace JSON is external input, but
ParseVersionscallsJsonElement.GetString()without consistently checkingValueKind. A valid JSON response such as a numericextensionName,publisherName, property key, or property value throwsInvalidOperationException;VsCodeExtensionCheckdoes not handle that exception, soEnvironmentCheckersilently drops this check instead of returning the documented unavailable warning. Normalize malformed-shape exceptions toInvalidDataException.
return ParseVersions(responseBytes);
extension/src/extension.ts:84
- The new activation-to-child-process contract is only tested by calling the helper with a fake collection; the CLI tests then inject the variable manually. No extension E2E test proves that activating the real extension makes the version visible in an integrated terminal, so the feature's sole version signal can regress without either test suite noticing. Add an extension E2E test that activates the extension, opens a terminal, and verifies this environment value or the resulting
aspire doctorbehavior.
syncAspireExtensionVersionEnvironment(context.environmentVariableCollection, context.extension.packageJSON?.version);
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:187
- The release channel cannot be inferred from SemVer here. VS Code publishes pre-releases via a Marketplace flag and officially recommends ordinary odd-minor versions (for example
1.15.0), while this repository's publish path usesvsce --pre-release(eng/pipelines/release-publish-nuget.yml:2288-2291). Such an installed pre-release hasIsPrerelease == falseand is compared with the stable feed, so an outdated pre-release can be reported current. Propagate the actual Marketplace pre-release metadata and skip lookup when the channel is unknown.
var channel = installedVersion.IsPrerelease ? PreReleaseChannel : StableChannel;
var latestVersion = installedVersion.IsPrerelease ? versions.PreReleaseVersion : versions.StableVersion;
- Files reviewed: 32/33 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
There was a problem hiding this comment.
Review details
Files not reviewed (1)
- src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file
Suppressed comments (4)
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:152
- Nothing before this request establishes that the running extension came from the Marketplace. VSIX and sideloaded builds also execute the new activation code and contribute a parseable manifest version, so an older local build will be queried and receive an actionable “update from Marketplace” warning. This contradicts the PR’s stated gallery-only gating; carry the installation source with the extension signal (or determine it reliably) and skip the lookup for non-gallery installs.
VsCodeExtensionMarketplaceVersions versions;
try
{
versions = await _marketplaceClient.GetLatestVersionsAsync(cancellationToken);
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionMarketplaceClient.cs:223
- A syntactically valid but structurally malformed Marketplace response can throw
InvalidOperationExceptionhere (TryGetPropertyrequires an object), and the same applies to nestedresult,candidate, and property elements.VsCodeExtensionCheckonly translates JSON/I/O/HTTP failures, soEnvironmentCheckerswallows this exception and the entire check disappears instead of reporting the documented unavailable warning. Validate eachValueKindand convert shape violations toInvalidDataException.
private static bool TryFindExtension(JsonElement root, out JsonElement extension)
{
if (root.TryGetProperty("results", out var results) &&
results.ValueKind == JsonValueKind.Array)
{
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:187
IsPrereleasecannot identify actual Marketplace pre-release installs here.extension/Extension.proj:54publishes them with VSCE’s--pre-releaseflag while the manifest version remains an ordinaryx.y.z, so the contributedpackageJSON.versionis not a SemVer prerelease and this always selectsstable. Such users will never be compared with newer pre-release versions. Propagate the Marketplace channel separately from the version instead of inferring it from SemVer.
var channel = installedVersion.IsPrerelease ? PreReleaseChannel : StableChannel;
var latestVersion = installedVersion.IsPrerelease ? versions.PreReleaseVersion : versions.StableVersion;
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:407
- This folder-name-only test never consults the extensions root’s
.obsoletefile. Consequently, an extension folder that VS Code has marked obsolete and is awaiting deletion is still reported as installed, despite the PR description explicitly promising those entries are skipped. Load the obsolete folder names for each root and exclude them before accepting a match.
return folderName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) &&
folderName.Length > prefix.Length &&
char.IsAsciiDigit(folderName[prefix.Length]);
- Files reviewed: 32/33 changed files
- Comments generated: 1
- Review effort level: Balanced
The npm subprocess was spawned and then configured to behave exactly like a plain HTTPS GET. RemoveAmbientNpmConfiguration stripped NPM_CONFIG_*, empty --userconfig/--globalconfig files replaced the user and global layers, a marker package.json pinned the local prefix, and --cache pointed at a throwaway directory. Everything npm could contribute over an HttpClient was deliberately disabled, and the process plumbing that remained produced the scoped-registry, inherited-json, unbounded-Dispose, and descendant-pipe-retention defects raised on this change. Replace it with INpmRegistryClient: one GET of the package document, read dist-tags.latest, parse. This follows SigstoreNpmProvenanceChecker in the same folder, which already reads registry.npmjs.org with an injected HttpClient, and matches the client shape in microsoft#19128 so the two merge mechanically. Target the public npm registry rather than the dnceng mirror. The advertised remedy is "npm install -g @microsoft/aspire-cli@latest", which npm resolves through the user's own registry configuration, defaulting to public npm. The mirror is a downstream cache that only Playwright acquisition reads, so checking it made the notification a lagging proxy for a source the update never touches. Removing the subprocess also removes the npm-on-PATH requirement: the update check no longer warns on machines without the Node toolchain, and CliUpdateNotifier no longer needs IDisposable or a blocking shutdown drain. The release-pipeline mirror seeding gate is no longer needed by this change and moves to its own branch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
There was a problem hiding this comment.
Review details
Files not reviewed (1)
- src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file
Suppressed comments (6)
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:426
- This does the opposite of the PR's stated platform-suffix handling: it rejects
1.2.3-darwin-arm64as a pre-release instead of stripping the target-platform suffix before parsing. If a valid platform-specific installation has a missing or temporarily unreadable manifest, doctor emits an unknown-version warning rather than recovering stable version1.2.3. Strip recognized VS Code target-platform suffixes before the fallback parse.
if (SemVersion.TryParse(versionSegment, SemVersionStyles.Strict, out var folderVersion) &&
!folderVersion.IsPrerelease &&
folderVersion.Metadata.Length == 0)
src/Aspire.Cli/Utils/VsCodeInstallLayout.cs:71
- The active-root resolution described by the PR is missing here. Apart from
VSCODE_EXTENSIONS, this ignoresVSCODE_AGENT_FOLDER, the remote IPC/client signals, and the desktop askpass path, then scans every default root. With desktop and remote installs present, doctor can therefore compare an inactive version instead of treating the installation as ambiguous. Resolve the authoritative signals first and represent multiple matching fallback roots as unknown.
var overrideDirectory = environment.GetEnvironmentVariable("VSCODE_EXTENSIONS");
if (!string.IsNullOrWhiteSpace(overrideDirectory))
{
yield return overrideDirectory;
yield break;
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:177
- This lookup runs for every parseable installed version because detection carries no gallery/VSIX source. Consequently, an older sideloaded or locally built extension is compared with the Marketplace and receives an update warning even though it has no Marketplace counterpart, contrary to the stated query gating. Track the installation source and only query for an identifiable gallery install.
This issue also appears on line 424 of the same file.
VsCodeExtensionMarketplaceVersions versions;
try
{
versions = await _marketplaceClient.GetLatestVersionsAsync(cancellationToken);
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:394
- These candidates are yielded without consulting the root's
.obsoletefile. VS Code records superseded or pending-deletion extension folders there, so an uninstalled/obsolete Aspire folder can still be reported as installed and compared. Load the obsolete entries for each root and exclude matching folders before resolving versions.
if (IsVersionedExtensionFolder(Path.GetFileName(current)))
{
yield return current;
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionMarketplaceClient.cs:185
- Structurally valid JSON with an unexpected shape is not handled as an unavailable Marketplace response. For example, a top-level
[], a non-objectresultsentry, or a non-string publisher causesTryGetProperty/GetStringto throwInvalidOperationException;VsCodeExtensionCheckdoes not catch that type, soEnvironmentCheckersilently drops the entire row instead of returning the documented warning. Validate element kinds throughout parsing or normalize shape errors toInvalidDataException.
using var document = JsonDocument.Parse(responseBytes);
if (!TryFindExtension(document.RootElement, out var extension))
tests/Aspire.Cli.EndToEnd.Tests/DoctorCommandTests.cs:165
- The isolation command leaves the newly introduced
ASPIRE_VSCODE_EXTENSION_VERSIONuntouched. When these E2E tests are launched from an Aspire-extension-created environment, detection trusts that ambient value before inspecting the empty override root and contacts the live Marketplace, defeating this helper's determinism. Unset the version variable here as well.
"""mkdir -p "$PWD/.doctor-vscode-extensions" && export VSCODE_EXTENSIONS="$PWD/.doctor-vscode-extensions" && unset VSCODE_AGENT_FOLDER VSCODE_GIT_ASKPASS_NODE VSCODE_GIT_ASKPASS_MAIN VSCODE_CLIENT_COMMAND""",
- Files reviewed: 32/33 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
Detect the installed Aspire VS Code extension version, ignore obsolete installs, and compare it with the latest stable Marketplace version. Keep installed-extension checks passing when Marketplace lookup fails while surfacing diagnostic details and metadata. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ffeff87e-f284-434d-87d3-843e21a7aebb
Only compare against the Marketplace when the installed extension can be attributed to a single active extension root, a gallery source, and a known release channel, and honor the update-notification feature switch. Compare pre-release installs against the pre-release feed instead of the stable one. Fix the Marketplace request itself: the gallery is an Azure DevOps service and rejects a query with HTTP 400 unless the Accept header names an API version, so the lookup previously never succeeded. Drop the X-Market-Client-Id and X-Market-User-Id headers, which the anonymous query does not need and which would have made the CLI emit a VS Code installation identifier. Report a warning with diagnostic details when the lookup times out or is unavailable, and document the resulting JSON contract. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 32c21d87-2f53-4647-bf1c-c5ca64abf14a
Address two review findings on the doctor VS Code extension check. The Marketplace client requests with HttpCompletionOption.ResponseHeadersRead, so the response body is still streaming after SendAsync returns. The private timeout translation only wrapped SendAsync, so a server that answered with headers and then stalled cancelled the body read with the timeout token and surfaced a bare OperationCanceledException. Doctor drops a check on cancellation, so the documented timeout warning was never reported. Move the body read and parse inside the same translation. Extension root detection also ignored portable mode. VS Code resolves the extension root as --extensions-dir, VSCODE_EXTENSIONS, VSCODE_PORTABLE/extensions, then the home data folder, so a portable install fell through to the home-directory defaults and doctor either reported the extension missing or compared an unrelated installation against the Marketplace. Probe VSCODE_PORTABLE after VSCODE_EXTENSIONS and before every default root. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Threads a reported extension source through the doctor test service setup and asserts the JSON check metadata surfaces it, so the marketplace-vs- sideloaded distinction is covered rather than only version and channel. Recovered from an uncommitted working tree after the authoring session was killed by a CLI out-of-memory crash on 2026-08-11. The worktree was on a detached HEAD, so the work is preserved on a named branch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f1bd51d5-cb9f-4370-a07b-c94416baad61
There was a problem hiding this comment.
Review details
Files not reviewed (1)
- src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file
Suppressed comments (5)
docs/specs/cli-output-formats.md:488
- This metadata contract omits
extensionSourceand incorrectly sayslatestVersionKnownis added by a Marketplace lookup. The implementation emits the source with any discovered version and setslatestVersionKnown: falsebefore the unknown-channel/source gates, even when no lookup occurs. Document those fields at the points where they are actually emitted.
Its `metadata` always exposes `vsCodeInstalled` (bool), `extensionInstalled` (bool), and `extensionId` (string). An installed extension can add `extensionVersion` (string), `extensionVersionKnown` (bool), and `extensionChannel` (`stable`, `prerelease`, or `unknown`). A Marketplace lookup adds `latestVersionKnown` (bool). A successful applicable comparison also adds `latestVersion` (string), `latestVersionChannel` (`stable` or `prerelease`), and `updateAvailable` (bool); an unavailable lookup adds `latestVersionError` with the value `unavailable`.
docs/specs/cli-output-formats.md:490
- This lists only two contributed signals, but the extension also contributes
ASPIRE_VSCODE_EXTENSION_SOURCE, and doctor relies on it to suppress Microsoft Marketplace requests and links for VSCodium, Code - OSS, and unknown galleries. Documenting only version/channel leaves out a required part of the protocol.
The Aspire VS Code extension contributes `ASPIRE_VSCODE_EXTENSION_VERSION` and `ASPIRE_VSCODE_EXTENSION_CHANNEL` to terminals created by VS Code. Those signals identify the active extension instance and select the matching Marketplace channel; Marketplace prerelease versions use ordinary `major.minor.patch` versions, so the version string alone cannot identify the channel. When the signals are unavailable, doctor falls back to the existing known extension roots and reads the version from `package.json` or the versioned extension folder, but reports the channel as `unknown`.
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionCheck.cs:214
- The disk fallback cannot find pre-signal VSCodium/Code - OSS installs because
GetExtensionDirectoriesonly scans the.vscode*roots. VSCodium stores extensions under~/.vscode-oss/extensions, while its integrated terminal still setsTERM_PROGRAM=vscode; this branch therefore reports the extension as missing and returns the Microsoft Marketplace link instead of preserving an installed/unknown result. Include the OSS roots while keeping their source unknown, or otherwise avoid the Marketplace link when product identity is unavailable.
docs/specs/cli-output-formats.md:469 - The documented JSON example omits
extensionSource, althoughBuildMetadataalways emits it wheneverextensionVersionis present and the command-level test asserts it. Add the field so consumers copying this output contract see the actual shape.
This issue also appears in the following locations of the same file:
- line 488
- line 490
"extensionChannel": "stable",
"latestVersion": "1.16.0",
src/Aspire.Cli/Utils/EnvironmentChecker/VsCodeExtensionMarketplaceClient.cs:28
- The Marketplace response parsing is covered, but the request test never inspects this payload. A regression in the extension ID, target, exclusion filter, or
65584flags would make the live service stop returning the stable/prerelease pair while all current tests still pass. ExtendGetLatestVersionsAsync_SendsTheAnonymousMarketplaceQueryto assert the JSON criteria and flags sent here.
- Files reviewed: 40/41 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8dfcd7cf-8ebb-4393-84ba-f53927dcb1ab # Conflicts: # .github/workflows/tests.yml # extension/scripts/run-e2e.js # extension/src/extension.ts # extension/src/testing/e2eStateFileBridge.ts # extension/webpack.config.js
Account for the VS Code extension pre-release DefinePlugin while preserving the production E2E bridge gate, and assert the local E2E package carries both required build markers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8dfcd7cf-8ebb-4393-84ba-f53927dcb1ab
There was a problem hiding this comment.
Review details
Files not reviewed (1)
- src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file
Suppressed comments (4)
docs/specs/cli-output-formats.md:490
- The metadata contract and environment-signal list omit the new
extensionSourcefield andASPIRE_VSCODE_EXTENSION_SOURCEvariable, even though they gate Marketplace lookup and are emitted for every detected version. Document the field'smicrosoft-marketplace/other/unknownvalues and the third signal.
Its `metadata` always exposes `vsCodeInstalled` (bool), `extensionInstalled` (bool), and `extensionId` (string). An installed extension can add `extensionVersion` (string), `extensionVersionKnown` (bool), and `extensionChannel` (`stable`, `prerelease`, or `unknown`). A Marketplace lookup adds `latestVersionKnown` (bool). A successful applicable comparison also adds `latestVersion` (string), `latestVersionChannel` (`stable` or `prerelease`), and `updateAvailable` (bool); an unavailable lookup adds `latestVersionError` with the value `unavailable`.
The Aspire VS Code extension contributes `ASPIRE_VSCODE_EXTENSION_VERSION` and `ASPIRE_VSCODE_EXTENSION_CHANNEL` to terminals created by VS Code. Those signals identify the active extension instance and select the matching Marketplace channel; Marketplace prerelease versions use ordinary `major.minor.patch` versions, so the version string alone cannot identify the channel. When the signals are unavailable, doctor falls back to the existing known extension roots and reads the version from `package.json` or the versioned extension folder, but reports the channel as `unknown`.
extension/src/utils/cliPathEnvironment.ts:273
- This still classifies official VS Code as Microsoft Marketplace even when VS Code is configured to use a private gallery. Current VS Code registers
extensions.gallery.serviceUrland uses a non-empty value instead of the product's default gallery, so this path causesaspire doctorto query and link to Microsoft Marketplace despite the source guard. Include the configured gallery in the source classification and treat a non-Microsoft override asother/unknown.
// VS Code does not expose its configured extension gallery URL. Require the matching
// Microsoft product name and URI scheme so forks and side-loaded Code - OSS builds do not
// direct users to the Microsoft Marketplace.
return appName === 'Visual Studio Code' && uriScheme === 'vscode'
|| appName === 'Visual Studio Code - Insiders' && uriScheme === 'vscode-insiders';
.github/workflows/tests.yml:648
- This marker is scoped only to the first package step. The production step below also passes
--pre-release, but without this variable webpack bakesstableinto that VSIX, so the artifact intended to represent the shipping production bundle has a prerelease manifest and a stable reported identity. Pass the marker to the production step while keeping only the E2E bridge opt-in scoped to this step.
ASPIRE_VSCODE_EXTENSION_PACKAGE_PRERELEASE: 'true'
docs/specs/cli-output-formats.md:468
- The documented outdated-extension JSON omits
extensionSource, although this path always emits it and requiresmicrosoft-marketplaceto perform the shown comparison. Include the field so the example matches the output contract.
This issue also appears on line 488 of the same file.
"extensionChannel": "stable",
- Files reviewed: 41/42 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Resolved six conflicts. The central one is that this branch threads an AspireExtensionEnvironment through the terminal provider and MCP server constructors while main threads a CliPathResolver through the same parameter slot. The two are disjoint contributions to the same child environment (ASPIRE_VSCODE_EXTENSION_* versus AspireCliPath), so both are kept rather than picking a side. - AspireTerminalProvider: constructor now takes the resolver and the extension environment. - AspireMcpServerDefinitionProvider: createAspireMcpServerDefinition takes the extension environment as a trailing parameter so main's existing call sites keep their argument order, and the produced environment composes the identity overrides with a forwardable AspireCliPath. - cliSpawn/mcpServerDefinition tests: kept both sides' tests. The identity tests now stub path probing instead of relying on the real filesystem, so the expected environment no longer depends on the host.
sinon.createStubInstance replaces prototype methods but leaves accessors alone, so a bare stub still ran the real aspireExtensionEnvironment getter. That getter delegates to the terminal provider, which a stub instance never receives, so the five AppHost launch tests that reach prepareDebugSession threw while building the debug configuration. Add a shared createFakeAspireDebugSession helper that shadows the accessor with an own property, and use it wherever the dotnet debugger tests build a session double so a future AppHost test does not rediscover this.
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| const extensionEnvironment = getAspireExtensionEnvironment(context.extension.packageJSON, { | ||
| appName: vscode.env.appName, | ||
| uriScheme: vscode.env.uriScheme, | ||
| extensionGalleryServiceUrl: vscode.workspace.getConfiguration().get<unknown>('extensions.gallery.serviceUrl'), |
Description
Teach
aspire doctorto distinguish a current, outdated, missing, and unknown-version Aspire VS Code extension.The running extension contributes its authoritative version, stable/prerelease channel, and product/gallery source to terminals, tasks, debug processes, and stdio MCP servers so doctor can inspect the instance VS Code actually activated. Packaged builds derive the channel from the VSIX manifest, including offline stable and prerelease packages. The source signal prevents VSCodium, code-oss, and unknown galleries from triggering Microsoft Marketplace lookup or links.
MCP definitions pass only the
ASPIRE_CLI_*identity overrides, leaving inheritedPATHand unrelated environment values out of VS Code's cached server definition. A reported active-extension version is sufficient to run the check even when a remote or desktop extension host has nocodelauncher or VS Code terminal marker.Outside an extension-host terminal, doctor falls back to installed extension directories and reports the channel as unknown rather than guessing or making an unusable Marketplace request.
The check now:
Validation
Checklist