From 623e6013bf954fbce2e1c396549366678a2bd00a Mon Sep 17 00:00:00 2001 From: Chris Titus Date: Tue, 10 Dec 2024 00:25:11 -0600 Subject: [PATCH] fix winget for good --- Test-WingetInstall.ps1 | 18 +-- functions/private/Get-WinUtilWingetLatest.ps1 | 104 --------------- functions/private/Install-WinUtilWinget.ps1 | 120 ++++++++++++++++-- 3 files changed, 113 insertions(+), 129 deletions(-) delete mode 100644 functions/private/Get-WinUtilWingetLatest.ps1 diff --git a/Test-WingetInstall.ps1 b/Test-WingetInstall.ps1 index ba300eacaa..060e259211 100644 --- a/Test-WingetInstall.ps1 +++ b/Test-WingetInstall.ps1 @@ -1,5 +1,6 @@ # Import the function (adjust the path according to your setup) -. "./functions/private/Get-WinUtilWingetLatest.ps1" +. "./functions/private/Install-WinUtilWinget.ps1" +. "./functions/private/Test-WinUtilPackageManager.ps1" # Set up Information stream to be visible $InformationPreference = "Continue" @@ -7,20 +8,7 @@ $InformationPreference = "Continue" Write-Host "Starting Winget installation test..." -ForegroundColor Cyan try { - # Test the function with verbose output - Write-Host "Attempting to run Get-WinUtilWingetLatest..." -ForegroundColor Cyan - Get-WinUtilWingetLatest -Verbose - - # Verify Winget is working - if (Get-Command winget -ErrorAction SilentlyContinue) { - Write-Host "Success! Winget is installed and accessible." -ForegroundColor Green - - # Display Winget version - Write-Host "`nWinget version:" -ForegroundColor Cyan - winget --version - } else { - Write-Host "Warning: Winget is installed but not accessible in the current session. You may need to restart your terminal." -ForegroundColor Yellow - } + Install-WinUtilWinget } catch { Write-Host "Error occurred during testing: $($_.Exception.Message)" -ForegroundColor Red Write-Host "Stack Trace:" -ForegroundColor Red diff --git a/functions/private/Get-WinUtilWingetLatest.ps1 b/functions/private/Get-WinUtilWingetLatest.ps1 deleted file mode 100644 index 4dfe9423c0..0000000000 --- a/functions/private/Get-WinUtilWingetLatest.ps1 +++ /dev/null @@ -1,104 +0,0 @@ -function Get-WinUtilWingetLatest { - [CmdletBinding()] - param() - - <# - .SYNOPSIS - Uses GitHub API to check for the latest release of Winget. - .DESCRIPTION - This function first attempts to update WinGet using winget itself, then falls back to manual installation if needed. - #> - $ProgressPreference = "SilentlyContinue" - $InformationPreference = 'Continue' - - try { - $wingetCmd = Get-Command winget -ErrorAction Stop - Write-Information "Attempting to update WinGet using WinGet..." - $result = Start-Process -FilePath "`"$($wingetCmd.Source)`"" -ArgumentList "install -e --accept-source-agreements --accept-package-agreements Microsoft.AppInstaller" -Wait -NoNewWindow -PassThru - if ($result.ExitCode -ne 0) { - throw "WinGet update failed with exit code: $($result.ExitCode)" - } - return $true - } - catch { - Write-Information "WinGet not found or update failed. Attempting to install from Microsoft Store..." - try { - # Try to close any running WinGet processes - Get-Process -Name "DesktopAppInstaller", "winget" -ErrorAction SilentlyContinue | ForEach-Object { - Write-Information "Stopping running WinGet process..." - $_.Kill() - Start-Sleep -Seconds 2 - } - - # Try to load Windows Runtime assemblies more reliably - $null = [System.Runtime.WindowsRuntime.WindowsRuntimeSystemExtensions] - Add-Type -AssemblyName System.Runtime.WindowsRuntime - - # Load required assemblies from Windows SDK - $null = @( - [Windows.Management.Deployment.PackageManager, Windows.Management.Deployment, ContentType = WindowsRuntime] - [Windows.Foundation.Uri, Windows.Foundation, ContentType = WindowsRuntime] - [Windows.Management.Deployment.DeploymentOptions, Windows.Management.Deployment, ContentType = WindowsRuntime] - ) - - # Initialize PackageManager - $packageManager = New-Object Windows.Management.Deployment.PackageManager - - # Rest of the Microsoft Store installation logic - $appxPackage = "https://aka.ms/getwinget" - $uri = New-Object Windows.Foundation.Uri($appxPackage) - $deploymentOperation = $packageManager.AddPackageAsync($uri, $null, "Add") - - # Add timeout check for deployment operation - $timeout = 300 - $timer = [System.Diagnostics.Stopwatch]::StartNew() - - while ($deploymentOperation.Status -eq 0) { - if ($timer.Elapsed.TotalSeconds -gt $timeout) { - throw "Installation timed out after $timeout seconds" - } - Start-Sleep -Milliseconds 100 - } - - if ($deploymentOperation.Status -eq 1) { - Write-Information "Successfully installed WinGet from Microsoft Store" - return $true - } else { - throw "Installation failed with status: $($deploymentOperation.Status)" - } - } - catch [System.Management.Automation.RuntimeException] { - Write-Information "Windows Runtime components not available. Attempting manual download..." - try { - # Try to close any running WinGet processes - Get-Process -Name "DesktopAppInstaller", "winget" -ErrorAction SilentlyContinue | ForEach-Object { - Write-Information "Stopping running WinGet process..." - $_.Kill() - Start-Sleep -Seconds 2 - } - - # Fallback to direct download from GitHub - $apiUrl = "https://api.github.com/repos/microsoft/winget-cli/releases/latest" - $release = Invoke-RestMethod -Uri $apiUrl - $msixBundleUrl = ($release.assets | Where-Object { $_.name -like "*.msixbundle" }).browser_download_url - - $tempFile = Join-Path $env:TEMP "Microsoft.DesktopAppInstaller.msixbundle" - Invoke-WebRequest -Uri $msixBundleUrl -OutFile $tempFile - - Add-AppxPackage -Path $tempFile -ErrorAction Stop - Remove-Item $tempFile -Force - - Write-Information "Successfully installed WinGet from GitHub release" - return $true - } - catch { - Write-Error "Failed to install WinGet: $_" - return $false - } - } - catch { - Write-Error "Failed to install WinGet: $_" - return $false - } - } -} diff --git a/functions/private/Install-WinUtilWinget.ps1 b/functions/private/Install-WinUtilWinget.ps1 index 7621ef913d..cd412fa9e3 100644 --- a/functions/private/Install-WinUtilWinget.ps1 +++ b/functions/private/Install-WinUtilWinget.ps1 @@ -33,18 +33,118 @@ function Install-WinUtilWinget { return } - # Install Winget via GitHub method. - # Used part of my own script with some modification: ruxunderscore/windows-initialization - Write-Host "Downloading Winget and License File`r" - Get-WinUtilWingetLatest - Write-Host "Enabling NuGet and Module..." - Install-PackageProvider -Name NuGet -Force - Install-Module -Name Microsoft.WinGet.Client -Force - # Winget only needs a refresh of the environment variables to be used. + Write-Host "Attempting to install/update Winget`r" + try { + $wingetCmd = Get-Command winget -ErrorAction Stop + Write-Information "Attempting to update WinGet using WinGet..." + $result = Start-Process -FilePath "`"$($wingetCmd.Source)`"" -ArgumentList "install -e --accept-source-agreements --accept-package-agreements Microsoft.AppInstaller" -Wait -NoNewWindow -PassThru + if ($result.ExitCode -ne 0) { + throw "WinGet update failed with exit code: $($result.ExitCode)" + } + Write-Output "Refreshing Environment Variables...`n" + $ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") + return + } catch { + Write-Information "WinGet not found or update failed. Attempting to install from Microsoft Store..." + } + try { + # Try to close any running WinGet processes + Get-Process -Name "DesktopAppInstaller", "winget" -ErrorAction SilentlyContinue | ForEach-Object { + Write-Information "Stopping running WinGet process..." + $_.Kill() + Start-Sleep -Seconds 2 + } + + # Try to load Windows Runtime assemblies more reliably + $null = [System.Runtime.WindowsRuntime.WindowsRuntimeSystemExtensions] + Add-Type -AssemblyName System.Runtime.WindowsRuntime + + # Load required assemblies from Windows SDK + $null = @( + [Windows.Management.Deployment.PackageManager, Windows.Management.Deployment, ContentType = WindowsRuntime] + [Windows.Foundation.Uri, Windows.Foundation, ContentType = WindowsRuntime] + [Windows.Management.Deployment.DeploymentOptions, Windows.Management.Deployment, ContentType = WindowsRuntime] + ) + + # Initialize PackageManager + $packageManager = New-Object Windows.Management.Deployment.PackageManager + + # Rest of the Microsoft Store installation logic + $appxPackage = "https://aka.ms/getwinget" + $uri = New-Object Windows.Foundation.Uri($appxPackage) + $deploymentOperation = $packageManager.AddPackageAsync($uri, $null, "Add") + + # Add timeout check for deployment operation + $timeout = 300 + $timer = [System.Diagnostics.Stopwatch]::StartNew() + + while ($deploymentOperation.Status -eq 0) { + if ($timer.Elapsed.TotalSeconds -gt $timeout) { + throw "Installation timed out after $timeout seconds" + } + Start-Sleep -Milliseconds 100 + } + + if ($deploymentOperation.Status -eq 1) { + Write-Information "Successfully installed WinGet from Microsoft Store" + Write-Output "Refreshing Environment Variables...`n" + $ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") + return + } else { + throw "Installation failed with status: $($deploymentOperation.Status)" + } + } catch { + Write-Information "Microsoft Store installation failed. Attempting to install from Nuget..." + } + try { + ## Nuget Method + Write-Host "Enabling NuGet and Module..." + # Enable TLS 1.2 for the PowerShell session + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + + # Try to register the NuGet package source if not present + if (-not (Get-PackageSource -Name "NuGet" -ErrorAction SilentlyContinue)) { + Register-PackageSource -Name "NuGet" -Location "https://www.nuget.org/api/v2" -ProviderName NuGet -Force + } + + # Install NuGet provider with error handling + try { + Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Confirm:$false -ErrorAction Stop + } catch { + Write-Warning "Failed to install NuGet provider through standard method. Trying alternative approach..." + Install-PackageProvider -Name NuGet -Source "https://www.powershellgallery.com/api/v2" -Force -Confirm:$false + } + Install-Module -Name Microsoft.WinGet.Client -Confirm:$false -Force + + # Check if WinGet was installed successfully through NuGet + $wingetCmd = Get-Command winget -ErrorAction Stop + Write-Information "Successfully installed WinGet through NuGet" + Write-Output "Refreshing Environment Variables...`n" + $ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") + return + } catch { + Write-Warning "NuGet installation failed. Attempting to install from GitHub..." + } + # GitHub fallback installation method + $releases_url = "https://api.github.com/repos/microsoft/winget-cli/releases/latest" + $asset = (Invoke-RestMethod -Uri $releases_url).assets | + Where-Object { $_.name -match "\.msixbundle$" } | + Select-Object -First 1 + + $download_url = $asset.browser_download_url + $output_path = Join-Path $env:TEMP $asset.name + + Invoke-WebRequest -Uri $download_url -OutFile $output_path + Add-AppxPackage -Path $output_path -ErrorAction Stop + + # Verify installation + $wingetCmd = Get-Command winget -ErrorAction Stop + Write-Information "Successfully installed WinGet through GitHub" Write-Output "Refreshing Environment Variables...`n" $ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") + return } catch { - Write-Error "Failed to install Winget: $($_.Exception.Message)" + Write-Error "All installation methods failed. Unable to install WinGet." + throw } - }