mirror of
https://github.com/Mintplex-Labs/winget-pkgs.git
synced 2026-07-01 18:28:34 -04:00
Refactor SandboxTest (#188570)
Co-authored-by: Muhammad Danish <mdanishkhdev@gmail.com>
This commit is contained in:
@@ -203,6 +203,8 @@ BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.msix
|
||||
*.msixbundle
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
|
||||
+535
-287
@@ -1,269 +1,530 @@
|
||||
# Parse arguments
|
||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = 'This script is not intended to have any outputs piped')]
|
||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Prerelease', Justification = 'The variable is used in a conditional but ScriptAnalyser does not recognize the scope')]
|
||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'WinGetVersion', Justification = 'The variable is used in a conditional but ScriptAnalyser does not recognize the scope')]
|
||||
### Exit Codes:
|
||||
# -1 = Sandbox is not enabled
|
||||
# 0 = Success
|
||||
# 1 = Error fetching GitHub release
|
||||
# 2 = Unable to kill a running process
|
||||
# 3 = WinGet is not installed
|
||||
# 4 = Manifest validation error
|
||||
###
|
||||
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[Parameter(Position = 0, HelpMessage = 'The Manifest to install in the Sandbox.')]
|
||||
[String] $Manifest,
|
||||
[Parameter(Position = 1, HelpMessage = 'The script to run in the Sandbox.')]
|
||||
[ScriptBlock] $Script,
|
||||
[Parameter(HelpMessage = 'The folder to map in the Sandbox.')]
|
||||
[String] $MapFolder = $pwd,
|
||||
[switch] $SkipManifestValidation,
|
||||
[switch] $Prerelease,
|
||||
[switch] $EnableExperimentalFeatures,
|
||||
[string] $WinGetVersion,
|
||||
[Parameter(HelpMessage = 'Additional options for WinGet')]
|
||||
[string] $WinGetOptions
|
||||
# Manifest
|
||||
[Parameter(Position = 0, HelpMessage = 'The Manifest to install in the Sandbox.')]
|
||||
[ValidateScript({
|
||||
if (-Not (Test-Path -Path $_)) { throw "$_ does not exist" }
|
||||
return $true
|
||||
})]
|
||||
[String] $Manifest,
|
||||
# Script
|
||||
[Parameter(Position = 1, HelpMessage = 'The script to run in the Sandbox.')]
|
||||
[ScriptBlock] $Script,
|
||||
# MapFolder
|
||||
[Parameter(HelpMessage = 'The folder to map in the Sandbox.')]
|
||||
[ValidateScript({
|
||||
if (-Not (Test-Path -Path $_ -PathType Container)) { throw "$_ is not a folder." }
|
||||
return $true
|
||||
})]
|
||||
[String] $MapFolder = $pwd,
|
||||
# WinGetVersion
|
||||
[Parameter(HelpMessage = 'The version of WinGet to use')]
|
||||
[string] $WinGetVersion,
|
||||
# WinGetOptions
|
||||
[Parameter(HelpMessage = 'Additional options for WinGet')]
|
||||
[string] $WinGetOptions,
|
||||
# Switches
|
||||
[switch] $SkipManifestValidation,
|
||||
[switch] $Prerelease,
|
||||
[switch] $EnableExperimentalFeatures,
|
||||
[switch] $Clean
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$useNuGetForMicrosoftUIXaml = $false
|
||||
$mapFolder = (Resolve-Path -Path $MapFolder).Path
|
||||
$geoID = (Get-WinHomeLocation).GeoID
|
||||
|
||||
if (-Not (Test-Path -Path $mapFolder -PathType Container)) {
|
||||
Write-Error -Category InvalidArgument -Message 'The provided MapFolder is not a folder.'
|
||||
enum DependencySources {
|
||||
InRelease
|
||||
Legacy
|
||||
}
|
||||
|
||||
# Validate manifest file
|
||||
# Script Behaviors
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
$ErrorActionPreference = 'Stop' # This gets overriden most places, but is set explicitly here to help catch errors
|
||||
if ($PSBoundParameters.Keys -notcontains 'InformationAction') { $InformationPreference = 'Continue' } # If the user didn't explicitly set an InformationAction, Override their preference
|
||||
$script:OnMappedFolderWarning = ($PSBoundParameters.Keys -contains 'WarningAction') ? $PSBoundParameters.WarningAction : 'Inquire'
|
||||
$script:UseNuGetForMicrosoftUIXaml = $false
|
||||
$script:ScriptName = 'SandboxTest'
|
||||
$script:AppInstallerPFN = 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe'
|
||||
$script:DependenciesBaseName = 'DesktopAppInstaller_Dependencies'
|
||||
$script:ReleasesApiUrl = 'https://api.github.com/repos/microsoft/winget-cli/releases?per_page=100'
|
||||
$script:DependencySource = [DependencySources]::InRelease
|
||||
$script:UsePowerShellModuleForInstall = $false
|
||||
|
||||
if (-Not $SkipManifestValidation -And -Not [String]::IsNullOrWhiteSpace($Manifest)) {
|
||||
Write-Host '--> Validating Manifest'
|
||||
# File Names
|
||||
$script:AppInstallerMsixFileName = "$script:AppInstallerPFN.msixbundle" # This should exactly match the name of the file in the CLI GitHub Release
|
||||
$script:DependenciesZipFileName = "$script:DependenciesBaseName.zip" # This should exactly match the name of the file in the CLI GitHub Release
|
||||
|
||||
if (-Not (Test-Path -Path $Manifest)) {
|
||||
throw [System.IO.DirectoryNotFoundException]::new('The Manifest does not exist.')
|
||||
}
|
||||
# Download Urls
|
||||
$script:VcLibsDownloadUrl = 'https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx'
|
||||
$script:UiLibsDownloadUrl_v2_7 = 'https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.7.3/Microsoft.UI.Xaml.2.7.x64.appx'
|
||||
$script:UiLibsDownloadUrl_v2_8 = 'https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/Microsoft.UI.Xaml.2.8.x64.appx'
|
||||
$script:UiLibsDownloadUrl_NuGet = 'https://globalcdn.nuget.org/packages/microsoft.ui.xaml.2.8.6.nupkg?packageVersion=2.8.6'
|
||||
|
||||
winget.exe validate $Manifest
|
||||
switch ($LASTEXITCODE) {
|
||||
'-1978335191' { throw [System.Activities.ValidationException]::new('Manifest validation failed.') }
|
||||
'-1978335192' { Start-Sleep -Seconds 5 }
|
||||
Default { continue }
|
||||
}
|
||||
# Expected Hashes
|
||||
$script:VcLibsHash = 'B56A9101F706F9D95F815F5B7FA6EFBAC972E86573D378B96A07CFF5540C5961'
|
||||
$script:UiLibsHash_v2_7 = '8CE30D92ABEC6522BEB2544E7B716983F5CBA50751B580D89A36048BF4D90316'
|
||||
$script:UiLibsHash_v2_8 = '249D2AFB41CC009494841372BD6DD2DF46F87386D535DDF8D9F32C97226D2E46'
|
||||
$script:UiLibsHash_NuGet = '6B62BD3C277F55518C3738121B77585AC5E171C154936EC58D87268BBAE91736'
|
||||
|
||||
Write-Host
|
||||
# File Paths
|
||||
$script:AppInstallerDataFolder = Join-Path -Path $env:LOCALAPPDATA -ChildPath 'Packages' -AdditionalChildPath $script:AppInstallerPFN
|
||||
$script:DependenciesCacheFolder = Join-Path -Path $script:AppInstallerDataFolder -ChildPath "$script:ScriptName.Dependencies"
|
||||
$script:TestDataFolder = Join-Path -Path $script:AppInstallerDataFolder -ChildPath $script:ScriptName
|
||||
$script:PrimaryMappedFolder = (Resolve-Path -Path $MapFolder).Path
|
||||
$script:ConfigurationFile = Join-Path -Path $script:TestDataFolder -ChildPath "$script:ScriptName.wsb"
|
||||
|
||||
# Sandbox Settings
|
||||
$script:SandboxDesktopFolder = 'C:\Users\WDAGUtilityAccount\Desktop'
|
||||
$script:SandboxWorkingDirectory = Join-Path -Path $script:SandboxDesktopFolder -ChildPath $($script:PrimaryMappedFolder | Split-Path -Leaf)
|
||||
$script:SandboxTestDataFolder = Join-Path -Path $script:SandboxDesktopFolder -ChildPath $($script:TestDataFolder | Split-Path -Leaf)
|
||||
$script:SandboxBootstrapFile = Join-Path -Path $script:SandboxTestDataFolder -ChildPath "$script:ScriptName.ps1"
|
||||
$script:HostGeoID = (Get-WinHomeLocation).GeoID
|
||||
|
||||
# Misc
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$script:WebClient = New-Object System.Net.WebClient
|
||||
$script:CleanupPaths = @()
|
||||
|
||||
# The experimental features get updated later based on a switch that is set
|
||||
$script:SandboxWinGetSettings = @{
|
||||
'$schema' = 'https://aka.ms/winget-settings.schema.json'
|
||||
logging = @{
|
||||
level = 'verbose'
|
||||
}
|
||||
experimentalFeatures = @{
|
||||
fonts = $false
|
||||
}
|
||||
}
|
||||
|
||||
####
|
||||
# Description: Cleans up resources used by the script and then exits
|
||||
# Inputs: Exit code
|
||||
# Outputs: None
|
||||
####
|
||||
function Invoke-CleanExit {
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int] $ExitCode
|
||||
)
|
||||
Invoke-FileCleanup -FilePaths $script:CleanupPaths
|
||||
$script:WebClient.Dispose()
|
||||
Write-Debug "Exiting ($ExitCode)"
|
||||
exit $ExitCode
|
||||
}
|
||||
|
||||
####
|
||||
# Description: Ensures that a folder is present. Creates it if it does not exist
|
||||
# Inputs: Path to folder
|
||||
# Outputs: Boolean. True if path exists or was created; False if otherwise
|
||||
####
|
||||
function Initialize-Folder {
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[String] $FolderPath
|
||||
)
|
||||
$FolderPath = [System.Io.Path]::GetFullPath($FolderPath) # Normalize the path just in case the separation characters weren't quite right, or dot notation was used
|
||||
if (Test-Path -Path $FolderPath -PathType Container) { return $true } # The path exists and is a folder
|
||||
if (Test-Path -Path $FolderPath) { return $false } # The path exists but was not a folder
|
||||
Write-Debug "Initializing folder at $FolderPath"
|
||||
$directorySeparator = [System.IO.Path]::DirectorySeparatorChar
|
||||
|
||||
# Build the path up one part at a time. This is safer than using the `-Force` parameter on New-Item to create the directory
|
||||
foreach ($pathPart in $FolderPath.Split($directorySeparator)) {
|
||||
$builtPath += $pathPart + $directorySeparator
|
||||
if (!(Test-Path -Path $builtPath)) { New-Item -Path $builtPath -ItemType Directory | Out-Null }
|
||||
}
|
||||
|
||||
# Make sure that the path was actually created
|
||||
return Test-Path -Path $FolderPath
|
||||
}
|
||||
|
||||
####
|
||||
# Description: Gets the details for a specific WinGet CLI release
|
||||
# Inputs: None
|
||||
# Outputs: Nullable Object containing GitHub release details
|
||||
####
|
||||
function Get-Release {
|
||||
$releasesAPIResponse = Invoke-RestMethod $script:ReleasesApiUrl
|
||||
if (!$script:Prerelease) {
|
||||
$releasesAPIResponse = $releasesAPIResponse.Where({ !$_.prerelease })
|
||||
}
|
||||
if (![String]::IsNullOrWhiteSpace($script:WinGetVersion)) {
|
||||
$releasesAPIResponse = @($releasesAPIResponse.Where({ $_.tag_name -match $('^v?' + [regex]::escape($script:WinGetVersion)) }))
|
||||
}
|
||||
if ($releasesAPIResponse.Count -lt 1) { return $null }
|
||||
return $releasesAPIResponse | Sort-Object -Property published_at -Descending | Select-Object -First 1
|
||||
}
|
||||
|
||||
####
|
||||
# Description: Gets the content of a file from a URI
|
||||
# Inputs: Remote URI
|
||||
# Outputs: File Contents
|
||||
####
|
||||
function Get-RemoteContent {
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[AllowEmptyString()]
|
||||
[String] $URL,
|
||||
[String] $OutputPath = '',
|
||||
[switch] $Raw
|
||||
)
|
||||
Write-Debug "Attempting to fetch content from $URL"
|
||||
# Check if the URL is valid before trying to download
|
||||
$response = [String]::IsNullOrWhiteSpace($URL) ? @{StatusCode = 400 } : $(Invoke-WebRequest -Uri $URL -Method Head -ErrorAction SilentlyContinue) # If the URL is null, return a status code of 400
|
||||
if ($response.StatusCode -ne 200) {
|
||||
Write-Debug "Fetching remote content from $URL returned status code $($response.StatusCode)"
|
||||
return $null
|
||||
}
|
||||
$localFile = $OutputPath ? [System.IO.FileInfo]::new($OutputPath) : $(New-TemporaryFile) # If a path was specified, store it at that path; Otherwise use the temp folder
|
||||
Write-Debug "Remote content will be stored at $($localFile.FullName)"
|
||||
$script:CleanupPaths += $Raw ? @($localFile.FullName) : @() # Mark the file for cleanup when the script ends if the raw data was requested
|
||||
try {
|
||||
$script:WebClient.DownloadFile($URL, $localFile.FullName)
|
||||
}
|
||||
catch {
|
||||
# If the download fails, write a zero-byte file anyways
|
||||
$null | Out-File $localFile.FullName
|
||||
}
|
||||
return $Raw ? $(Get-Content -Path $localFile.FullName) : $localFile # If the raw content was requested, return the content, otherwise, return the FileInfo object
|
||||
}
|
||||
|
||||
####
|
||||
# Description: Removes files and folders from the file system
|
||||
# Inputs: List of paths to remove
|
||||
# Outputs: None
|
||||
####
|
||||
function Invoke-FileCleanup {
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[AllowEmptyString()]
|
||||
[AllowEmptyCollection()]
|
||||
[String[]] $FilePaths
|
||||
)
|
||||
if (!$FilePaths) { return }
|
||||
foreach ($path in $FilePaths) {
|
||||
Write-Debug "Removing $path"
|
||||
if (Test-Path $path) { Remove-Item -Path $path -Recurse }
|
||||
else { Write-Warning "Could not remove $path as it does not exist" }
|
||||
}
|
||||
}
|
||||
|
||||
####
|
||||
# Description: Stops a process and waits for it to terminate
|
||||
# Inputs: ProcessName, TimeoutSeconds
|
||||
# Outputs: None
|
||||
####
|
||||
function Stop-NamedProcess {
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[String] $ProcessName,
|
||||
[int] $TimeoutMilliseconds = 30000 # Default to 30 seconds
|
||||
)
|
||||
$process = Get-Process -Name $ProcessName -ErrorAction SilentlyContinue
|
||||
if (!$process) { return } # Process was not running
|
||||
|
||||
# Stop The Process
|
||||
Write-Information "--> Stopping $ProcessName"
|
||||
if ($PSCmdlet.ShouldProcess($process)) { $process | Stop-Process -WhatIf:$WhatIfPreference }
|
||||
|
||||
$elapsedTime = 0
|
||||
$waitMilliseconds = 500
|
||||
$processStillRunning = $true
|
||||
# Wait for the process to terminate
|
||||
do {
|
||||
$processStillRunning = Get-Process -Name $processName -ErrorAction SilentlyContinue
|
||||
if ($processStillRunning) {
|
||||
Write-Debug "$ProcessName is still running after $($elapsedTime/1000) seconds"
|
||||
Start-Sleep -Milliseconds $waitMilliseconds # Wait before checking again
|
||||
$elapsedTime += $waitMilliseconds
|
||||
}
|
||||
} while ($processStillRunning -and $elapsedTime -lt $TimeoutMilliseconds)
|
||||
|
||||
if ($processStillRunning) {
|
||||
Write-Error -Category OperationTimeout "Unable to terminate running process: $ProcessName" -ErrorAction Continue
|
||||
Invoke-CleanExit -ExitCode 2
|
||||
}
|
||||
}
|
||||
|
||||
####
|
||||
# Description: Ensures that a file has the expected checksum
|
||||
# Inputs: Expected Checksum, Path to file, Hashing algorithm
|
||||
# Outputs: Boolean
|
||||
####
|
||||
function Test-FileChecksum {
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[String] $ExpectedChecksum,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[String] $Path,
|
||||
[Parameter()]
|
||||
[String] $Algorithm = 'SHA256'
|
||||
)
|
||||
|
||||
# Get the hash of the file that is currently at the expected location for the dependency; This can be $null
|
||||
$currentHash = (Get-FileHash -Path $Path -Algorithm $Algorithm -ErrorAction SilentlyContinue).Hash
|
||||
return ($currentHash -eq $ExpectedChecksum)
|
||||
}
|
||||
|
||||
#### Start of main script ####
|
||||
|
||||
# Check if Windows Sandbox is enabled
|
||||
|
||||
if (-Not (Get-Command 'WindowsSandbox' -ErrorAction SilentlyContinue)) {
|
||||
Write-Error -Category NotInstalled -Message @'
|
||||
Write-Error -ErrorAction Continue -Category NotInstalled -Message @'
|
||||
Windows Sandbox does not seem to be available. Check the following URL for prerequisites and further details:
|
||||
https://docs.microsoft.com/windows/security/threat-protection/windows-sandbox/windows-sandbox-overview
|
||||
|
||||
You can run the following command in an elevated PowerShell for enabling Windows Sandbox:
|
||||
$ Enable-WindowsOptionalFeature -Online -FeatureName 'Containers-DisposableClientVM'
|
||||
'@
|
||||
Invoke-CleanExit -ExitCode -1
|
||||
}
|
||||
|
||||
# Close Windows Sandbox
|
||||
|
||||
$sandbox = Get-Process 'WindowsSandboxClient' -ErrorAction SilentlyContinue
|
||||
if ($sandbox) {
|
||||
Write-Host '--> Closing Windows Sandbox'
|
||||
|
||||
$sandbox | Stop-Process
|
||||
Start-Sleep -Seconds 5
|
||||
|
||||
Write-Host
|
||||
}
|
||||
Remove-Variable sandbox
|
||||
|
||||
# Initialize Temp Folder
|
||||
|
||||
$tempFolderName = 'SandboxTest'
|
||||
$tempFolder = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath $tempFolderName
|
||||
New-Item $tempFolder -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
|
||||
|
||||
# Set dependencies
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$WebClient = New-Object System.Net.WebClient
|
||||
|
||||
function Get-Release {
|
||||
$releasesAPIResponse = Invoke-RestMethod 'https://api.github.com/repos/microsoft/winget-cli/releases?per_page=100'
|
||||
if (!$script:Prerelease) {
|
||||
$releasesAPIResponse = $releasesAPIResponse.Where({ !$_.prerelease })
|
||||
}
|
||||
if (![String]::IsNullOrWhiteSpace($script:WinGetVersion)) {
|
||||
$releasesAPIResponse = @($releasesAPIResponse.Where({ $_.tag_name -match $('^v?' + [regex]::escape($script:WinGetVersion)) }))
|
||||
}
|
||||
if ($releasesAPIResponse.Count -lt 1) {
|
||||
Write-Output 'No WinGet releases found matching criteria'
|
||||
exit 1
|
||||
}
|
||||
$releasesAPIResponse = $releasesAPIResponse | Sort-Object -Property published_at -Descending
|
||||
|
||||
$assets = $releasesAPIResponse[0].assets
|
||||
$shaFileUrl = $assets.Where({ $_.name -eq 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.txt' }).browser_download_url
|
||||
$shaFile = New-TemporaryFile
|
||||
$WebClient.DownloadFile($shaFileUrl, $shaFile.FullName)
|
||||
|
||||
return @{
|
||||
shaFileUrl = $assets.Where({ $_.name -eq 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.txt' }).browser_download_url
|
||||
msixFileUrl = $assets.Where({ $_.name -eq 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle' }).browser_download_url
|
||||
releaseTag = $releasesAPIResponse[0].tag_name
|
||||
shaFileContent = $(Get-Content $shaFile.FullName)
|
||||
}
|
||||
# Validate the provided manifest
|
||||
if (!$SkipManifestValidation -and ![String]::IsNullOrWhiteSpace($Manifest)) {
|
||||
# Check that WinGet is Installed
|
||||
if (!(Get-Command 'winget.exe' -ErrorAction SilentlyContinue)) {
|
||||
Write-Error -Category NotInstalled "WinGet is not installed. Manifest cannot be validated" -ErrorAction Continue
|
||||
Invoke-CleanExit -ExitCode 3
|
||||
}
|
||||
Write-Information "--> Validating Manifest"
|
||||
$validateCommandOutput = winget.exe validate $Manifest
|
||||
switch ($LASTEXITCODE) {
|
||||
'-1978335191' {
|
||||
($validateCommandOutput | Select-Object -Skip 1 -SkipLast 1) | Write-Information # Skip the first line and the empty last line
|
||||
Write-Error -Category ParserError 'Manifest validation failed' -ErrorAction Continue
|
||||
Invoke-CleanExit -ExitCode 4
|
||||
}
|
||||
'-1978335192' {
|
||||
($validateCommandOutput | Select-Object -Skip 1 -SkipLast 1) | Write-Information # Skip the first line and the empty last line
|
||||
Write-Warning "Manifest validation succeeded with warnings"
|
||||
Start-Sleep -Seconds 5 # Allow the user 5 seconds to read the warnings before moving on
|
||||
}
|
||||
Default {
|
||||
$validateCommandOutput.Trim() | Write-Information # On the success, print an empty line after the command output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Hide the progress bar of Invoke-WebRequest
|
||||
$oldProgressPreference = $ProgressPreference
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
|
||||
$latestRelease = Get-Release
|
||||
$versionTag = $latestRelease.releaseTag
|
||||
$desktopAppInstaller = @{
|
||||
url = $latestRelease.msixFileUrl
|
||||
hash = $latestRelease.shaFileContent
|
||||
SaveTo = $(Join-Path $env:LOCALAPPDATA -ChildPath "Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\bin\$versionTag\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle")
|
||||
# Get the details for the version of WinGet that was requested
|
||||
Write-Verbose "Fetching release details from $script:ReleasesApiUrl; Filters: {Prerelease=$script:Prerelease; Version~=$script:WinGetVersion}"
|
||||
$script:WinGetReleaseDetails = Get-Release
|
||||
if (!$script:WinGetReleaseDetails) {
|
||||
Write-Error -Category ObjectNotFound 'No WinGet releases found matching criteria' -ErrorAction Continue
|
||||
Invoke-CleanExit -ExitCode 1
|
||||
}
|
||||
if (!$script:WinGetReleaseDetails.assets) {
|
||||
Write-Error -Category ResourceUnavailable 'Could not fetch WinGet CLI release assets' -ErrorAction Continue
|
||||
Invoke-CleanExit -ExitCode 1
|
||||
}
|
||||
|
||||
$ProgressPreference = $oldProgressPreference
|
||||
Write-Verbose "Parsing Release Information"
|
||||
# Parse the needed URLs out of the release. It is entirely possible that these could end up being $null
|
||||
$script:AppInstallerMsixShaDownloadUrl = $script:WinGetReleaseDetails.assets.Where({ $_.name -eq "$script:AppInstallerPFN.txt" }).browser_download_url
|
||||
$script:AppInstallerMsixDownloadUrl = $script:WinGetReleaseDetails.assets.Where({ $_.name -eq $script:AppInstallerMsixFileName }).browser_download_url
|
||||
$script:DependenciesShaDownloadUrl = $script:WinGetReleaseDetails.assets.Where({ $_.name -eq "$script:DependenciesBaseName.txt" }).browser_download_url
|
||||
$script:DependenciesZipDownloadUrl = $script:WinGetReleaseDetails.assets.Where({ $_.name -eq $script:DependenciesZipFileName }).browser_download_url
|
||||
Write-Debug @"
|
||||
|
||||
$vcLibsUwp = @{
|
||||
url = 'https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx'
|
||||
hash = 'B56A9101F706F9D95F815F5B7FA6EFBAC972E86573D378B96A07CFF5540C5961'
|
||||
SaveTo = $(Join-Path $tempFolder -ChildPath 'Microsoft.VCLibs.x64.14.00.Desktop.appx')
|
||||
}
|
||||
$uiLibsUwp_2_7 = @{
|
||||
url = 'https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.7.3/Microsoft.UI.Xaml.2.7.x64.appx'
|
||||
hash = '8CE30D92ABEC6522BEB2544E7B716983F5CBA50751B580D89A36048BF4D90316'
|
||||
SaveTo = $(Join-Path $tempFolder -ChildPath 'Microsoft.UI.Xaml.2.7.x64.appx')
|
||||
}
|
||||
$uiLibsUwp_2_8 = @{
|
||||
url = 'https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/Microsoft.UI.Xaml.2.8.x64.appx'
|
||||
hash = '249D2AFB41CC009494841372BD6DD2DF46F87386D535DDF8D9F32C97226D2E46'
|
||||
SaveTo = $(Join-Path $tempFolder -ChildPath 'Microsoft.UI.Xaml.2.8.x64.appx')
|
||||
}
|
||||
$uiLibs_nuget = @{
|
||||
url = 'https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.8.6'
|
||||
hash = '6B62BD3C277F55518C3738121B77585AC5E171C154936EC58D87268BBAE91736'
|
||||
SaveTo = $(Join-Path $tempFolder -ChildPath 'Microsoft.UI.Xaml.2.8.zip')
|
||||
}
|
||||
|
||||
$dependencies = @($desktopAppInstaller, $vcLibsUwp, $uiLibsUwp_2_7, $uiLibsUwp_2_8)
|
||||
|
||||
if ($useNuGetForMicrosoftUIXaml) {
|
||||
$dependencies += $uiLibs_nuget
|
||||
}
|
||||
|
||||
# Clean temp directory
|
||||
Get-ChildItem $tempFolder -Recurse -Exclude $($(Split-Path $dependencies.SaveTo -Leaf) -replace '\.([^\.]+)$', '.*') | Remove-Item -Force -Recurse
|
||||
|
||||
if (-Not [String]::IsNullOrWhiteSpace($Manifest)) {
|
||||
Copy-Item -Path $Manifest -Recurse -Destination $tempFolder
|
||||
}
|
||||
|
||||
# Download dependencies
|
||||
|
||||
Write-Host '--> Checking dependencies'
|
||||
|
||||
$desktopInSandbox = 'C:\Users\WDAGUtilityAccount\Desktop'
|
||||
|
||||
foreach ($dependency in $dependencies) {
|
||||
$dependency.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $(Split-Path $dependency.SaveTo -Leaf))
|
||||
|
||||
# Only download if the file does not exist, or its hash does not match.
|
||||
if (-Not ((Test-Path -Path $dependency.SaveTo) -And $dependency.hash -eq $(Get-FileHash $dependency.SaveTo).Hash)) {
|
||||
Write-Host @"
|
||||
- Downloading:
|
||||
$($dependency.url)
|
||||
AppInstallerMsixShaDownloadUrl = $script:AppInstallerMsixShaDownloadUrl
|
||||
AppInstallerMsixDownloadUrl = $script:AppInstallerMsixDownloadUrl
|
||||
DependenciesShaDownloadUrl = $script:DependenciesShaDownloadUrl
|
||||
DependenciesZipDownloadUrl = $script:DependenciesZipDownloadUrl
|
||||
"@
|
||||
|
||||
try {
|
||||
# If the directory doesn't already exist, create it
|
||||
$saveDirectory = Split-Path $dependency.SaveTo
|
||||
if (-Not (Test-Path -Path $saveDirectory)) {
|
||||
New-Item -ItemType Directory -Path $saveDirectory -Force | Out-Null
|
||||
}
|
||||
$WebClient.DownloadFile($dependency.url, $dependency.SaveTo)
|
||||
# Parse out the version
|
||||
$script:AppInstallerReleaseTag = $script:WinGetReleaseDetails.tag_name
|
||||
$script:AppInstallerParsedVersion = [System.Version]($script:AppInstallerReleaseTag -replace '(^v)|(-preview$)')
|
||||
Write-Debug "Using Release version $script:AppinstallerReleaseTag ($script:AppInstallerParsedVersion)"
|
||||
|
||||
} catch {
|
||||
# If the download failed, remove the item so the sandbox can fall-back to using the PowerShell module
|
||||
Remove-Item $dependency.SaveTo -Force | Out-Null
|
||||
# Get the hashes for the files that change with each release version
|
||||
Write-Verbose "Fetching file hash information"
|
||||
$script:AppInstallerMsixHash = Get-RemoteContent -URL $script:AppInstallerMsixShaDownloadUrl -Raw
|
||||
$script:DependenciesZipHash = Get-RemoteContent -URL $script:DependenciesShaDownloadUrl -Raw
|
||||
Write-Debug @"
|
||||
|
||||
AppInstallerMsixHash = $script:AppInstallerMsixHash
|
||||
DependenciesZipHash = $script:DependenciesZipHash
|
||||
"@
|
||||
|
||||
# Set the folder for the files that change with each release version
|
||||
$script:AppInstallerReleaseAssetsFolder = Join-Path $script:AppInstallerDataFolder -ChildPath 'bin' -AdditionalChildPath $script:AppInstallerReleaseTag
|
||||
|
||||
# Build the dependency information
|
||||
Write-Verbose "Building Dependency List"
|
||||
$script:AppInstallerDependencies = @()
|
||||
if ($script:AppInstallerParsedVersion -ge [System.Version]'1.9.25180') {
|
||||
# As of WinGet 1.9.25180, VCLibs no longer publishes to the public URL and must be downloaded from the WinGet release
|
||||
# Add the Zip file from the release to the dependencies
|
||||
Write-Debug "Adding $script:DependenciesZipFileName to dependency list"
|
||||
$script:AppInstallerDependencies += @{
|
||||
DownloadUrl = $script:DependenciesZipDownloadUrl
|
||||
Checksum = $script:DependenciesZipHash
|
||||
Algorithm = 'SHA256'
|
||||
SaveTo = (Join-Path -Path $script:AppInstallerReleaseAssetsFolder -ChildPath $script:DependenciesZipFileName)
|
||||
}
|
||||
if (-not ($dependency.hash -eq $(Get-FileHash $dependency.SaveTo).Hash)) {
|
||||
# If the hash didn't match, remove the item so the sandbox can fall-back to using the PowerShell module
|
||||
Write-Host -ForegroundColor Red ' Dependency hash does not match the downloaded file'
|
||||
Write-Host -ForegroundColor Red ' Please open an issue referencing this error at https://bit.ly/WinGet-SandboxTest-Needs-Update'
|
||||
Write-Host
|
||||
Remove-Item $dependency.SaveTo -Force | Out-Null
|
||||
}
|
||||
else {
|
||||
$script:DependencySource = [DependencySources]::Legacy
|
||||
# Add the VCLibs to the dependencies
|
||||
Write-Debug "Adding VCLibs UWP to dependency list"
|
||||
$script:AppInstallerDependencies += @{
|
||||
DownloadUrl = $script:VcLibsDownloadUrl
|
||||
Checksum = $script:VcLibsHash
|
||||
Algorithm = 'SHA256'
|
||||
SaveTo = (Join-Path -Path $script:DependenciesCacheFolder -ChildPath 'Microsoft.VCLibs.Desktop.x64.appx')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Copy the version of winget to the sandbox test folder
|
||||
Copy-Item -Path $desktopAppInstaller.SaveTo -Destination (Join-Path -Path $tempFolder -ChildPath (Split-Path $desktopAppInstaller.SaveTo -Leaf))
|
||||
|
||||
# Create Bootstrap settings
|
||||
# Experimental features can be enabled for forward compatibility with PR's
|
||||
$bootstrapSettingsContent = @{}
|
||||
$bootstrapSettingsContent['$schema'] = 'https://aka.ms/winget-settings.schema.json'
|
||||
$bootstrapSettingsContent['logging'] = @{level = 'verbose' }
|
||||
if ($EnableExperimentalFeatures) {
|
||||
$bootstrapSettingsContent['experimentalFeatures'] = @{
|
||||
dependencies = $true
|
||||
openLogsArgument = $true
|
||||
}
|
||||
}
|
||||
|
||||
$settingsFolderName = 'WingetSettings'
|
||||
$settingsFolder = Join-Path -Path $tempFolder -ChildPath $settingsFolderName
|
||||
|
||||
New-Item $settingsFolder -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
|
||||
$bootstrapSettingsFileName = 'settings.json'
|
||||
$bootstrapSettingsContent | ConvertTo-Json | Out-File (Join-Path -Path $settingsFolder -ChildPath $bootstrapSettingsFileName) -Encoding ascii
|
||||
$settingsPathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath "$settingsFolderName\settings.json")
|
||||
|
||||
# Create Bootstrap script
|
||||
|
||||
# See: https://stackoverflow.com/a/22670892/12156188
|
||||
$bootstrapPs1Content = @'
|
||||
function Update-EnvironmentVariables {
|
||||
foreach($level in "Machine","User") {
|
||||
[Environment]::GetEnvironmentVariables($level).GetEnumerator() | % {
|
||||
# For Path variables, append the new values, if they're not already in there
|
||||
if($_.Name -match '^Path$') {
|
||||
$_.Value = ($((Get-Content "Env:$($_.Name)") + ";$($_.Value)") -split ';' | Select -unique) -join ';'
|
||||
if ($script:UseNuGetForMicrosoftUIXaml) {
|
||||
# Add the NuGet file to the dependencies
|
||||
Write-Debug "Adding Microsoft.UI.Xaml (NuGet) to dependency list"
|
||||
$script:AppInstallerDependencies += @{
|
||||
DownloadUrl = $script:UiLibsDownloadUrl_NuGet
|
||||
Checksum = $script:UiLibsHash_NuGet
|
||||
Algorithm = 'SHA256'
|
||||
SaveTo = (Join-Path -Path $script:DependenciesCacheFolder -ChildPath 'Microsoft.UI.Xaml.zip')
|
||||
}
|
||||
$_
|
||||
} | Set-Content -Path { "Env:$($_.Name)" }
|
||||
}
|
||||
}
|
||||
# As of WinGet 1.7.10514 (https://github.com/microsoft/winget-cli/pull/4218), the dependency on uiLibsUwP was bumped from version 2.7.3 to version 2.8.6
|
||||
elseif ($script:AppInstallerParsedVersion -lt [System.Version]'1.7.10514') {
|
||||
# Add Xaml 2.7 to the dependencies
|
||||
Write-Debug "Adding Microsoft.UI.Xaml (v2.7) to dependency list"
|
||||
$script:AppInstallerDependencies += @{
|
||||
DownloadUrl = $script:UiLibsDownloadUrl_v2_7
|
||||
Checksum = $script:UiLibsHash_v2_7
|
||||
Algorithm = 'SHA256'
|
||||
SaveTo = (Join-Path -Path $script:DependenciesCacheFolder -ChildPath 'Microsoft.UI.Xaml.2.7.x64.appx')
|
||||
}
|
||||
}
|
||||
else {
|
||||
# Add Xaml 2.8 to the dependencies
|
||||
Write-Debug "Adding Microsoft.UI.Xaml (v2.8) to dependency list"
|
||||
$script:AppInstallerDependencies += @{
|
||||
DownloadUrl = $script:UiLibsDownloadUrl_v2_8
|
||||
Checksum = $script:UiLibsHash_v2_8
|
||||
Algorithm = 'SHA256'
|
||||
SaveTo = (Join-Path -Path $script:DependenciesCacheFolder -ChildPath 'Microsoft.UI.Xaml.2.8.x64.appx')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Add WinGet as a dependency for itself
|
||||
# This seems weird, but it's the easiest way to ensure that it is downloaded and has the right hash
|
||||
Write-Debug "Adding $script:AppInstallerMsixFileName ($script:AppInstallerReleaseTag) to dependency list"
|
||||
$script:AppInstallerDependencies += @{
|
||||
DownloadUrl = $script:AppInstallerMsixDownloadUrl
|
||||
Checksum = $script:AppInstallerMsixHash
|
||||
Algorithm = 'SHA256'
|
||||
SaveTo = (Join-Path -Path $script:AppInstallerReleaseAssetsFolder -ChildPath $script:AppInstallerMsixFileName)
|
||||
}
|
||||
|
||||
# If the PowerShell Module will be used, destroy the dependency list that was just created
|
||||
# This is cleaner than adding if statements everywhere to try and handle this flag.
|
||||
# Since the time it takes to build the dependency tree is minimal, don't worry about performance yet
|
||||
if ($script:UsePowerShellModuleForInstall) {
|
||||
$script:AppInstallerDependencies = @()
|
||||
}
|
||||
|
||||
# Process the dependency list
|
||||
Write-Information "--> Checking Dependencies"
|
||||
foreach ($dependency in $script:AppInstallerDependencies) {
|
||||
# On a clean install, remove the existing files
|
||||
if ($Clean) { Invoke-FileCleanup -FilePaths $dependency.SaveTo }
|
||||
|
||||
# If the hash doesn't match, the dependency needs to be re-downloaded
|
||||
# If the file doesn't exist on the system, the hashes will not match since $null != ''
|
||||
Write-Verbose "Checking the hash of $($dependency.SaveTo)"
|
||||
if (!(Test-FileChecksum -ExpectedChecksum $dependency.Checksum -Path $dependency.SaveTo -Algorithm $dependency.Algorithm)) {
|
||||
if (!(Initialize-Folder $($dependency.SaveTo | Split-Path))) { throw "Could not create folder for caching $($dependency.DownloadUrl)" } # The folder needs to be present, otherwise the WebClient request will fail
|
||||
Write-Information " - Downloading $($dependency.DownloadUrl)"
|
||||
Get-RemoteContent -URL $dependency.DownloadUrl -OutputPath $dependency.SaveTo -ErrorAction SilentlyContinue | Out-Null
|
||||
}
|
||||
|
||||
# If the hash didn't match, remove the item so the sandbox can fall-back to using the PowerShell module
|
||||
if (!(Test-FileChecksum -ExpectedChecksum $dependency.Checksum -Path $dependency.SaveTo -Algorithm $dependency.Algorithm)) {
|
||||
$script:UsePowerShellModuleForInstall = $true
|
||||
Write-Debug "Hashes did not match; Expected $($dependency.Checksum), Received $((Get-FileHash $dependency.SaveTo -Algorithm $dependency.Algorithm -ErrorAction Continue).Hash)"
|
||||
Remove-Item $dependency.SaveTo -Force | Out-Null
|
||||
# Continue on these errors because the PowerShell module will be used instead
|
||||
Write-Error -Category SecurityError 'Dependency hash does not match the downloaded file' -ErrorAction Continue
|
||||
Write-Error -Category SecurityError 'Please open an issue referencing this error at https://bit.ly/WinGet-SandboxTest-Needs-Update' -ErrorAction Continue
|
||||
break # Skip processing further dependencies, since the PowerShell Module will be used
|
||||
}
|
||||
}
|
||||
|
||||
# Kill the active running sandbox, if it exists, otherwise the test data folder can't be removed
|
||||
Stop-NamedProcess -ProcessName 'WindowsSandboxClient'
|
||||
Start-Sleep -Milliseconds 5000 # Wait for the lock on the file to be released
|
||||
|
||||
# Remove the test data folder if it exists. We will rebuild it with new test data
|
||||
Write-Verbose "Cleaning up previous test data"
|
||||
Invoke-FileCleanup -FilePaths $script:TestDataFolder
|
||||
|
||||
# Create the paths if they don't exist
|
||||
if (!(Initialize-Folder $script:TestDataFolder)) { throw 'Could not create folder for mapping files into the sandbox' }
|
||||
if (!(Initialize-Folder $script:DependenciesCacheFolder)) { throw 'Could not create folder for caching dependencies' }
|
||||
|
||||
# Set Experimental Features to be Enabled, If requested
|
||||
if ($EnableExperimentalFeatures) {
|
||||
Write-Debug "Setting Experimental Features to Enabled"
|
||||
$experimentalFeatures = @($script:SandboxWinGetSettings.experimentalFeatures.Keys)
|
||||
foreach ($feature in $experimentalFeatures) {
|
||||
$script:SandboxWinGetSettings.experimentalFeatures[$feature] = $true
|
||||
}
|
||||
}
|
||||
|
||||
# Copy Files to the TestDataFolder that will be mapped into sandbox
|
||||
Write-Verbose "Copying assets into $script:TestDataFolder"
|
||||
if ($Manifest) { Copy-Item -Path $Manifest -Destination $script:TestDataFolder -Recurse -ErrorAction SilentlyContinue }
|
||||
$script:SandboxWinGetSettings | ConvertTo-Json | Out-File -FilePath (Join-Path -Path $script:TestDataFolder -ChildPath 'settings.json') -Encoding ascii
|
||||
foreach ($dependency in $script:AppInstallerDependencies) { Copy-Item -Path $dependency.SaveTo -Destination $script:TestDataFolder -ErrorAction SilentlyContinue }
|
||||
|
||||
# Create a script file from the script parameter
|
||||
if (-Not [String]::IsNullOrWhiteSpace($Script)) {
|
||||
Write-Verbose "Creating script file from 'Script' argument"
|
||||
$Script | Out-File -Path (Join-Path $script:TestDataFolder -ChildPath 'BoundParameterScript.ps1')
|
||||
}
|
||||
|
||||
# Create the bootstrapping script
|
||||
Write-Verbose "Creating the script for bootstrapping the sandbox"
|
||||
@"
|
||||
function Update-EnvironmentVariables {
|
||||
foreach(`$level in "Machine","User") {
|
||||
[Environment]::GetEnvironmentVariables(`$level).GetEnumerator() | % {
|
||||
# For Path variables, append the new values, if they're not already in there
|
||||
if(`$_.Name -match '^Path$') {
|
||||
`$_.Value = (`$((Get-Content "Env:`$(`$_.Name)") + ";`$(`$_.Value)") -split ';' | Select -unique) -join ';'
|
||||
}
|
||||
`$_
|
||||
} | Set-Content -Path { "Env:`$(`$_.Name)" }
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ARPTable {
|
||||
$registry_paths = @('HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*','HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKCU:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*')
|
||||
return Get-ItemProperty $registry_paths -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.DisplayName -and (-not $_.SystemComponent -or $_.SystemComponent -ne 1 ) } |
|
||||
Select-Object DisplayName, DisplayVersion, Publisher, @{N='ProductCode'; E={$_.PSChildName}}, @{N='Scope'; E={if($_.PSDrive.Name -eq 'HKCU') {'User'} else {'Machine'}}}
|
||||
`$registry_paths = @('HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*','HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKCU:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*')
|
||||
return Get-ItemProperty `$registry_paths -ErrorAction SilentlyContinue |
|
||||
Where-Object { `$_.DisplayName -and (-not `$_.SystemComponent -or `$_.SystemComponent -ne 1 ) } |
|
||||
Select-Object DisplayName, DisplayVersion, Publisher, @{N='ProductCode'; E={`$_.PSChildName}}, @{N='Scope'; E={if(`$_.PSDrive.Name -eq 'HKCU') {'User'} else {'Machine'}}}
|
||||
}
|
||||
'@
|
||||
|
||||
### The NuGet may be needed if the latest Appx Packages are not available on GitHub ###
|
||||
if ($useNuGetForMicrosoftUIXaml) {
|
||||
$bootstrapPs1Content += @"
|
||||
`$ProgressPreference = 'SilentlyContinue'
|
||||
|
||||
Expand-Archive -Path $($uiLibs_nuget.pathInSandbox) -DestinationPath C:\Users\WDAGUtilityAccount\Downloads\Microsoft.UI.Xaml -ErrorAction SilentlyContinue
|
||||
Get-ChildItem C:\Users\WDAGUtilityAccount\Downloads\Microsoft.UI.Xaml\tools\AppX\x64\Release -Filter *.appx | Add-AppxPackage
|
||||
|
||||
"@
|
||||
}
|
||||
#######################################################################################
|
||||
|
||||
$bootstrapPs1Content += @"
|
||||
Push-Location $($script:SandboxTestDataFolder)
|
||||
Write-Host @'
|
||||
--> Installing WinGet
|
||||
'@
|
||||
`$ProgressPreference = 'SilentlyContinue'
|
||||
|
||||
try {
|
||||
Add-AppxPackage -Path '$($vcLibsUwp.pathInSandbox)' -ErrorAction Stop
|
||||
Add-AppxPackage -Path '$($uiLibsUwp_2_7.pathInSandbox)' -ErrorAction Stop
|
||||
Add-AppxPackage -Path '$($uiLibsUwp_2_8.pathInSandbox)' -ErrorAction Stop
|
||||
Add-AppxPackage -Path '$($desktopAppInstaller.pathInSandbox)' -ErrorAction Stop
|
||||
if ($([int]$script:UsePowerShellModuleForInstall)) { throw } # Using exceptions for control logic is generally not preferred, but is done here to keep things clean and readable
|
||||
Get-ChildItem -Filter '*.zip' | Expand-Archive
|
||||
Get-ChildItem -Recurse -Filter '*.appx' | Where-Object {`$_.FullName -match 'x64'} | Add-AppxPackage -ErrorAction Stop
|
||||
# This path is set explicitly instead of using Get-ChildItem as an error prevention measure
|
||||
Add-AppxPackage './$($script:AppInstallerPFN).msixbundle' -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Host -ForegroundColor Red 'Could not install from cached packages. Falling back to Repair-WinGetPackageManager cmdlet'
|
||||
try {
|
||||
@@ -277,11 +538,11 @@ try {
|
||||
throw "Microsoft.Winget.Client was not found. Check that the Windows Package Manager PowerShell module was installed correctly."
|
||||
}
|
||||
}
|
||||
Repair-WinGetPackageManager -Version $versionTag
|
||||
Repair-WinGetPackageManager -Version $($script:AppInstallerReleaseTag)
|
||||
}
|
||||
|
||||
Write-Host @'
|
||||
--> Disabling safety warning when running installer
|
||||
--> Disabling safety warning when running installers
|
||||
'@
|
||||
New-Item -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\Associations' | Out-Null
|
||||
New-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\Associations' -Name 'ModRiskFileTypes' -Type 'String' -Value '.bat;.exe;.reg;.vbs;.chm;.msi;.js;.cmd' | Out-Null
|
||||
@@ -290,118 +551,105 @@ Write-Host @'
|
||||
Tip: you can type 'Update-EnvironmentVariables' to update your environment variables, such as after installing a new software.
|
||||
'@
|
||||
|
||||
|
||||
"@
|
||||
|
||||
if (-Not [String]::IsNullOrWhiteSpace($Manifest)) {
|
||||
$manifestFileName = Split-Path $Manifest -Leaf
|
||||
$manifestPathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $manifestFileName)
|
||||
|
||||
$bootstrapPs1Content += @"
|
||||
Write-Host @'
|
||||
|
||||
--> Configuring Winget
|
||||
'@
|
||||
winget settings --Enable LocalManifestFiles
|
||||
winget settings --Enable LocalArchiveMalwareScanOverride
|
||||
copy -Path $settingsPathInSandbox -Destination C:\Users\WDAGUtilityAccount\AppData\Local\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\settings.json
|
||||
`$originalARP = Get-ARPTable
|
||||
Set-WinHomeLocation -GeoID $geoID
|
||||
Write-Host @'
|
||||
Get-ChildItem -Filter 'settings.json' | Copy-Item -Destination C:\Users\WDAGUtilityAccount\AppData\Local\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\settings.json
|
||||
Set-WinHomeLocation -GeoID $($script:HostGeoID)
|
||||
|
||||
--> Installing the Manifest $manifestFileName
|
||||
`$manifestFolder = (Get-ChildItem `$pwd -Directory).Where({Get-ChildItem `$_ -Filter '*.yaml'}).FullName | Select-Object -First 1
|
||||
if (`$manifestFolder) {
|
||||
Write-Host @"
|
||||
|
||||
'@
|
||||
winget install -m '$manifestPathInSandbox' --verbose-logs --ignore-local-archive-malware-scan --dependency-source winget $WinGetOptions
|
||||
--> Installing the Manifest `$(`$manifestFolder | Split-Path -Leaf)
|
||||
|
||||
Write-Host @'
|
||||
`"@
|
||||
`$originalARP = Get-ARPTable
|
||||
winget install -m `$manifestFolder --accept-package-agreements --verbose-logs --ignore-local-archive-malware-scan --dependency-source winget $WinGetOptions
|
||||
|
||||
Write-Host @'
|
||||
|
||||
--> Refreshing environment variables
|
||||
'@
|
||||
Update-EnvironmentVariables
|
||||
Update-EnvironmentVariables
|
||||
|
||||
Write-Host @'
|
||||
Write-Host @'
|
||||
|
||||
--> Comparing ARP Entries
|
||||
'@
|
||||
(Compare-Object (Get-ARPTable) `$originalARP -Property DisplayName,DisplayVersion,Publisher,ProductCode,Scope)| Select-Object -Property * -ExcludeProperty SideIndicator | Format-Table
|
||||
"@
|
||||
(Compare-Object (Get-ARPTable) `$originalARP -Property DisplayName,DisplayVersion,Publisher,ProductCode,Scope)| Select-Object -Property * -ExcludeProperty SideIndicator | Format-Table
|
||||
}
|
||||
|
||||
if (-Not [String]::IsNullOrWhiteSpace($Script)) {
|
||||
$bootstrapPs1Content += @"
|
||||
Write-Host @'
|
||||
`$BoundParameterScript = Get-ChildItem -Filter 'BoundParameterScript.ps1'
|
||||
if (`$BoundParameterScript) {
|
||||
Write-Host @'
|
||||
|
||||
--> Running the following script:
|
||||
|
||||
{
|
||||
$Script
|
||||
--> Running the following script: {
|
||||
`$(Get-Content -Path `$BoundParameterScript.FullName)
|
||||
}
|
||||
|
||||
'@
|
||||
|
||||
$Script
|
||||
|
||||
|
||||
"@
|
||||
& `$BoundParameterScript.FullName
|
||||
}
|
||||
|
||||
$bootstrapPs1FileName = 'Bootstrap.ps1'
|
||||
$bootstrapPs1Content | Out-File (Join-Path -Path $tempFolder -ChildPath $bootstrapPs1FileName)
|
||||
Pop-Location
|
||||
"@ | Out-File -FilePath $(Join-Path -Path $script:TestDataFolder -ChildPath "$script:ScriptName.ps1")
|
||||
|
||||
# Create Wsb file
|
||||
|
||||
$bootstrapPs1InSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $bootstrapPs1FileName)
|
||||
$mapFolderInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Split-Path -Path $mapFolder -Leaf)
|
||||
|
||||
$sandboxTestWsbContent = @"
|
||||
# Create the WSB file
|
||||
# Although this could be done using the native XML processor, it's easier to just write the content directly as a string
|
||||
Write-Verbose "Creating WSB file for launching the sandbox"
|
||||
@"
|
||||
<Configuration>
|
||||
<Networking>Enable</Networking>
|
||||
<MappedFolders>
|
||||
<MappedFolder>
|
||||
<HostFolder>$tempFolder</HostFolder>
|
||||
<ReadOnly>true</ReadOnly>
|
||||
<HostFolder>$($script:TestDataFolder)</HostFolder>
|
||||
</MappedFolder>
|
||||
<MappedFolder>
|
||||
<HostFolder>$mapFolder</HostFolder>
|
||||
<HostFolder>$($script:PrimaryMappedFolder)</HostFolder>
|
||||
</MappedFolder>
|
||||
</MappedFolders>
|
||||
<LogonCommand>
|
||||
<Command>PowerShell Start-Process PowerShell -WindowStyle Maximized -WorkingDirectory '$mapFolderInSandbox' -ArgumentList '-ExecutionPolicy Bypass -NoExit -NoLogo -File $bootstrapPs1InSandbox'</Command>
|
||||
<Command>PowerShell Start-Process PowerShell -WindowStyle Maximized -WorkingDirectory '$($script:SandboxWorkingDirectory)' -ArgumentList '-ExecutionPolicy Bypass -NoExit -NoLogo -File $($script:SandboxBootstrapFile)'</Command>
|
||||
</LogonCommand>
|
||||
</Configuration>
|
||||
"@
|
||||
"@ | Out-File -FilePath $script:ConfigurationFile
|
||||
|
||||
$sandboxTestWsbFileName = 'SandboxTest.wsb'
|
||||
$sandboxTestWsbFile = Join-Path -Path $tempFolder -ChildPath $sandboxTestWsbFileName
|
||||
$sandboxTestWsbContent | Out-File $sandboxTestWsbFile
|
||||
if ($script:PrimaryMappedFolder -notmatch 'winget-pkgs') {
|
||||
Write-Warning @"
|
||||
The mapped folder does not appear to be within the winget-pkgs repository path.
|
||||
This will give read-and-write access to $($script:PrimaryMappedFolder) within the sandbox
|
||||
"@ -WarningAction $script:OnMappedFolderWarning
|
||||
}
|
||||
|
||||
Write-Host @"
|
||||
Write-Information @"
|
||||
--> Starting Windows Sandbox, and:
|
||||
- Mounting the following directories:
|
||||
- $tempFolder as read-only
|
||||
- $mapFolder as read-and-write
|
||||
- $($script:TestDataFolder) as read-and-write
|
||||
- $($script:PrimaryMappedFolder) as read-and-write
|
||||
- Installing WinGet
|
||||
- Configuring Winget
|
||||
"@
|
||||
|
||||
if (-Not [String]::IsNullOrWhiteSpace($Manifest)) {
|
||||
Write-Host @"
|
||||
- Installing the Manifest $manifestFileName
|
||||
- Refreshing environment variables
|
||||
- Comparing ARP Entries
|
||||
Write-Information @"
|
||||
- Installing the Manifest $(Split-Path $Manifest -Leaf)
|
||||
- Refreshing environment variables
|
||||
- Comparing ARP Entries
|
||||
"@
|
||||
}
|
||||
|
||||
if (-Not [String]::IsNullOrWhiteSpace($Script)) {
|
||||
Write-Host @"
|
||||
- Running the following script:
|
||||
|
||||
{
|
||||
Write-Information @"
|
||||
- Running the following script: {
|
||||
$Script
|
||||
}
|
||||
"@
|
||||
}
|
||||
|
||||
Write-Host
|
||||
|
||||
WindowsSandbox $SandboxTestWsbFile
|
||||
Write-Verbose "Invoking the sandbox using $script:ConfigurationFile"
|
||||
WindowsSandbox $script:ConfigurationFile
|
||||
Invoke-CleanExit -ExitCode 0
|
||||
|
||||
Reference in New Issue
Block a user