Install-Winget.ps1 - Add existing installer files parameter

Adds optional string parameter to use existing installer files
pull/13/head^2
ThioJoe 2026-02-12 14:38:50 -07:00
parent dc084ba12b
commit a716ce5574
No known key found for this signature in database
GPG Key ID: 2E328FE64CC3898C
1 changed files with 108 additions and 51 deletions

View File

@ -3,11 +3,24 @@
# Author: ThioJoe
# Repo Url: https://github.com/ThioJoe/Windows-Sandbox-Tools
# Last Updated: August 4, 2025
# Last Updated: February 12, 2026
param(
[switch]$removeMsStoreAsSource = $false # If switch is included, it will remove the 'msstore' source after installing winget, which doesn't work with Sandbox, unless the Microsoft Store is also installed
)
# If switch is included, it will remove the 'msstore' source after installing winget, which doesn't work with Sandbox, unless the Microsoft Store is also installed
[switch]$removeMsStoreAsSource = $false,
# Optional path to a local directory containing the installation files. If provided, the download steps will be skipped.
# - Just copy the entire "Winget Install" folder with the files the script normally downloads, and put it in your mounted folder.
# - Make sure to use the mounted path from the perspective of within the sandbox.
[string]$ExistingInstallerFilesPath
)
# --- Parameter Usage Examples ---
# Standard run (Download & Install):
# .\Install-Winget.ps1
#
# Install from existing files instead of downloading:
# .\Install-Winget.ps1 -ExistingInstallerFilesPath "C:\Users\WDAGUtilityAccount\Desktop\HostShared\Winget Install"
function Get-LatestRelease {
param(
@ -77,25 +90,33 @@ function Install-WingetDependencies {
}
}
# --- Define Working Directory ---
$userDownloadsFolder = (New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path
$subfolderName = "Winget Install"
$msixName = "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle"
if ($ExistingInstallerFilesPath) {
if (Test-Path -Path $ExistingInstallerFilesPath) {
$workingDir = $ExistingInstallerFilesPath
Write-Host "Using local source path: $workingDir" -ForegroundColor Yellow
} else {
Write-Error "The specified local source path does not exist: $ExistingInstallerFilesPath"
return
}
} else {
# Combine them to create the full working directory path
$workingDir = Join-Path -Path $userDownloadsFolder -ChildPath $subfolderName
# Create the directory if it doesn't exist
if (-not (Test-Path -Path $workingDir)) {
New-Item -Path $workingDir -ItemType Directory -Force | Out-Null
}
}
# Prevents progress bar from showing (often speeds downloads)
$ProgressPreference = 'SilentlyContinue'
$downloadPath = Join-Path $env:USERPROFILE "Downloads"
$latestRelease = Get-LatestRelease
if (-not $latestRelease) { Write-Error "Could not retrieve the latest release. Exiting."; return; }
$latestTag = $latestRelease.tag_name
Write-Host "Latest winget version tag is: $latestTag"
# Download the MSIX bundle
$msixName = "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle"
$msixUrl = Get-AssetUrl -release $latestRelease -assetName $msixName
if (-not $msixUrl) { Write-Error "Could not find $msixName in the latest release assets."; return; }
Write-Host "Downloading $msixName..."
$msixPath = Join-Path $downloadPath $msixName
Invoke-WebRequest -Uri $msixUrl -OutFile $msixPath
# --- Determine Architecture ---
# Figure out the OS architecture using environment variable
$procArch = $env:PROCESSOR_ARCHITECTURE
switch -Wildcard ($procArch) {
@ -109,49 +130,85 @@ switch -Wildcard ($procArch) {
}
}
# Download the dependencies zip
$depsZipName = "DesktopAppInstaller_Dependencies.zip"
$depsZipUrl = Get-AssetUrl -release $latestRelease -assetName $depsZipName
# --- Download Steps (Skipped if using existing files) ---
if (-not $ExistingInstallerFilesPath) {
$latestRelease = Get-LatestRelease
if (-not $latestRelease) { Write-Error "Could not retrieve the latest release. Exiting."; return; }
# We'll expand to a base 'Dependencies' folder
$topDepsFolder = Join-Path $downloadPath "Dependencies"
# Then pick the sub-folder for the architecture
$depsFolder = Join-Path $topDepsFolder $arch
$latestTag = $latestRelease.tag_name
Write-Host "Latest winget version tag is: $latestTag"
if ($depsZipUrl) {
Write-Host "Downloading $depsZipName..."
$depsZipPath = Join-Path $downloadPath $depsZipName
Invoke-WebRequest -Uri $depsZipUrl -OutFile $depsZipPath
# Download the MSIX bundle
$msixUrl = Get-AssetUrl -release $latestRelease -assetName $msixName
if (-not $msixUrl) { Write-Error "Could not find $msixName in the latest release assets."; return; }
# Remove existing Dependencies folder and expand the zip
if (Test-Path $topDepsFolder) { Remove-Item -Path $topDepsFolder -Recurse -Force }
Write-Host "Downloading $msixName..."
$msixPath = Join-Path $workingDir $msixName
Invoke-WebRequest -Uri $msixUrl -OutFile $msixPath
# Use Expand-Archive cmdlet by default because it's safe for constrained language mode. Fall back to .NET assembly if it fails.
try {
Expand-Archive -LiteralPath $depsZipPath -DestinationPath $topDepsFolder -Force -ErrorAction Stop
}
catch {
Write-Warning "Standard extraction failed, attempting .NET fallback. The error was: $($_.Exception.Message)"
# Fallback using .NET System.IO.Compression (Fixes issues in non-EN Windows Sandbox)
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory($depsZipPath, $topDepsFolder)
# Download the dependencies zip
$depsZipName = "DesktopAppInstaller_Dependencies.zip"
$depsZipUrl = Get-AssetUrl -release $latestRelease -assetName $depsZipName
# We'll expand to a base 'Dependencies' folder
$topDepsFolder = Join-Path $workingDir "Dependencies"
if ($depsZipUrl) {
Write-Host "Downloading $depsZipName..."
$depsZipPath = Join-Path $workingDir $depsZipName
Invoke-WebRequest -Uri $depsZipUrl -OutFile $depsZipPath
# Remove existing Dependencies folder and expand the zip
if (Test-Path $topDepsFolder) { Remove-Item -Path $topDepsFolder -Recurse -Force }
# Use Expand-Archive cmdlet by default because it's safe for constrained language mode. Fall back to .NET assembly if it fails.
try {
Expand-Archive -LiteralPath $depsZipPath -DestinationPath $topDepsFolder -Force -ErrorAction Stop
}
catch {
Write-Warning "Standard extraction failed, attempting .NET fallback. The error was: $($_.Exception.Message)"
# Fallback using .NET System.IO.Compression (Fixes issues in non-EN Windows Sandbox)
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory($depsZipPath, $topDepsFolder)
}
# Cleanup the zip file
if (Test-Path $depsZipPath) {
Remove-Item -Path $depsZipPath -Force
}
}
else { Write-Warning "No $depsZipName found in $latestTag, skipping dependency download."; }
}
else { Write-Warning "No $depsZipName found in $latestTag, skipping dependency download."; }
# Restore progress preference
$ProgressPreference = 'Continue'
# If dependencies exist for this architecture, install them
# --- Installation Steps ---
# Define paths based on working directory
$msixPath = Join-Path $workingDir $msixName
$topDepsFolder = Join-Path $workingDir "Dependencies"
$depsFolder = Join-Path $topDepsFolder $arch
# Install Dependencies
if (Test-Path $depsFolder) {
Install-WingetDependencies -depsFolder $depsFolder
} else {
Write-Warning "No architecture-specific dependencies found at $depsFolder"
if ($ExistingInstallerFilesPath) {
Write-Error "Dependencies folder not found at: $depsFolder`nEnsure the 'Dependencies' folder is present in your source directory."
} else {
Write-Warning "No architecture-specific dependencies found at $depsFolder"
}
}
# Finally, install the winget MSIX bundle
Write-Host "Installing $msixName..."
Add-AppxPackage -Path $msixPath
# Install Winget MSIX bundle
if (Test-Path $msixPath) {
Write-Host "Installing $msixName..."
Add-AppxPackage -Path $msixPath
} else {
Write-Error "Winget package not found at: $msixPath"
return
}
# Remove msstore source if set to do so
if ($removeMsStoreAsSource.IsPresent) {