-
Notifications
You must be signed in to change notification settings - Fork 0
Fix fresh Windows and Linux runtime setup #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -37,8 +37,19 @@ function Fetch-File { | |||||||||||||||||
| # version/list matches fail unless the NULs are stripped first. | ||||||||||||||||||
| function Get-WslText { | ||||||||||||||||||
| param([Parameter(Mandatory = $true)][string[]]$ArgumentList) | ||||||||||||||||||
| $raw = & $wsl @ArgumentList 2>&1 | Out-String | ||||||||||||||||||
| $code = $LASTEXITCODE | ||||||||||||||||||
| # Windows PowerShell promotes native stderr to ErrorRecord objects. With the | ||||||||||||||||||
| # script-wide Stop policy, an expected nonzero probe (such as --version on a | ||||||||||||||||||
| # genuinely fresh host) otherwise aborts before we can inspect LASTEXITCODE | ||||||||||||||||||
| # and install WSL. Keep the probe non-terminating, then restore fail-closed | ||||||||||||||||||
| # behavior for the surrounding setup transaction. | ||||||||||||||||||
| $previousErrorAction = $ErrorActionPreference | ||||||||||||||||||
| try { | ||||||||||||||||||
| $ErrorActionPreference = "Continue" | ||||||||||||||||||
| $raw = & $wsl @ArgumentList 2>&1 | Out-String | ||||||||||||||||||
| $code = $LASTEXITCODE | ||||||||||||||||||
| } finally { | ||||||||||||||||||
| $ErrorActionPreference = $previousErrorAction | ||||||||||||||||||
| } | ||||||||||||||||||
| $text = ($raw -replace [char]0, "").Trim() | ||||||||||||||||||
| return [pscustomobject]@{ ExitCode = $code; Text = $text } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
@@ -103,63 +114,69 @@ function Fail-Setup { | |||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if ($HostSetup) { | ||||||||||||||||||
| $identity = [Security.Principal.WindowsIdentity]::GetCurrent() | ||||||||||||||||||
| $principal = [Security.Principal.WindowsPrincipal]::new($identity) | ||||||||||||||||||
| if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { | ||||||||||||||||||
| Fail-Setup "The WSL host setup phase requires administrator approval." | ||||||||||||||||||
| } | ||||||||||||||||||
| Write-SetupStatus -Status "running" -Step "Enabling Windows WSL features..." -Progress 8 | ||||||||||||||||||
| $wslFeature = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux | ||||||||||||||||||
| $vmFeature = Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform | ||||||||||||||||||
| if ($wslFeature.State -ne "Enabled") { Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -All -NoRestart | Out-Null } | ||||||||||||||||||
| if ($vmFeature.State -ne "Enabled") { Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -All -NoRestart | Out-Null } | ||||||||||||||||||
| $wslFeature = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux | ||||||||||||||||||
| $vmFeature = Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform | ||||||||||||||||||
| $restartRequired = (Test-RestartRequired $wslFeature) -or (Test-RestartRequired $vmFeature) | ||||||||||||||||||
| $hostTemporary = Join-Path ([System.IO.Path]::GetTempPath()) ("1helm-wsl-host-" + [Guid]::NewGuid().ToString("N")) | ||||||||||||||||||
| New-Item -ItemType Directory -Path $hostTemporary | Out-Null | ||||||||||||||||||
| try { | ||||||||||||||||||
| if (-not (Test-PinnedWslRuntime)) { | ||||||||||||||||||
| $msi = Join-Path $hostTemporary "wsl.candidate.msi" | ||||||||||||||||||
| Write-SetupStatus -Status "running" -Step "Downloading Microsoft WSL installer..." -Progress 12 | ||||||||||||||||||
| Fetch-File -Url $wslInstallerUrl -Destination $msi | ||||||||||||||||||
| if ((Get-FileHash -LiteralPath $msi -Algorithm SHA256).Hash.ToLowerInvariant() -ne $wslInstallerSha256) { | ||||||||||||||||||
| Fail-Setup "Microsoft WSL installer did not match 1Helm's pinned SHA-256." | ||||||||||||||||||
| } | ||||||||||||||||||
| $signature = Get-AuthenticodeSignature -LiteralPath $msi | ||||||||||||||||||
| if ($signature.Status -ne [System.Management.Automation.SignatureStatus]::Valid -or $null -eq $signature.SignerCertificate -or | ||||||||||||||||||
| $signature.SignerCertificate.Subject -notmatch '(^|,\s*)CN=Microsoft Corporation(,|$)') { | ||||||||||||||||||
| Fail-Setup "Microsoft WSL installer did not have a valid Microsoft Corporation signature." | ||||||||||||||||||
| } | ||||||||||||||||||
| Write-SetupStatus -Status "running" -Step "Installing Microsoft WSL $wslVersion..." -Progress 18 | ||||||||||||||||||
| $installer = Start-Process -FilePath "$env:SystemRoot\System32\msiexec.exe" -ArgumentList @("/i", $msi, "/qn", "/norestart") -Wait -PassThru | ||||||||||||||||||
| # 0 success; 1641/3010 success+reboot; 1638 already installed (same/newer). | ||||||||||||||||||
| # 1603 is a hard fail from msiexec, but on machines that already have the | ||||||||||||||||||
| # pinned WSL build (or UTF-16 made detection fail earlier) the package may | ||||||||||||||||||
| # still leave a working runtime - re-verify before failing closed. | ||||||||||||||||||
| if ($installer.ExitCode -in @(1641, 3010)) { $restartRequired = $true } | ||||||||||||||||||
| elseif ($installer.ExitCode -notin @(0, 1638)) { | ||||||||||||||||||
| if (Test-PinnedWslRuntime) { | ||||||||||||||||||
| Write-Host "Microsoft WSL installer returned $($installer.ExitCode), but pinned WSL $wslVersion is already present; continuing." | ||||||||||||||||||
| } else { | ||||||||||||||||||
| Fail-Setup "Microsoft WSL installer failed with exit code $($installer.ExitCode)." | ||||||||||||||||||
| $identity = [Security.Principal.WindowsIdentity]::GetCurrent() | ||||||||||||||||||
| $principal = [Security.Principal.WindowsPrincipal]::new($identity) | ||||||||||||||||||
| if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { | ||||||||||||||||||
| Fail-Setup "The WSL host setup phase requires administrator approval." | ||||||||||||||||||
| } | ||||||||||||||||||
| Write-SetupStatus -Status "running" -Step "Enabling Windows WSL features..." -Progress 8 | ||||||||||||||||||
| $wslFeature = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux | ||||||||||||||||||
| $vmFeature = Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform | ||||||||||||||||||
| if ($wslFeature.State -ne "Enabled") { Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -All -NoRestart | Out-Null } | ||||||||||||||||||
| if ($vmFeature.State -ne "Enabled") { Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -All -NoRestart | Out-Null } | ||||||||||||||||||
| $wslFeature = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux | ||||||||||||||||||
| $vmFeature = Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform | ||||||||||||||||||
| $restartRequired = (Test-RestartRequired $wslFeature) -or (Test-RestartRequired $vmFeature) | ||||||||||||||||||
| $hostTemporary = Join-Path ([System.IO.Path]::GetTempPath()) ("1helm-wsl-host-" + [Guid]::NewGuid().ToString("N")) | ||||||||||||||||||
| New-Item -ItemType Directory -Path $hostTemporary | Out-Null | ||||||||||||||||||
| try { | ||||||||||||||||||
| if (-not (Test-PinnedWslRuntime)) { | ||||||||||||||||||
| $msi = Join-Path $hostTemporary "wsl.candidate.msi" | ||||||||||||||||||
| Write-SetupStatus -Status "running" -Step "Downloading Microsoft WSL installer..." -Progress 12 | ||||||||||||||||||
| Fetch-File -Url $wslInstallerUrl -Destination $msi | ||||||||||||||||||
| if ((Get-FileHash -LiteralPath $msi -Algorithm SHA256).Hash.ToLowerInvariant() -ne $wslInstallerSha256) { | ||||||||||||||||||
| Fail-Setup "Microsoft WSL installer did not match 1Helm's pinned SHA-256." | ||||||||||||||||||
| } | ||||||||||||||||||
| $signature = Get-AuthenticodeSignature -LiteralPath $msi | ||||||||||||||||||
| if ($signature.Status -ne [System.Management.Automation.SignatureStatus]::Valid -or $null -eq $signature.SignerCertificate -or | ||||||||||||||||||
| $signature.SignerCertificate.Subject -notmatch '(^|,\s*)CN=Microsoft Corporation(,|$)') { | ||||||||||||||||||
| Fail-Setup "Microsoft WSL installer did not have a valid Microsoft Corporation signature." | ||||||||||||||||||
| } | ||||||||||||||||||
| Write-SetupStatus -Status "running" -Step "Installing Microsoft WSL $wslVersion..." -Progress 18 | ||||||||||||||||||
| $installer = Start-Process -FilePath "$env:SystemRoot\System32\msiexec.exe" -ArgumentList @("/i", $msi, "/qn", "/norestart") -Wait -PassThru | ||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🌐 Web query:
💡 Result: In Windows PowerShell 5.1, the Start-Process cmdlet's -ArgumentList parameter accepts either a single string or an array of strings [1][2]. When you provide an array, the cmdlet joins the elements into a single string by inserting a space between each element [1][3]. Crucially, Start-Process does not automatically handle quoting for array elements that contain spaces [4][5][6]. If an argument contains spaces, the resulting joined string may cause the target process to interpret the arguments incorrectly [7][5]. To pass arguments containing spaces correctly in PowerShell 5.1, you must manually include escaped double quotes around the specific argument within your string or array element [1][2][8]. Example using an array: Start-Process -FilePath "C:\Path\To\Executable.exe" -ArgumentList "FirstArg", " Citations:
🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- candidate script ---'
sed -n '1,230p' scripts/install-wsl-runtime.ps1
printf '%s\n' '--- PowerShell version references ---'
rg -n -i --glob '!node_modules' --glob '!dist' --glob '!build' 'PowerShell|pwsh|powershell.exe|Windows PowerShell|PSVersion' .
printf '%s\n' '--- installer-related references ---'
rg -n -C 3 'Start-Process|GetTempPath|wsl\.candidate\.msi|Test-PinnedWslRuntime|msiexec' scriptsRepository: gitcommit90/1Helm Length of output: 19099 🌐 Web query:
💡 Result: In Windows PowerShell 5.1, the Start-Process cmdlet does not automatically add quotation marks to elements in an ArgumentList array [1][2]. Instead, it flattens the array by joining all elements into a single string, separated by a single space [1][3][2]. Because this process does not preserve the structure of the original array or apply automatic quoting, arguments containing spaces or special characters will be misinterpreted by the target executable unless you manually handle the quoting [1][3]. To ensure that arguments containing spaces are correctly passed to a child process, you must include escaped double quotes around those specific elements within your PowerShell code [3][2]. For example, if you need to pass a file path that contains spaces: # Incorrect: The executable will see 'C:\Program' and 'Files\app.txt' as two separate arguments Start-Process -FilePath "app.exe" -ArgumentList "C:\Program Files\app.txt" # Correct: Escaped quotes ensure the path is treated as a single argument Start-Process -FilePath "app.exe" -ArgumentList " Citations:
Quote the MSI path passed to Windows PowerShell 5.1 joins 🤖 Prompt for AI Agents |
||||||||||||||||||
| # 0 success; 1641/3010 success+reboot; 1638 already installed (same/newer). | ||||||||||||||||||
| # 1603 is a hard fail from msiexec, but on machines that already have the | ||||||||||||||||||
| # pinned WSL build (or UTF-16 made detection fail earlier) the package may | ||||||||||||||||||
| # still leave a working runtime - re-verify before failing closed. | ||||||||||||||||||
| if ($installer.ExitCode -in @(1641, 3010)) { $restartRequired = $true } | ||||||||||||||||||
| elseif ($installer.ExitCode -notin @(0, 1638)) { | ||||||||||||||||||
| if (Test-PinnedWslRuntime) { | ||||||||||||||||||
| Write-Host "Microsoft WSL installer returned $($installer.ExitCode), but pinned WSL $wslVersion is already present; continuing." | ||||||||||||||||||
| } else { | ||||||||||||||||||
| Fail-Setup "Microsoft WSL installer failed with exit code $($installer.ExitCode)." | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| } else { | ||||||||||||||||||
| Write-SetupStatus -Status "running" -Step "Microsoft WSL $wslVersion is already installed." -Progress 18 | ||||||||||||||||||
| } | ||||||||||||||||||
| } else { | ||||||||||||||||||
| Write-SetupStatus -Status "running" -Step "Microsoft WSL $wslVersion is already installed." -Progress 18 | ||||||||||||||||||
| } | ||||||||||||||||||
| if ($restartRequired) { | ||||||||||||||||||
| Write-SetupStatus -Status "restart_required" -Step "WSL 2 features are enabled. Restart Windows once, then retry 1Helm computer setup." -Progress 20 | ||||||||||||||||||
| exit 10 | ||||||||||||||||||
| if ($restartRequired) { | ||||||||||||||||||
| Write-SetupStatus -Status "restart_required" -Step "WSL 2 features are enabled. Restart Windows once, then retry 1Helm computer setup." -Progress 20 | ||||||||||||||||||
| exit 10 | ||||||||||||||||||
| } | ||||||||||||||||||
| if (-not (Test-PinnedWslRuntime)) { Fail-Setup "Microsoft WSL $wslVersion was installed but could not be verified." } | ||||||||||||||||||
| Write-SetupStatus -Status "running" -Step "Setting WSL 2 as the default..." -Progress 22 | ||||||||||||||||||
| $defaultVersion = Get-WslText -ArgumentList @("--set-default-version", "2") | ||||||||||||||||||
| if ($defaultVersion.ExitCode -ne 0) { Fail-Setup "WSL could not set version 2 as the default. $($defaultVersion.Text)" } | ||||||||||||||||||
| } finally { | ||||||||||||||||||
| if (Test-Path -LiteralPath $hostTemporary) { Remove-Item -LiteralPath $hostTemporary -Recurse -Force } | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+171
to
173
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win Make temporary cleanup best-effort so it cannot mask the real error. The script-wide policy is ♻️ Proposed fix } finally {
- if (Test-Path -LiteralPath $hostTemporary) { Remove-Item -LiteralPath $hostTemporary -Recurse -Force }
+ if (Test-Path -LiteralPath $hostTemporary) {
+ Remove-Item -LiteralPath $hostTemporary -Recurse -Force -ErrorAction SilentlyContinue
+ }
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| if (-not (Test-PinnedWslRuntime)) { Fail-Setup "Microsoft WSL $wslVersion was installed but could not be verified." } | ||||||||||||||||||
| Write-SetupStatus -Status "running" -Step "Setting WSL 2 as the default..." -Progress 22 | ||||||||||||||||||
| $defaultVersion = Get-WslText -ArgumentList @("--set-default-version", "2") | ||||||||||||||||||
| if ($defaultVersion.ExitCode -ne 0) { Fail-Setup "WSL could not set version 2 as the default. $($defaultVersion.Text)" } | ||||||||||||||||||
| } finally { | ||||||||||||||||||
| if (Test-Path -LiteralPath $hostTemporary) { Remove-Item -LiteralPath $hostTemporary -Recurse -Force } | ||||||||||||||||||
| exit 0 | ||||||||||||||||||
| } catch { | ||||||||||||||||||
| $message = $_.Exception.Message | ||||||||||||||||||
| if (-not $message) { $message = "$_" } | ||||||||||||||||||
| Fail-Setup $message | ||||||||||||||||||
| } | ||||||||||||||||||
| exit 0 | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| # Keep the distribution owned by the signed-in Windows account. Only optional | ||||||||||||||||||
|
|
||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Do not trust
$LASTEXITCODEwhenwsl.exenever runs.$codeis set only from$LASTEXITCODE. If& $wslcannot start (wsl.exe absent fromSystem32, or launch blocked), no native exit code is produced.$LASTEXITCODEthen holds a stale value from an earlier native call, or$nullon first use. A stale0makes the probe report success and makesGet-WslDistributionNamesreturn the error text as distribution names. Default$codeto a nonzero value and check$?.🛡️ Proposed fix to fail closed on launch failures
📝 Committable suggestion
🤖 Prompt for AI Agents