-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create SandboxTest script #827
Changes from 23 commits
f7ec04d
da8548d
e8ca1c4
ee50cb8
eea0fe4
8efaeac
8f90038
ef769c5
22c6233
42e8651
d72fcac
c3a12a0
c825b3e
be375f9
f799acb
7074857
b20c3f2
5c9d1d9
799bdde
8cb5a7a
73fc9a0
e875fa9
f72b42a
3693035
3060469
c3cccd8
b1319ef
22d04a4
7228aa7
553a646
dd7408a
d438e94
a85396a
b607189
cb06ad1
498374a
76b44aa
25df74c
ac75e09
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
# Parse arguments | ||
|
||
Param( | ||
[Parameter(Position=0, HelpMessage = "The Manifest to install in the Sandbox.")] | ||
[String] $Manifest, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be declared mandatory? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The purpose of the script is to be flexible. Some times you just want to open sandbox, have winget installed, the current folder mounted and mess with it freely, this allows: > .\Tools\SandboxTest.ps1 # works
> .\Tools\SandboxTest.ps1 -Script { winget install git; Update-Environment; git --version } # also works, no manifest There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, I didn't realize that was an intended purpose, makes sense |
||
[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 | ||
) | ||
|
||
$ErrorActionPreference = "Stop" | ||
|
||
$mapFolder = [System.IO.Path]::GetFullPath($MapFolder) | ||
felipecrs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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 [String]::IsNullOrWhiteSpace($Manifest)) { | ||
Write-Host '--> Validating Manifest' | ||
|
||
if (-Not (Test-Path -Path $Manifest -PathType Leaf)) { | ||
throw 'The Manifest file does not exist.' | ||
} | ||
|
||
winget.exe validate $Manifest | ||
if (-Not $?) { | ||
throw 'Manifest validation failed.' | ||
} | ||
} | ||
|
||
# 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/en-us/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 | ||
Write-Host '--> Closing Windows Sandbox' | ||
$sandbox | Stop-Process | ||
Start-Sleep -Seconds 5 | ||
} | ||
Remove-Variable sandbox | ||
|
||
# Set dependencies | ||
felipecrs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
$desktopAppInstaller = @{ | ||
fileName = 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.appxbundle' | ||
url = 'https://github.com/microsoft/winget-cli/releases/download/v0.1.42101-preview/Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.appxbundle' | ||
hash = '2ea378a034b836efc6e49751dde253e6c43105d2d584527347ee127b734aa6c6' | ||
} | ||
|
||
$vcLibs = @{ | ||
fileName = 'Microsoft.VCLibs.140.00_14.0.27810.0_x64__8wekyb3d8bbwe.Appx' | ||
url = 'https://github.com/felipecassiors/winget-pkgs/raw/da8548d90369eb8f69a4738dc1474caaffb58e12/Tools/SandboxTest_Temp/Microsoft.VCLibs.140.00_14.0.27810.0_x64__8wekyb3d8bbwe.Appx' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I need to see if we can find a more official release path. I will reach out to VS. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure! I used my GitHub as a workaround, to have a placeholder for the right URL. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems to be the only missing thing for this PR, take a look on my last comment for some alternatives.
felipecrs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
hash = 'fe660c46a3ff8462d9574902e735687e92eeb835f75ec462a41ef76b54ef13ed' | ||
} | ||
|
||
$vcLibsUwp = @{ | ||
felipecrs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
fileName = 'Microsoft.VCLibs.140.00.UWPDesktop_14.0.27810.0_x64__8wekyb3d8bbwe.Appx' | ||
url = 'https://raw.githubusercontent.com/felipecassiors/winget-pkgs/da8548d90369eb8f69a4738dc1474caaffb58e12/Tools/SandboxTest_Temp/Microsoft.VCLibs.140.00.UWPDesktop_14.0.27810.0_x64__8wekyb3d8bbwe.Appx' | ||
felipecrs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
hash = '66de9fde9d2ebf18893a890987f35d2d145c18cc5ee0e8ecaa09477dcc13b16b' | ||
} | ||
|
||
$dependencies = @($desktopAppInstaller, $vcLibs, $vcLibsUwp) | ||
|
||
# Initialize Temp Folder | ||
|
||
$tempFolderName = 'SandboxTest' | ||
$tempFolder = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath $tempFolderName | ||
|
||
New-Item $tempFolder -ItemType Directory -ErrorAction SilentlyContinue | Out-Null | ||
|
||
Get-ChildItem $tempFolder -Recurse -Exclude $dependencies.fileName | Remove-Item -Force | ||
|
||
# Download dependencies | ||
|
||
Write-Host | ||
Write-Host '--> Downloading dependencies' | ||
|
||
$desktopInSandbox = 'C:\Users\WDAGUtilityAccount\Desktop' | ||
|
||
$WebClient = New-Object System.Net.WebClient | ||
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)) { | ||
# This downloads the file | ||
Write-Host "Downloading $($dependency.url) ..." | ||
try { | ||
$WebClient.DownloadFile($dependency.url, $dependency.file) | ||
} | ||
catch { | ||
throw "Error downloading $($dependency.url) ." | ||
} | ||
if (-not ($dependency.hash -eq $(get-filehash $dependency.file).Hash)) { | ||
throw 'Hashes do not match, try gain.' | ||
} | ||
} | ||
} | ||
|
||
# Create Bootstrap script | ||
|
||
$manifestFileName = $Manifest | ||
|
||
# See: https://stackoverflow.com/a/14382047/12156188 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rename this for a little more clarity. Maybe Update-EnvironmentVariables? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, this implementation seems a little less fraught to me? https://stackoverflow.com/a/22670892/892770 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Well, it's really a long string, especially if you consider that it will be typed manually. But that's how PowerShell is, I agree with this change.
Definitely. I don't know why I picked the first one, but I'll try and if it works I'll switch. |
||
$bootstrapPs1Content = @' | ||
function Update-Environment { | ||
$locations = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment', | ||
'HKCU:\Environment' | ||
|
||
$locations | ForEach-Object { | ||
$k = Get-Item $_ | ||
$k.GetValueNames() | ForEach-Object { | ||
$name = $_ | ||
$value = $k.GetValue($_) | ||
|
||
if ($userLocation -and $name -ieq 'PATH') { | ||
$Env:Path += ";$value" | ||
} else { | ||
Set-Item -Path Env:$name -Value $value | ||
} | ||
} | ||
|
||
$userLocation = $true | ||
} | ||
} | ||
'@ | ||
|
||
$bootstrapPs1Content += @" | ||
Write-Host @' | ||
--> Installing WinGet | ||
|
||
|
||
|
||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why so much whitespace? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because of this (the progress bar stays on the screen even after the completion): One option would be to hide the progress bar though, but I just wanted to make the script as transparent as possible. I even though about turning on the PowerShell debug feature to output each command being runned, but it turned out to be too noisy. What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gotcha, keep it then no prob |
||
|
||
'@ | ||
Add-AppxPackage -Path '$($desktopAppInstaller.pathInSandbox)' -DependencyPath '$($vcLibs.pathInSandbox)','$($vcLibsUwp.pathInSandbox)' | ||
|
||
Write-Host @' | ||
|
||
Tip: you can type 'Update-Environment' to update your environment variables, such as after installing a new software. | ||
|
||
'@ | ||
"@ | ||
|
||
if (-Not [String]::IsNullOrWhiteSpace($Manifest)) { | ||
$bootstrapPs1Content += @" | ||
|
||
Write-Host @' | ||
|
||
--> Installing the Manifest $manifestFileName | ||
|
||
'@ | ||
winget install -m '$manifestFileName' | ||
"@ | ||
|
||
$bootstrapPs1Content += @' | ||
|
||
Write-Host @" | ||
|
||
--> Refreshing environment variables | ||
"@ | ||
Update-Environment | ||
'@ | ||
} | ||
|
||
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 = @" | ||
<Configuration> | ||
<MappedFolders> | ||
<MappedFolder> | ||
<HostFolder>$tempFolder</HostFolder> | ||
<ReadOnly>true</ReadOnly> | ||
</MappedFolder> | ||
<MappedFolder> | ||
<HostFolder>$mapFolder</HostFolder> | ||
</MappedFolder> | ||
</MappedFolders> | ||
<LogonCommand> | ||
<Command>PowerShell Start-Process PowerShell -WindowStyle Maximized -WorkingDirectory '$mapFolderInSandbox' -ArgumentList '-ExecutionPolicy Bypass -NoExit -NoLogo -File $bootstrapPs1InSandbox'</Command> | ||
</LogonCommand> | ||
</Configuration> | ||
"@ | ||
|
||
$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 | ||
"@ | ||
|
||
if (-Not [String]::IsNullOrWhiteSpace($Manifest)) { | ||
Write-Host @" | ||
- Installing the Manifest $manifestFileName | ||
- Refreshing environment variables | ||
"@ | ||
} | ||
|
||
if (-Not [String]::IsNullOrWhiteSpace($Script)) { | ||
Write-Host @" | ||
- Running the following script: | ||
|
||
{ | ||
$Script | ||
} | ||
"@ | ||
} | ||
|
||
Write-Host | ||
|
||
WindowsSandbox $SandboxTestWsbFile |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kept passing the manifest version file itself and validation kept failing, took me a while to realize this parameter expects the manifest version directory. I'd clarify that here and in the param help
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@palenshus Well, my goal was to support the two kinds of manifests to keep backwards compatibility (it was files previously). Since you said you're not from the winget-cli team, it may be better to ask them about it.
@denelon, any suggestion?
If yet we decide to keep accepting both files and folders, the help can be surely enhanced to clarify that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you have a singleton manifest you can point to the file, but the directory option works for both cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, I was only trying it on multi-file schemas. I'd just make it take a directory in both cases for consistency. Winget will validate the directory and ensure that if the manifestType is singleton, then there's only the one file in there