Update-PowerShellRelease.ps1
<#
.SYNOPSIS Update PowerShell to the specified version. #> function Update-PowerShellRelease { [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Default')] param ( [Parameter(ParameterSetName = 'Default')] [Switch]$Latest, [Parameter(ParameterSetName = 'Default')] [ReleaseTypes]$Release = [ReleaseTypes]::Stable, [Parameter(ParameterSetName = 'Version')] [SemVer]$Version, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Version')] [Switch]$Silent, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Version')] [hashtable]$InstallOptions, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Version')] [Switch]$NotExitConsole, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Version')] [string]$Token, [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Version')] [Switch]$Force ) # This function supports Windows, macOS only if (-not ($IsWindows -or $IsMacOS)) { Write-Warning $Messages.Update_PowerShellRelease_001 return } # Find update version $psReleaseInfo = $null switch ($PSCmdlet.ParameterSetName) { 'Version' { $psReleaseInfo = Find-PowerShellRelease -Version $Version -Token $Token } Default { $psReleaseInfo = Find-PowerShellRelease -Latest -Release $Release -Token $Token } } if (-not $psReleaseInfo) { Write-Warning $Messages.Update_PowerShellRelease_002 return } if ($psReleaseInfo.Version -lt $PSVersionTable.PSVersion -and (-not $Force)) { Write-Warning $Messages.Update_PowerShellRelease_003 return } if ($psReleaseInfo.Version -eq $PSVersionTable.PSVersion -and (-not $Force)) { $releaseName = switch ($Release) { 'Preview' { $Messages.Update_PowerShellRelease_012 } 'LTS' { $Messages.Update_PowerShellRelease_013 } Default { $Messages.Update_PowerShellRelease_014 } } Write-Warning ($Messages.Update_PowerShellRelease_004 -f $releaseName) return } WriteInfo ($Messages.Update_PowerShellRelease_005 -f $psReleaseInfo.Version) # Download installer asset $installerAssetUrls = GetInstallerAssetUrls -Release $psReleaseInfo if (@($installerAssetUrls).Count -eq 0) { Write-Error $Messages.Update_PowerShellRelease_006 return } if (@($installerAssetUrls).Count -gt 1) { Write-Warning $Messages.Update_PowerShellRelease_007 return } $localInstallerPath = Join-Path -Path ([IO.Path]::GetTempPath()) -ChildPath $installerAssetUrls.split("/")[-1] if ($PSCmdlet.ShouldProcess('Download PowerShell installer asset')) { DownloadFile -Uri $installerAssetUrls -OutFile $localInstallerPath -Token $Token } else { Write-Warning $Messages.Update_PowerShellRelease_008 WriteInfo ("(Skip) Download {0}`r`n To {1}..." -f $installerAssetUrls, $localInstallerPath) } # Do install $shouldInstall = $PSCmdlet.ShouldProcess('Install PowerShell') WriteInfo ($Messages.Update_PowerShellRelease_009 -f $psReleaseInfo.Version) $params = @{ CustomParameters = @{ Windows = @{ NewVersion = $psReleaseInfo.Version } } CommonParameters = [InstallCommonParameters]@{ InstallerPath = $localInstallerPath InstallOptions = $InstallOptions Silent = $Silent ShouldProcess = $shouldInstall } } DoInstall @params # Exit PowerShel Console if ((-not $NotExitConsole) -or $Silent) { WriteInfo $Messages.Update_PowerShellRelease_011 if ($shouldInstall) { Start-Sleep -Seconds 1 exit } else { WriteInfo '(skip) exit console.' return } } } function GetInstallerAssetUrls ([PowerShellCoreRelease]$Release) { if ($IsWindows) { return GetMSIAssetUrls -Release $Release } if ($IsMacOS) { return GetPKGAssetUrls -Release $Release } return } function GetMSIAssetUrls ([PowerShellCoreRelease]$Release) { if (IsCurrentProcess64bit) { return ($Release.Assets | Where-Object { $_.Architecture -eq [AssetArchtectures]::MSI_WIN64 }).DownloadUrl.OriginalString } return ($Release.Assets | Where-Object { $_.Architecture -eq [AssetArchtectures]::MSI_WIN32 }).DownloadUrl.OriginalString } # this function is for unit tests mainly function GetMacOSProductVersion () { try { # e.g. macOS Big Sur returns 11.1. $major, $minor = (/usr/bin/sw_vers -productVersion).Split('.') return ($major, $minor) } catch { return (0, 0) } } function GetPKGAssetUrls ([PowerShellCoreRelease]$Release) { # On macOS, Package file may be "powershell-[version].*.pkg" or "powershell-lts-[version].*.pkg" # So we ignore "powershell-lts-[version].*.pkg". $majorVer, $minorVer = GetMacOSProductVersion if ($majorVer -ge 11) { $asset = if (IsArmCPU) { # PKG_OSXARM64 $Release.Assets | Where-Object { $_.Architecture -eq [AssetArchtectures]::PKG_OSXARM64 } } else { # PKG_OSX $Release.Assets | Where-Object { $_.Architecture -eq [AssetArchtectures]::PKG_OSX -and $_.Name -notlike 'powershell-lts-*.pkg' } } if ($null -ne $asset) { return $asset.DownloadUrl.OriginalString } return } if ($majorVer -eq 10) { switch ($minorVer) { 11 { # PKG_OSX1011 # * OSX El Capitan (10.11) $asset = $Release.Assets | Where-Object { $_.Architecture -eq [AssetArchtectures]::PKG_OSX1011 } if ($null -ne $asset) { return $asset.DownloadUrl.OriginalString } return } { $_ -in (12, 13) } { # PKG_OSX1012 or PKG_OSX # * macOS Sierra (10.12) # * macOS High Sierra (10.13) $asset = $Release.Assets | Where-Object { $_.Architecture -eq [AssetArchtectures]::PKG_OSX -and $_.Name -notlike 'powershell-lts-*.pkg' } if ($null -ne $asset) { return $asset.DownloadUrl.OriginalString } $asset = $Release.Assets | Where-Object { $_.Architecture -eq [AssetArchtectures]::PKG_OSX1012 } if ($null -ne $asset) { return $asset.DownloadUrl.OriginalString } return } Default { $asset = if (IsArmCPU) { # PKG_OSXARM64 $Release.Assets | Where-Object { $_.Architecture -eq [AssetArchtectures]::PKG_OSXARM64 } } else { # PKG_OSX $Release.Assets | Where-Object { $_.Architecture -eq [AssetArchtectures]::PKG_OSX -and $_.Name -notlike 'powershell-lts-*.pkg' } } if ($null -ne $asset) { return $asset.DownloadUrl.OriginalString } return } } } } class InstallCommonParameters { [string]$InstallerPath [hashtable]$InstallOptions [bool]$Silent [bool]$ShouldProcess } function DoInstall ([hashtable]$CustomParameters, [InstallCommonParameters]$CommonParameters) { if ($IsWindows) { InstallMSI -NewVersion $CustomParameters["Windows"]["NewVersion"] -CommonParameters $CommonParameters return } if ($IsMacOS) { InstallPKG -CommonParameters $CommonParameters return } return } function InstallMSI ([SemVer]$NewVersion, [InstallCommonParameters]$CommonParameters) { $msiArgs = @('/i', '"{0}"' -f $CommonParameters.InstallerPath) if ($CommonParameters.Silent) { $msiArgs += '/passive' } # Set the default install options if not specified. # Note : These options are valid only for silent installation. if ($null -eq $CommonParameters.InstallOptions) { if ($NewVersion -ge '6.1.0-preview.2') { $CommonParameters.InstallOptions = @{ ADD_PATH = 1; REGISTER_MANIFEST = 1; } } if ($NewVersion -ge '7.2.0-preview.8') { $CommonParameters.InstallOptions["ENABLE_MU"] = 1 $CommonParameters.InstallOptions["USE_MU"] = 1 } } if ($null -ne $CommonParameters.InstallOptions) { # Currently following parameters are allowed. # INSTALLFOLDER = "C:\PowerShell\" : Install folder # ADD_PATH = [0|1] : Add PowerShell to Path Environment Variable # REGISTER_MANIFEST = [0|1] : Register Windows Event Logging Manifest # ENABLE_PSREMOTING = [0|1] : Enable PowerShell remoting # ADD_EXPLORER_CONTEXT_MENU_OPENPOWERSHELL = [0|1] : Add 'Open here' context menus to Explorer # ADD_FILE_CONTEXT_MENU_RUNPOWERSHELL = [0|1] : Add 'Run with PowerShell' context menu for PowerShell files # ENABLE_MU = [0|1] : Enable updating PowerShell through Microsoft Update or WSUS (recommended) # USE_MU = [0|1] : Use Microsoft Update when I check for updates (recommended) foreach ($key in $CommonParameters.InstallOptions.Keys) { $msiArgs += ('{0}={1}' -f $key, $CommonParameters.InstallOptions[$key]) } } if ($CommonParameters.ShouldProcess) { WriteInfo ('msiexec.exe {0}' -f ($msiArgs -join ' ')) Start-Process -FilePath 'msiexec.exe' -ArgumentList $msiArgs } else { Write-Warning $Messages.Update_PowerShellRelease_010 WriteInfo ('(skip) msiexec.exe {0}' -f ($msiArgs -join ' ')) } } function InstallPKG ([InstallCommonParameters]$CommonParameters) { # [string]$PkgFile, [bool]$Silent, [hashtable]$InstallOptions, [bool]$ShouldProcess) { $targetVolume = '/' if ($null -ne $CommonParameters.InstallOptions) { # Install volume if ($CommonParameters.InstallOptions.ContainsKey('target')) { $targetVolume = $CommonParameters.InstallOptions['target'] } } if ($CommonParameters.Silent) { if ($CommonParameters.ShouldProcess) { WriteInfo "/usr/bin/sudo /usr/sbin/installer -pkg ""$($CommonParameters.InstallerPath)"" -target $targetVolume" /usr/bin/sudo /usr/sbin/installer -pkg "$($CommonParameters.InstallerPath)" -target $targetVolume } else { Write-Warning $Messages.Update_PowerShellRelease_010 WriteInfo "(skip) /usr/bin/sudo /usr/sbin/installer -pkg ""$($CommonParameters.InstallerPath)"" -target $targetVolume" } return } if ($CommonParameters.ShouldProcess) { WriteInfo "Invoke-Item ""$($CommonParameters.InstallerPath)""" Invoke-Item "$($CommonParameters.InstallerPath)" } else { Write-Warning $Messages.Update_PowerShellRelease_010 WriteInfo "(skip) Invoke-Item ""$($CommonParameters.InstallerPath)""" } } |