Report an undetermined WSL version instead of asserting WSL2 - #19158
Report an undetermined WSL version instead of asserting WSL2#19158Adam Ratzman (adamint) merged 10 commits into
Conversation
The WSL check classified the version by parsing a kernel major version out of /proc/version and treating anything >= 4 as WSL2. WSL1 has no kernel of its own and always reports a fixed 4.4.0 compatibility banner, so that comparison classified every real WSL1 system as WSL2. The check then emitted a green "WSL2 environment detected" row, which meant the limited-container-support warning the check exists to surface never fired for the users who needed it. The same method also collapsed two distinct unknowns into confident answers. A missing, unreadable, or unrecognized /proc/version fell through to WSL2 and passed, while a Microsoft banner whose version could not be parsed fell through to WSL1 and told the user to upgrade to a version they may already run. Classify from the markers each version actually writes: WSL2 kernels carry a "WSL2" suffix, and WSL1 carries the 4.4.0 "-Microsoft" compatibility banner. Anything else, including a custom kernel configured through .wslconfig, is now reported as a distinct warning naming the file that could not be classified, so doctor never asserts an environment it was unable to observe. The banner read moves behind a constructor seam so the classification can be tested without the host being WSL. Reverting the classification fails 7 of the 15 new tests, including a real WSL1 banner reported as WSL2 and an unreadable banner reported as pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 19158Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 19158" |
There was a problem hiding this comment.
Pull request overview
Corrects aspire doctor WSL detection to avoid misreporting WSL1 or unknown environments as WSL2.
Changes:
- Adds three-state WSL classification and actionable warnings.
- Introduces injectable kernel-banner reading.
- Adds 15 focused regression tests.
Show a summary per file
| File | Description |
|---|---|
src/Aspire.Cli/Utils/EnvironmentChecker/WslEnvironmentCheck.cs |
Implements marker-based WSL classification. |
tests/Aspire.Cli.Tests/Commands/WslEnvironmentCheckTests.cs |
Covers WSL1, WSL2, unknown, and non-WSL scenarios. |
Review details
Suppressed comments (2)
src/Aspire.Cli/Utils/EnvironmentChecker/WslEnvironmentCheck.cs:159
- The WSL1 matcher is broader than the fixed marker it is intended to recognize: it accepts every
4.4.xrelease, while the separate check on line 132 allowsMicrosoftto occur anywhere in compiler/build metadata. For example,Linux version 4.4.1-custom (Microsoft@builder)is reported as WSL1 instead of unknown. Match the complete4.4.0-<build>-Microsoftrelease token so custom/native kernels are not given a confident WSL1 warning.
[GeneratedRegex(@"Linux\s+version\s+4\.4\.", RegexOptions.IgnoreCase)]
src/Aspire.Cli/Utils/EnvironmentChecker/WslEnvironmentCheck.cs:121
- This searches the entire
/proc/versionpayload, not the kernel release token described above. Build metadata follows that token, so a native/custom banner such asLinux version 6.1.0-custom (root@WSL2-builder) ...is classified as WSL2 and produces a passing row even though no WSL2 kernel marker was observed. Restrict the match to the release token afterLinux version.
if (procVersion.Contains("WSL2", StringComparison.OrdinalIgnoreCase))
- Files reviewed: 2/2 changed files
- Comments generated: 1
- 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>
There was a problem hiding this comment.
Review details
Suppressed comments (1)
src/Aspire.Cli/Utils/EnvironmentChecker/WslEnvironmentCheck.cs:123
- Microsoft's WSL kernel release notes list the 4.19 WSL2 releases as
4.19.84-microsoft-standardthrough4.19.128-microsoft-standard; those banners do not contain the literalWSL2. This condition therefore classifies genuine early WSL2 installations asUnknownand emits an unnecessary warning/upgrade command. Recognize themicrosoft-standardmarker as WSL2 as well, and cover an actual 4.19 banner in the regression cases.
// WSL 2 runs a genuine Microsoft-built kernel whose release string carries a "WSL2" marker:
// Linux version 5.15.90.1-microsoft-standard-WSL2 (oe-user@oe-host) (...) #1 SMP ...
if (procVersion.Contains("WSL2", StringComparison.OrdinalIgnoreCase))
{
return WslVersion.Wsl2;
- Files reviewed: 6/6 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. |
|
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: 487b899d-d79d-4349-a84b-a026861bde7f
There was a problem hiding this comment.
Review details
Suppressed comments (2)
src/Aspire.Cli/Utils/EnvironmentChecker/ContainerRuntimeCheck.cs:104
- Treating a blank primary variable as absent makes
doctordisagree with the runtime it diagnoses.DcpOptionsreads these keys viaGetString(..., fallbackOnEmpty: false)(src/Aspire.Hosting/Dcp/DcpOptions.cs:311), so an empty primary value suppresses the legacy value and defaults DCP to Docker, while whitespace is passed to DCP as the configured runtime (DcpHost.cs:304-307). With the new fallback, this check can instead report the legacy runtime (for example, Podman) as active. Align blank-value handling in the shared/AppHost configuration path and this check so the reported runtime matches what Aspire will launch.
var configuredRuntime = environment.GetEnvironmentVariable("ASPIRE_CONTAINER_RUNTIME");
if (!string.IsNullOrWhiteSpace(configuredRuntime))
{
return configuredRuntime;
}
src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs:565
- This condition routes whitespace to the “unset” branch, but that branch still emits
$SSL_CERT_DIR:. WithSSL_CERT_DIR=' ', executing the recommendation therefore preserves the whitespace value as the first certificate-directory entry, contrary to the stated goal of treating it as unset. Omit the expansion in the unset branch and update the new test expectation accordingly.
if (!string.IsNullOrWhiteSpace(currentSslCertDir))
- Files reviewed: 6/6 changed files
- Comments generated: 0 new
- Review effort level: Balanced
There was a problem hiding this comment.
Review details
Suppressed comments (3)
src/Aspire.Cli/Utils/EnvironmentChecker/ContainerRuntimeCheck.cs:101
- Treating a blank primary value as absent here makes
aspire doctordisagree with the AppHost runtime.DcpOptionsresolves these keys withconfiguration.GetString(...)withoutfallbackOnEmpty(src/Aspire.Hosting/Dcp/DcpOptions.cs:311), so an empty primary value suppresses the legacy value and whitespace is passed to DCP. In these cases doctor now reports the legacy runtime as configured even though the application will use the default runtime or an invalid whitespace value. Align the shared runtime resolution semantics before applying this fallback in doctor.
if (!string.IsNullOrWhiteSpace(configuredRuntime))
src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs:565
- This condition selects the “unset” branch for whitespace, but that branch still emits
$SSL_CERT_DIR:. Running the recommendation therefore preserves the whitespace value as a relative certificate directory instead of actually treating it as unset; an empty value also leaves a leading empty path component. Build the unset command solely from the detected system directories and dev-certs path, and update the expectation accordingly.
if (!string.IsNullOrWhiteSpace(currentSslCertDir))
src/Aspire.Cli/Utils/EnvironmentChecker/WslEnvironmentCheck.cs:169
- The WSL1 marker is documented here as the fixed
4.4.0-<build>-Microsoftrelease, but this regex accepts anyLinux version 4.4.*as long asMicrosoftappears anywhere else in the banner. For example,Linux version 4.4.1-custom (Microsoft@builder)is confidently reported as WSL1 instead ofUnknown, violating the new three-state behavior. Match the complete WSL1 release token rather than the broad version prefix/build metadata combination.
[GeneratedRegex(@"Linux\s+version\s+4\.4\.", RegexOptions.IgnoreCase)]
- Files reviewed: 6/6 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. |
|
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. |
GetConfiguredRuntime treated a blank ASPIRE_CONTAINER_RUNTIME as absent and fell back to DOTNET_ASPIRE_CONTAINER_RUNTIME, but DcpOptions resolves the same keys through GetString(primary, secondary) with fallbackOnEmpty: false, so a present primary key suppresses the legacy one regardless of its value. Doctor could therefore name Podman while the AppHost launched the DCP default. Resolve the same way the AppHost does, and normalize only the empty case, because DcpHost omits --container-runtime for an empty value but forwards whitespace verbatim. The SSL_CERT_DIR recommendation for a blank value still expanded $SSL_CERT_DIR, so running it made the whitespace the first certificate directory OpenSSL searches - the opposite of treating it as unset - and left a leading empty entry when the variable was genuinely absent. Build the unset command from the detected directories alone. The WSL 1 banner regex matched any 'Linux version 4.4.*' as long as 'Microsoft' appeared anywhere in the banner, so a custom 4.4.1 kernel built by a user whose name contains Microsoft was confidently reported as WSL 1 and told to upgrade. Match the whole fixed 4.4.0-<build>-Microsoft compatibility release instead; anything else falls through to Unknown, which is the state this check adds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 487b899d-d79d-4349-a84b-a026861bde7f
There was a problem hiding this comment.
Review details
Suppressed comments (6)
src/Aspire.Cli/Utils/EnvironmentChecker/WslEnvironmentCheck.cs:100
/proc/versionincludes compiler/build identity metadata, so scanning the whole banner still makes a native or custom kernel such as the test's(Microsoft@builder)example count as WSL.CheckAsyncwill then emit an incorrect WSL warning. Restrict detection to recognized markers in the kernel release token (or a nonblank WSL environment variable).
if (procVersion is not null &&
(procVersion.Contains("microsoft", StringComparison.OrdinalIgnoreCase) ||
procVersion.Contains("WSL", StringComparison.OrdinalIgnoreCase)))
src/Aspire.Cli/Utils/EnvironmentChecker/WslEnvironmentCheck.cs:121
- This searches all banner metadata, so a custom WSL kernel built under an identity such as
(wsl2@builder)is reported as a confident WSL2 pass even though its release token has no WSL2 marker. Match only the kernel release token immediately followingLinux version, and add an incidental-marker regression case.
if (procVersion.Contains("WSL2", StringComparison.OrdinalIgnoreCase))
src/Aspire.Cli/Utils/EnvironmentChecker/WslEnvironmentCheck.cs:149
- Like the WSL2 check above, this can match compiler/build metadata rather than the release token. A custom kernel banner containing
(microsoft-standard@builder)is therefore misclassified as WSL2. Parse the release token and requiremicrosoft-standardthere.
if (procVersion.Contains("microsoft-standard", StringComparison.OrdinalIgnoreCase))
src/Aspire.Cli/Utils/EnvironmentChecker/WslEnvironmentCheck.cs:87
- The unknown-version fix still tells every user to “then upgrade,” including WSL2 users whose custom kernel correctly classifies as
Unknown. That recreates the misleading guidance this change is intended to remove. Make the upgrade conditional onwsl --list --verbosereporting version 1.
This issue also appears in the following locations of the same file:
- line 98
- line 121
- line 149
Fix = "Run 'wsl --list --verbose' from Windows to check the version, then upgrade with: wsl --set-version <distro> 2",
src/Aspire.Cli/Utils/EnvironmentChecker/ContainerRuntimeCheck.cs:124
- Keeping whitespace non-null does not actually surface the invalid configuration. It matches neither probed runtime, so
selectedremains null and any installed Docker/Podman rows are reported as merely “available” (often withPass); no result mentions the whitespace value even though AppHost forwards it and fails. Emit an explicit failure for a configured value that matches no supported runtime, and cover the resultingCheckAsyncoutput rather than only this helper.
var configuredRuntime = environment.GetEnvironmentVariable("ASPIRE_CONTAINER_RUNTIME")
?? environment.GetEnvironmentVariable("DOTNET_ASPIRE_CONTAINER_RUNTIME");
return string.IsNullOrEmpty(configuredRuntime) ? null : configuredRuntime;
src/Aspire.Cli/Utils/EnvironmentChecker/ContainerRuntimeCheck.cs:124
- The PR description says a blank
ASPIRE_CONTAINER_RUNTIMEfalls back toDOTNET_ASPIRE_CONTAINER_RUNTIME, but??runs before empty normalization, so an empty primary suppresses the legacy value; the new tests explicitly expectnull. Either update the PR description to document this AppHost-compatible behavior or change both AppHost and doctor to use empty-value fallback.
var configuredRuntime = environment.GetEnvironmentVariable("ASPIRE_CONTAINER_RUNTIME")
?? environment.GetEnvironmentVariable("DOTNET_ASPIRE_CONTAINER_RUNTIME");
return string.IsNullOrEmpty(configuredRuntime) ? null : configuredRuntime;
- Files reviewed: 6/6 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
Suppressed comments (1)
src/Aspire.Cli/Utils/EnvironmentChecker/ContainerRuntimeCheck.cs:153
- This contradicts the PR description, which says a blank
ASPIRE_CONTAINER_RUNTIMEis treated as unset soDOTNET_ASPIRE_CONTAINER_RUNTIMEcan be used. Here an empty primary value suppresses the fallback, while whitespace remains configured; the new tests explicitly lock in that behavior. Please align the description and intended behavior (the current implementation mirrorsDcpOptionsatsrc/Aspire.Hosting/Dcp/DcpOptions.cs:311).
var configuredRuntime = environment.GetEnvironmentVariable("ASPIRE_CONTAINER_RUNTIME")
?? environment.GetEnvironmentVariable("DOTNET_ASPIRE_CONTAINER_RUNTIME");
return string.IsNullOrEmpty(configuredRuntime) ? null : configuredRuntime;
- Files reviewed: 6/6 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0d1cf760-b1bc-46ba-a6d4-628354b00f2c
There was a problem hiding this comment.
Review details
Suppressed comments (2)
src/Aspire.Cli/Utils/EnvironmentChecker/WslEnvironmentCheck.cs:157
- This broad substring match has the same false-pass behavior for the early marker: a custom release such as
6.1.0-microsoft-standard-customis classified as WSL2. The documented early WSL2 releases end in-microsoft-standard; require that suffix so custom or unrecognized releases remainUnknown.
if (kernelRelease.Contains("microsoft-standard", StringComparison.OrdinalIgnoreCase))
src/Aspire.Cli/Utils/EnvironmentChecker/WslEnvironmentCheck.cs:130
- This accepts any release token containing
WSL2, so a custom/native release such as6.1.0-custom-WSL2is reported as a healthy WSL2 environment. Official kernels use the-microsoft-standard-WSL2local-version suffix; require that suffix so unrecognized releases fall through toUnknown, and add this case to the regression matrix.
This issue also appears on line 157 of the same file.
if (kernelRelease.Contains("WSL2", StringComparison.OrdinalIgnoreCase))
- Files reviewed: 2/2 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: 3253974d-4f18-486f-863c-281a607656d4
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
Mitch Denny (mitchdenny)
left a comment
There was a problem hiding this comment.
Reviewed the WSL detection changes and regression coverage; no blocking issues found.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 461fd6a2-28f3-44e1-a316-14d594b50226
There was a problem hiding this comment.
Review details
Suppressed comments (1)
src/Aspire.Cli/Utils/EnvironmentChecker/WslEnvironmentCheck.cs:264
NumericKernelReleaserejects a release ending in+because\S+requires at least one character after the suffix delimiter. This makes the newly addedCustomTerminalPlusKernelBannercases returnUnknowninstead of the asserted WSL2 pass. Allow+to be terminal while retaining the existing validation for other suffixes.
[GeneratedRegex(@"^\d+(?:\.\d+)+(?:[-+._~]\S+)?$", RegexOptions.CultureInvariant)]
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Balanced
c94dbb8
into
microsoft:main
Description
aspire doctorclassified WSL by parsing the kernel major version from/proc/versionand treating anything>= 4as WSL2. That premise is wrong: WSL1 reports a synthetic 4.4.0 compatibility banner, so every current WSL1 system was reported as healthy WSL2.The check now classifies the complete kernel release:
3.4.0-Microsoftand4.4.0-<build>-Microsoftare WSL1.Unknown.WSL_DISTRO_NAMEandWSL_INTEROPestablish that the shell is in WSL; neither overrides an exact WSL1 banner. Blank values are ignored.The parser only examines the numeric kernel release token. Build identities such as
(Microsoft@builder)do not make native Linux WSL, and malformed tokens such asunknown-microsoft-standard-WSL2do not produce a false pass.Unknownproduces a warning instead of a false pass or unconditional upgrade instruction.References:
Verification
The CI-produced linux-arm64 PR artifact
13.6.0-pr.19158.g83a0bb0dwas also run through the realaspire doctorpath in Ubuntu 24.04. Its embedded SHA matches this PR's83a0bb0dafhead. A private mount namespace supplied each/proc/versionfixture. Machine-checked assertions passed for all 22 scenarios, including real WSL1 and WSL2 banners, custom kernels, malformed Microsoft-looking tokens, blank signals, native Linux false positives, and WSL1 precedence.This proves the shipped classifier and
/proc/versionread path on Linux. It is not a genuine Windows/WSL-host test.Checklist