Skip to content

Commit cefc320

Browse files
authored
Fix slow MSI package installations in Windows Sandbox (#340671)
1 parent 6ef2a29 commit cefc320

File tree

1 file changed

+41
-40
lines changed

1 file changed

+41
-40
lines changed

Tools/SandboxTest.ps1

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
###
99

1010
[CmdletBinding()]
11-
Param(
11+
param(
1212
# Manifest
1313
[Parameter(Position = 0, HelpMessage = 'The Manifest to install in the Sandbox.')]
1414
[ValidateScript({
15-
if (-Not (Test-Path -Path $_)) { throw "$_ does not exist" }
15+
if (-not (Test-Path -Path $_)) { throw "$_ does not exist" }
1616
return $true
1717
})]
1818
[String] $Manifest,
@@ -22,7 +22,7 @@ Param(
2222
# MapFolder
2323
[Parameter(HelpMessage = 'The folder to map in the Sandbox.')]
2424
[ValidateScript({
25-
if (-Not (Test-Path -Path $_ -PathType Container)) { throw "$_ is not a folder." }
25+
if (-not (Test-Path -Path $_ -PathType Container)) { throw "$_ is not a folder." }
2626
return $true
2727
})]
2828
[String] $MapFolder = $pwd,
@@ -165,7 +165,7 @@ function Initialize-Folder {
165165
####
166166
function Get-Release {
167167
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '',
168-
Justification='The standard workflow that users use with other applications requires the use of plaintext GitHub Access Tokens')]
168+
Justification = 'The standard workflow that users use with other applications requires the use of plaintext GitHub Access Tokens')]
169169

170170
param (
171171
[Parameter()]
@@ -183,8 +183,7 @@ function Get-Release {
183183
Write-Verbose 'Adding Bearer Token Authentication to Releases API Request'
184184
$requestParameters.Add('Authentication', 'Bearer')
185185
$requestParameters.Add('Token', $(ConvertTo-SecureString $GitHubToken -AsPlainText))
186-
}
187-
else {
186+
} else {
188187
# No token was provided or the token has expired
189188
# If an invalid token was provided, an exception will have been thrown before this code is reached
190189
Write-Warning @"
@@ -243,8 +242,7 @@ function Get-RemoteContent {
243242
try {
244243
$downloadTask = $script:HttpClient.GetByteArrayAsync($URL)
245244
[System.IO.File]::WriteAllBytes($localfile.FullName, $downloadTask.Result)
246-
}
247-
catch {
245+
} catch {
248246
# If the download fails, write a zero-byte file anyways
249247
$null | Out-File $localFile.FullName
250248
}
@@ -347,7 +345,7 @@ function Test-FileChecksum {
347345
####
348346
function Test-GithubToken {
349347
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '',
350-
Justification='The standard workflow that users use with other applications requires the use of plaintext GitHub Access Tokens')]
348+
Justification = 'The standard workflow that users use with other applications requires the use of plaintext GitHub Access Tokens')]
351349

352350
param (
353351
[Parameter(Mandatory = $true)]
@@ -403,7 +401,7 @@ function Test-GithubToken {
403401
$tokenExpirationDays = [Math]::Round($tokenExpirationDays, 2) # We don't need all the precision the system provides
404402

405403
if ($cachedExpirationForParsing -eq [System.DateTime]::MaxValue.ToLongDateString().Trim()) {
406-
Write-Verbose "The cached token contained content. It is set to never expire"
404+
Write-Verbose 'The cached token contained content. It is set to never expire'
407405
return $true
408406
}
409407

@@ -416,21 +414,18 @@ function Test-GithubToken {
416414
Write-Verbose 'The cached token contained content, but it could not be parsed as a date. It will be re-validated'
417415
Invoke-FileCleanup -FilePaths $cachedToken.FullName
418416
# Do not return anything, since the token will need to be re-validated
419-
}
420-
else {
417+
} else {
421418
Write-Verbose "The cached token contained content, but the token expired $([Math]::Abs($tokenExpirationDays)) days ago"
422419
# Leave the cached token so that it doesn't throw script exceptions in the future
423420
# Invoke-FileCleanup -FilePaths $cachedToken.FullName
424421
return $false
425422
}
426-
}
427-
else {
423+
} else {
428424
# Either the token was empty, or the cached token is expired. Remove the cached token so that re-validation
429425
# of the token will update the date the token was cached if it is still valid
430426
Invoke-FileCleanup -FilePaths $cachedToken.FullName
431427
}
432-
}
433-
else {
428+
} else {
434429
Write-Verbose 'Token was not found in the cache'
435430
}
436431

@@ -458,18 +453,18 @@ function Test-GithubToken {
458453
Write-Verbose 'Token validated successfully. Adding to cache'
459454
# Trim off any non-digit characters from the end
460455
# Strip off the array wrapper since it is no longer needed
461-
$tokenExpiration = $tokenExpiration[0] -replace '[^0-9]+$',''
456+
$tokenExpiration = $tokenExpiration[0] -replace '[^0-9]+$', ''
462457
# If the token doesn't expire, write a special value to the file
463458
if (!$tokenExpiration -or [string]::IsNullOrWhiteSpace($tokenExpiration)) {
464-
Write-Debug "Token expiration was empty, setting it to maximum"
459+
Write-Debug 'Token expiration was empty, setting it to maximum'
465460
$tokenExpiration = [System.DateTime]::MaxValue
466461
}
467462
# Try parsing the value to a datetime before storing it
468-
if ([DateTime]::TryParse($tokenExpiration,[ref]$tokenExpiration)) {
463+
if ([DateTime]::TryParse($tokenExpiration, [ref]$tokenExpiration)) {
469464
Write-Debug "Token expiration successfully parsed as DateTime ($tokenExpiration)"
470465
} else {
471466
# TryParse Failed
472-
Write-Warning "Could not parse expiration date as a DateTime object. It will be set to the minimum value"
467+
Write-Warning 'Could not parse expiration date as a DateTime object. It will be set to the minimum value'
473468
$tokenExpiration = [System.DateTime]::MinValue
474469
}
475470
# Explicitly convert to a string here to avoid implicit casting
@@ -483,7 +478,7 @@ function Test-GithubToken {
483478
#### Start of main script ####
484479

485480
# Check if Windows Sandbox is enabled
486-
if (-Not (Get-Command 'WindowsSandbox' -ErrorAction SilentlyContinue)) {
481+
if (-not (Get-Command 'WindowsSandbox' -ErrorAction SilentlyContinue)) {
487482
Write-Error -ErrorAction Continue -Category NotInstalled -Message @'
488483
Windows Sandbox does not seem to be available. Check the following URL for prerequisites and further details:
489484
https://docs.microsoft.com/windows/security/threat-protection/windows-sandbox/windows-sandbox-overview
@@ -501,20 +496,20 @@ if (!$SkipManifestValidation -and ![String]::IsNullOrWhiteSpace($Manifest)) {
501496
Write-Error -Category NotInstalled 'WinGet is not installed. Manifest cannot be validated' -ErrorAction Continue
502497
Invoke-CleanExit -ExitCode 3
503498
}
504-
Write-Information "--> Validating Manifest"
499+
Write-Information '--> Validating Manifest'
505500
$validateCommandOutput =
506-
& {
507-
# Store current output encoding setting
508-
$prevOutEnc = [Console]::OutputEncoding
509-
# Set [Console]::OutputEncoding to UTF-8 since winget uses UTF-8 for output
510-
[Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new()
501+
& {
502+
# Store current output encoding setting
503+
$prevOutEnc = [Console]::OutputEncoding
504+
# Set [Console]::OutputEncoding to UTF-8 since winget uses UTF-8 for output
505+
[Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new()
511506

512-
winget.exe validate $Manifest
507+
winget.exe validate $Manifest
513508

514-
# Reset the encoding to the previous values
515-
[Console]::OutputEncoding = $prevOutEnc
516-
}
517-
switch ($LASTEXITCODE) {
509+
# Reset the encoding to the previous values
510+
[Console]::OutputEncoding = $prevOutEnc
511+
}
512+
switch ($LASTEXITCODE) {
518513
'-1978335191' {
519514
# Skip the first line and the empty last line
520515
$validateCommandOutput | Select-Object -Skip 1 -SkipLast 1 | ForEach-Object {
@@ -532,7 +527,7 @@ if (!$SkipManifestValidation -and ![String]::IsNullOrWhiteSpace($Manifest)) {
532527
Write-Warning 'Manifest validation succeeded with warnings'
533528
Start-Sleep -Seconds 5 # Allow the user 5 seconds to read the warnings before moving on
534529
}
535-
Default {
530+
default {
536531
Write-Information $validateCommandOutput.Trim() # On the success, print an empty line after the command output
537532
}
538533
}
@@ -595,8 +590,7 @@ if ($script:AppInstallerParsedVersion -ge [System.Version]'1.9.25180') {
595590
Algorithm = 'SHA256'
596591
SaveTo = (Join-Path -Path $script:AppInstallerReleaseAssetsFolder -ChildPath $script:DependenciesZipFileName)
597592
}
598-
}
599-
else {
593+
} else {
600594
$script:DependencySource = [DependencySources]::Legacy
601595
# Add the VCLibs to the dependencies
602596
Write-Debug 'Adding VCLibs UWP to dependency list'
@@ -626,8 +620,7 @@ else {
626620
Algorithm = 'SHA256'
627621
SaveTo = (Join-Path -Path $script:DependenciesCacheFolder -ChildPath 'Microsoft.UI.Xaml.2.7.x64.appx')
628622
}
629-
}
630-
else {
623+
} else {
631624
# Add Xaml 2.8 to the dependencies
632625
Write-Debug 'Adding Microsoft.UI.Xaml (v2.8) to dependency list'
633626
$script:AppInstallerDependencies += @{
@@ -712,7 +705,7 @@ $script:SandboxWinGetSettings | ConvertTo-Json | Out-File -FilePath (Join-Path -
712705
foreach ($dependency in $script:AppInstallerDependencies) { Copy-Item -Path $dependency.SaveTo -Destination $script:TestDataFolder -ErrorAction SilentlyContinue }
713706

714707
# Create a script file from the script parameter
715-
if (-Not [String]::IsNullOrWhiteSpace($Script)) {
708+
if (-not [String]::IsNullOrWhiteSpace($Script)) {
716709
Write-Verbose "Creating script file from 'Script' argument"
717710
$Script | Out-File -Path (Join-Path $script:TestDataFolder -ChildPath 'BoundParameterScript.ps1')
718711
}
@@ -779,6 +772,14 @@ Tip: you can type 'Update-EnvironmentVariables' to update your environment varia
779772
780773
Write-Host @'
781774
775+
--> Fixing slow MSI package installers
776+
'@
777+
778+
reg add "HKLM\SYSTEM\CurrentControlSet\Control\CI\Policy" /v "VerifiedAndReputablePolicyState" /t REG_DWORD /d 0 /f # See: https://github.com/microsoft/Windows-Sandbox/issues/68#issuecomment-2754867968
779+
CiTool.exe --refresh --json | Out-Null # Refreshes policy. Use json output param or else it will prompt for confirmation, even with Out-Null
780+
781+
Write-Host @'
782+
782783
--> Configuring Winget
783784
'@
784785
winget settings --Enable LocalManifestFiles
@@ -860,15 +861,15 @@ Write-Information @"
860861
- Configuring Winget
861862
"@
862863

863-
if (-Not [String]::IsNullOrWhiteSpace($Manifest)) {
864+
if (-not [String]::IsNullOrWhiteSpace($Manifest)) {
864865
Write-Information @"
865866
- Installing the Manifest $(Split-Path $Manifest -Leaf)
866867
- Refreshing environment variables
867868
- Comparing ARP Entries
868869
"@
869870
}
870871

871-
if (-Not [String]::IsNullOrWhiteSpace($Script)) {
872+
if (-not [String]::IsNullOrWhiteSpace($Script)) {
872873
Write-Information @"
873874
- Running the following script: {
874875
$Script

0 commit comments

Comments
 (0)