diff --git a/.editorconfig b/.editorconfig index 11eae860efbde..a371d8f723ca9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,9 @@ -root=true - - -[*.{yml,yaml}] -end_of_line = crlf -insert_final_newline = true -# editorconfig/editorconfig#329 -indent_style = space -indent_size = 2 +root=true + + +[*.{yml,yaml}] +end_of_line = crlf +insert_final_newline = true +# editorconfig/editorconfig#329 +indent_style = space +indent_size = 2 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index c72a5749c52ac..f9ba8cf65f3e3 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,9 +1,9 @@ -# Microsoft Open Source Code of Conduct - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). - -Resources: - -- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) -- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns +# Microsoft Open Source Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns diff --git a/LICENSE b/LICENSE index 4b1ad51b2f0ef..21071075c2459 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/Tools/SandboxTest.ps1 b/Tools/SandboxTest.ps1 index abe2c6f6a3aef..a146a3616a132 100644 --- a/Tools/SandboxTest.ps1 +++ b/Tools/SandboxTest.ps1 @@ -1,340 +1,340 @@ -# Parse arguments - -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 -) - -$ErrorActionPreference = 'Stop' - -$mapFolder = (Resolve-Path -Path $MapFolder).Path - -if (-Not (Test-Path -Path $mapFolder -PathType Container)) { - Write-Error -Category InvalidArgument -Message 'The provided MapFolder is not a folder.' -} - -# Validate manifest file - -if (-Not $SkipManifestValidation -And -Not [String]::IsNullOrWhiteSpace($Manifest)) { - Write-Host '--> Validating Manifest' - - if (-Not (Test-Path -Path $Manifest)) { - throw [System.IO.DirectoryNotFoundException]::new('The Manifest does not exist.') - } - - winget.exe validate $Manifest - switch ($LASTEXITCODE) { - '-1978335191' { throw [System.Activities.ValidationException]::new('Manifest validation failed.') } - '-1978335192' { Start-Sleep -Seconds 5 } - Default { continue } - } - - Write-Host -} - -# Check if Windows Sandbox is enabled - -if (-Not (Get-Command 'WindowsSandbox' -ErrorAction SilentlyContinue)) { - Write-Error -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' -'@ -} - -# 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 - -$apiLatestUrl = if ($Prerelease) { 'https://api.github.com/repos/microsoft/winget-cli/releases?per_page=1' } else { 'https://api.github.com/repos/microsoft/winget-cli/releases/latest' } - - -[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -$WebClient = New-Object System.Net.WebClient - -function Get-LatestUrl { - ((Invoke-WebRequest $apiLatestUrl -UseBasicParsing | ConvertFrom-Json).assets | Where-Object { $_.name -match '^Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle$' }).browser_download_url -} - -function Get-LatestHash { - $shaUrl = ((Invoke-WebRequest $apiLatestUrl -UseBasicParsing | ConvertFrom-Json).assets | Where-Object { $_.name -match '^Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.txt$' }).browser_download_url - - $shaFile = Join-Path -Path $tempFolder -ChildPath 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.txt' - $WebClient.DownloadFile($shaUrl, $shaFile) - - Get-Content $shaFile -} - -# Hide the progress bar of Invoke-WebRequest -$oldProgressPreference = $ProgressPreference -$ProgressPreference = 'SilentlyContinue' - -$desktopAppInstaller = @{ - fileName = 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle' - url = $(Get-LatestUrl) - hash = $(Get-LatestHash) -} - -$ProgressPreference = $oldProgressPreference - -$vcLibsUwp = @{ - fileName = 'Microsoft.VCLibs.x64.14.00.Desktop.appx' - url = 'https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx' - hash = '9BFDE6CFCC530EF073AB4BC9C4817575F63BE1251DD75AAA58CB89299697A569' -} -$uiLibsUwp = @{ - fileName = 'Microsoft.UI.Xaml.2.7.zip' - url = 'https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.7.0' - hash = '422FD24B231E87A842C4DAEABC6A335112E0D35B86FAC91F5CE7CF327E36A591' -} - -$dependencies = @($desktopAppInstaller, $vcLibsUwp, $uiLibsUwp) - -# Clean temp directory - -Get-ChildItem $tempFolder -Recurse -Exclude $dependencies.fileName | 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.file = Join-Path -Path $tempFolder -ChildPath $dependency.fileName - $dependency.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $dependency.fileName) - - # Only download if the file does not exist, or its hash does not match. - if (-Not ((Test-Path -Path $dependency.file -PathType Leaf) -And $dependency.hash -eq $(Get-FileHash $dependency.file).Hash)) { - Write-Host @" - - Downloading: - $($dependency.url) -"@ - - try { - $WebClient.DownloadFile($dependency.url, $dependency.file) - } catch { - #Pass the exception as an inner exception - throw [System.Net.WebException]::new("Error downloading $($dependency.url).", $_.Exception) - } - if (-not ($dependency.hash -eq $(Get-FileHash $dependency.file).Hash)) { - throw [System.Activities.VersionMismatchException]::new('Dependency hash does not match the downloaded file') - } - } -} - -# Extract Microsoft.UI.Xaml from zip (if freshly downloaded). -# This is a workaround until https://github.com/microsoft/winget-cli/issues/1861 is resolved. - -if (-Not (Test-Path (Join-Path -Path $tempFolder -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx))) { - Expand-Archive -Path $uiLibsUwp.file -DestinationPath ($tempFolder + '\Microsoft.UI.Xaml.2.7') -Force -} -$uiLibsUwp.file = (Join-Path -Path $tempFolder -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx) -$uiLibsUwp.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx) -Write-Host - -# Create Bootstrap settings -# dependencies and zipInstall are 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 - zipInstall = $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 ';' - } - $_ - } | 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 | - Select-Object DisplayName, DisplayVersion, Publisher, @{N='ProductCode'; E={$_.PSChildName}} | - Where-Object {$null -ne $_.DisplayName } -} -'@ - -$bootstrapPs1Content += @" -Write-Host @' ---> Installing WinGet -'@ -`$ProgressPreference = 'SilentlyContinue' -Add-AppxPackage -Path '$($desktopAppInstaller.pathInSandbox)' -DependencyPath '$($vcLibsUwp.pathInSandbox)','$($uiLibsUwp.pathInSandbox)' - -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 -copy -Path $settingsPathInSandbox -Destination C:\Users\WDAGUtilityAccount\AppData\Local\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\settings.json -`$originalARP = Get-ARPTable -Write-Host @' - - ---> Installing the Manifest $manifestFileName - -'@ -winget install -m '$manifestPathInSandbox' --verbose-logs - -Write-Host @' - ---> Refreshing environment variables -'@ -Update-EnvironmentVariables - -Write-Host @' - ---> Comparing ARP Entries -'@ -(Compare-Object (Get-ARPTable) `$originalARP -Property DisplayName,DisplayVersion,Publisher,ProductCode)| Select-Object -Property * -ExcludeProperty SideIndicator | Format-Table - -"@ -} - -if (-Not [String]::IsNullOrWhiteSpace($Script)) { - $bootstrapPs1Content += @" -Write-Host @' - ---> Running the following script: - -{ -$Script -} - -'@ - -$Script - - -"@ -} - -$bootstrapPs1Content += @' -Write-Host -'@ - -$bootstrapPs1FileName = 'Bootstrap.ps1' -$bootstrapPs1Content | Out-File (Join-Path -Path $tempFolder -ChildPath $bootstrapPs1FileName) - -# 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 = @" - - - - $tempFolder - true - - - $mapFolder - - - - PowerShell Start-Process PowerShell -WindowStyle Maximized -WorkingDirectory '$mapFolderInSandbox' -ArgumentList '-ExecutionPolicy Bypass -NoExit -NoLogo -File $bootstrapPs1InSandbox' - - -"@ - -$sandboxTestWsbFileName = 'SandboxTest.wsb' -$sandboxTestWsbFile = Join-Path -Path $tempFolder -ChildPath $sandboxTestWsbFileName -$sandboxTestWsbContent | Out-File $sandboxTestWsbFile - -Write-Host @" ---> Starting Windows Sandbox, and: - - Mounting the following directories: - - $tempFolder as read-only - - $mapFolder 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 -"@ -} - -if (-Not [String]::IsNullOrWhiteSpace($Script)) { - Write-Host @" - - Running the following script: - -{ -$Script -} -"@ -} - -Write-Host - -WindowsSandbox $SandboxTestWsbFile - +# Parse arguments + +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 +) + +$ErrorActionPreference = 'Stop' + +$mapFolder = (Resolve-Path -Path $MapFolder).Path + +if (-Not (Test-Path -Path $mapFolder -PathType Container)) { + Write-Error -Category InvalidArgument -Message 'The provided MapFolder is not a folder.' +} + +# Validate manifest file + +if (-Not $SkipManifestValidation -And -Not [String]::IsNullOrWhiteSpace($Manifest)) { + Write-Host '--> Validating Manifest' + + if (-Not (Test-Path -Path $Manifest)) { + throw [System.IO.DirectoryNotFoundException]::new('The Manifest does not exist.') + } + + winget.exe validate $Manifest + switch ($LASTEXITCODE) { + '-1978335191' { throw [System.Activities.ValidationException]::new('Manifest validation failed.') } + '-1978335192' { Start-Sleep -Seconds 5 } + Default { continue } + } + + Write-Host +} + +# Check if Windows Sandbox is enabled + +if (-Not (Get-Command 'WindowsSandbox' -ErrorAction SilentlyContinue)) { + Write-Error -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' +'@ +} + +# 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 + +$apiLatestUrl = if ($Prerelease) { 'https://api.github.com/repos/microsoft/winget-cli/releases?per_page=1' } else { 'https://api.github.com/repos/microsoft/winget-cli/releases/latest' } + + +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$WebClient = New-Object System.Net.WebClient + +function Get-LatestUrl { + ((Invoke-WebRequest $apiLatestUrl -UseBasicParsing | ConvertFrom-Json).assets | Where-Object { $_.name -match '^Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle$' }).browser_download_url +} + +function Get-LatestHash { + $shaUrl = ((Invoke-WebRequest $apiLatestUrl -UseBasicParsing | ConvertFrom-Json).assets | Where-Object { $_.name -match '^Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.txt$' }).browser_download_url + + $shaFile = Join-Path -Path $tempFolder -ChildPath 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.txt' + $WebClient.DownloadFile($shaUrl, $shaFile) + + Get-Content $shaFile +} + +# Hide the progress bar of Invoke-WebRequest +$oldProgressPreference = $ProgressPreference +$ProgressPreference = 'SilentlyContinue' + +$desktopAppInstaller = @{ + fileName = 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle' + url = $(Get-LatestUrl) + hash = $(Get-LatestHash) +} + +$ProgressPreference = $oldProgressPreference + +$vcLibsUwp = @{ + fileName = 'Microsoft.VCLibs.x64.14.00.Desktop.appx' + url = 'https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx' + hash = '9BFDE6CFCC530EF073AB4BC9C4817575F63BE1251DD75AAA58CB89299697A569' +} +$uiLibsUwp = @{ + fileName = 'Microsoft.UI.Xaml.2.7.zip' + url = 'https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.7.0' + hash = '422FD24B231E87A842C4DAEABC6A335112E0D35B86FAC91F5CE7CF327E36A591' +} + +$dependencies = @($desktopAppInstaller, $vcLibsUwp, $uiLibsUwp) + +# Clean temp directory + +Get-ChildItem $tempFolder -Recurse -Exclude $dependencies.fileName | 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.file = Join-Path -Path $tempFolder -ChildPath $dependency.fileName + $dependency.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $dependency.fileName) + + # Only download if the file does not exist, or its hash does not match. + if (-Not ((Test-Path -Path $dependency.file -PathType Leaf) -And $dependency.hash -eq $(Get-FileHash $dependency.file).Hash)) { + Write-Host @" + - Downloading: + $($dependency.url) +"@ + + try { + $WebClient.DownloadFile($dependency.url, $dependency.file) + } catch { + #Pass the exception as an inner exception + throw [System.Net.WebException]::new("Error downloading $($dependency.url).", $_.Exception) + } + if (-not ($dependency.hash -eq $(Get-FileHash $dependency.file).Hash)) { + throw [System.Activities.VersionMismatchException]::new('Dependency hash does not match the downloaded file') + } + } +} + +# Extract Microsoft.UI.Xaml from zip (if freshly downloaded). +# This is a workaround until https://github.com/microsoft/winget-cli/issues/1861 is resolved. + +if (-Not (Test-Path (Join-Path -Path $tempFolder -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx))) { + Expand-Archive -Path $uiLibsUwp.file -DestinationPath ($tempFolder + '\Microsoft.UI.Xaml.2.7') -Force +} +$uiLibsUwp.file = (Join-Path -Path $tempFolder -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx) +$uiLibsUwp.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx) +Write-Host + +# Create Bootstrap settings +# dependencies and zipInstall are 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 + zipInstall = $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 ';' + } + $_ + } | 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 | + Select-Object DisplayName, DisplayVersion, Publisher, @{N='ProductCode'; E={$_.PSChildName}} | + Where-Object {$null -ne $_.DisplayName } +} +'@ + +$bootstrapPs1Content += @" +Write-Host @' +--> Installing WinGet +'@ +`$ProgressPreference = 'SilentlyContinue' +Add-AppxPackage -Path '$($desktopAppInstaller.pathInSandbox)' -DependencyPath '$($vcLibsUwp.pathInSandbox)','$($uiLibsUwp.pathInSandbox)' + +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 +copy -Path $settingsPathInSandbox -Destination C:\Users\WDAGUtilityAccount\AppData\Local\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\settings.json +`$originalARP = Get-ARPTable +Write-Host @' + + +--> Installing the Manifest $manifestFileName + +'@ +winget install -m '$manifestPathInSandbox' --verbose-logs + +Write-Host @' + +--> Refreshing environment variables +'@ +Update-EnvironmentVariables + +Write-Host @' + +--> Comparing ARP Entries +'@ +(Compare-Object (Get-ARPTable) `$originalARP -Property DisplayName,DisplayVersion,Publisher,ProductCode)| Select-Object -Property * -ExcludeProperty SideIndicator | Format-Table + +"@ +} + +if (-Not [String]::IsNullOrWhiteSpace($Script)) { + $bootstrapPs1Content += @" +Write-Host @' + +--> Running the following script: + +{ +$Script +} + +'@ + +$Script + + +"@ +} + +$bootstrapPs1Content += @' +Write-Host +'@ + +$bootstrapPs1FileName = 'Bootstrap.ps1' +$bootstrapPs1Content | Out-File (Join-Path -Path $tempFolder -ChildPath $bootstrapPs1FileName) + +# 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 = @" + + + + $tempFolder + true + + + $mapFolder + + + + PowerShell Start-Process PowerShell -WindowStyle Maximized -WorkingDirectory '$mapFolderInSandbox' -ArgumentList '-ExecutionPolicy Bypass -NoExit -NoLogo -File $bootstrapPs1InSandbox' + + +"@ + +$sandboxTestWsbFileName = 'SandboxTest.wsb' +$sandboxTestWsbFile = Join-Path -Path $tempFolder -ChildPath $sandboxTestWsbFileName +$sandboxTestWsbContent | Out-File $sandboxTestWsbFile + +Write-Host @" +--> Starting Windows Sandbox, and: + - Mounting the following directories: + - $tempFolder as read-only + - $mapFolder 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 +"@ +} + +if (-Not [String]::IsNullOrWhiteSpace($Script)) { + Write-Host @" + - Running the following script: + +{ +$Script +} +"@ +} + +Write-Host + +WindowsSandbox $SandboxTestWsbFile + diff --git a/Tools/cgmanifest.json b/Tools/cgmanifest.json index 7166f21919bef..bc692052f676c 100644 --- a/Tools/cgmanifest.json +++ b/Tools/cgmanifest.json @@ -1,13 +1,13 @@ -{ - "Registrations": [ - { - "Component": { - "Type": "git", - "git": { - "RepositoryUrl": "https://github.com/cloudbase/powershell-yaml", - "CommitHash": "03663c66e57ce2d0422077dc073bdb92e4d374b0" - } - }, - }, - ] +{ + "Registrations": [ + { + "Component": { + "Type": "git", + "git": { + "RepositoryUrl": "https://github.com/cloudbase/powershell-yaml", + "CommitHash": "03663c66e57ce2d0422077dc073bdb92e4d374b0" + } + }, + }, + ] } \ No newline at end of file