lib/Wsl.ps1
|
function Get-WslConfigConfig { <# .SYNOPSIS Reads the Wsl.Config object from the same config file used by Get-PathsConfig. Returns an ordered hashtable shaped like @{ section = @{ key = value; ... }; ... } or $null when the key is absent. #> [CmdletBinding()] param( [Parameter()] [string] $ConfigFile = $script:ConfigFilePath ) if (-not $ConfigFile -or -not (Test-Path -Path $ConfigFile -PathType Leaf)) { return $null } try { $raw = Get-Content -Path $ConfigFile -Raw -Encoding UTF8 $parsed = $raw | ConvertFrom-Json -ErrorAction Stop } catch { throw "Failed to parse '$ConfigFile': $($_.Exception.Message)" } if ($null -eq $parsed) { return $null } if (-not $parsed.PSObject.Properties['Wsl']) { return $null } if (-not $parsed.Wsl.PSObject.Properties['Config']) { return $null } $result = [ordered]@{} foreach ($sectionProp in $parsed.Wsl.Config.PSObject.Properties) { $sectionDict = [ordered]@{} foreach ($keyProp in $sectionProp.Value.PSObject.Properties) { $sectionDict[$keyProp.Name] = $keyProp.Value } $result[$sectionProp.Name] = $sectionDict } return $result } function ConvertTo-WslConfigIni { <# .SYNOPSIS Serializes an ordered { section -> { key -> value } } hashtable into the INI-style text format that .wslconfig expects. Booleans become lowercase "true"/"false". #> [CmdletBinding()] param( [Parameter(Mandatory)] [System.Collections.IDictionary] $Sections ) $lines = [System.Collections.Generic.List[string]]::new() foreach ($sectionName in $Sections.Keys) { if ($lines.Count -gt 0) { $lines.Add('') } $lines.Add("[$sectionName]") $sectionData = $Sections[$sectionName] foreach ($key in $sectionData.Keys) { $val = $sectionData[$key] if ($val -is [bool]) { $val = if ($val) { 'true' } else { 'false' } } $lines.Add("$key=$val") } } return ($lines -join [System.Environment]::NewLine) } function Initialize-Wsl { <# .SYNOPSIS Refreshes the WSL kernel (`wsl --update`). Assumes the WSL backend itself is already installed - the Wsl task declares Microsoft.WSL as a winget dependency, so the metadata-driven install pass that runs before any task action ensures WSL is present by the time this is called. Throws on non-zero exit so downstream tasks (Docker Desktop's first-run bootstrap, the WSL distro relocation task) don't waste time on a known-broken WSL state. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param() if (-not (Get-Command wsl -ErrorAction SilentlyContinue)) { Write-Status -Level Skip -Message ' [SKIP] wsl.exe is not on PATH; nothing to update.' return } if (-not $PSCmdlet.ShouldProcess('WSL kernel', 'wsl --update')) { return } Write-Status -Level Info -Message ' [INFO] Ensuring WSL kernel is up to date...' wsl --update 2>&1 | Format-ToolOutput if ($LASTEXITCODE -ne 0) { throw ("wsl --update failed with exit code {0}. " + "If WSL itself isn't installed, ensure Microsoft.WSL is in WingetPackages " + "(or run ``winget install -e --id Microsoft.WSL`` manually) and retry.") -f $LASTEXITCODE } } function Get-WslDistroBasePath { <# .SYNOPSIS Returns the BasePath of the named WSL distro by reading HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss\<id>. Strips the "\\?\" NT-path prefix so callers get a normal Windows path. Returns $null if the distro isn't registered (or the Lxss key doesn't exist - e.g. on non-Windows test runs). #> [CmdletBinding()] param( [Parameter(Mandatory)] [string] $Name ) $lxssRoot = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss' if (-not (Test-Path -Path $lxssRoot)) { return $null } $entries = Get-ChildItem -Path $lxssRoot -ErrorAction SilentlyContinue foreach ($entry in $entries) { $props = Get-ItemProperty -Path $entry.PSPath -ErrorAction SilentlyContinue if ($props -and $props.DistributionName -eq $Name) { $basePath = [string]$props.BasePath if ($basePath -match '^\\\\\?\\(.+)$') { return $Matches[1] } return $basePath } } return $null } function Get-WslDistrosConfig { <# .SYNOPSIS Reads the Wsl.Distros array from the same config file used by Get-PathsConfig. Returns an empty array when the key is absent or the file doesn't exist - callers can then fall back to the legacy "just relocate what's already installed" behavior. #> [CmdletBinding()] param( [Parameter()] [string] $ConfigFile = $script:ConfigFilePath ) if (-not $ConfigFile -or -not (Test-Path -Path $ConfigFile -PathType Leaf)) { return @() } try { $raw = Get-Content -Path $ConfigFile -Raw -Encoding UTF8 $parsed = $raw | ConvertFrom-Json -ErrorAction Stop } catch { throw "Failed to parse '$ConfigFile': $($_.Exception.Message)" } if ($null -eq $parsed) { return @() } if (-not $parsed.PSObject.Properties['Wsl']) { return @() } if (-not $parsed.Wsl.PSObject.Properties['Distros']) { return @() } @($parsed.Wsl.Distros | Where-Object { $_ }) } |