Skip to content
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

Merged
merged 39 commits into from
May 3, 2021
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f7ec04d
Create SandboxTest script
felipecrs May 22, 2020
da8548d
Add missing appx dependencies
felipecrs May 22, 2020
e8ca1c4
Clear temp folder
felipecrs May 22, 2020
ee50cb8
Merge branch 'master' of https://github.com/microsoft/winget-pkgs int…
felipecrs May 23, 2020
eea0fe4
Validate manifest before pushing to sandbox
felipecrs May 23, 2020
8efaeac
Merge branch 'master' of https://github.com/microsoft/winget-pkgs int…
felipecrs May 24, 2020
8f90038
Fix invalid character in Sandbox.ps1
chausner May 23, 2020
ef769c5
Download dependencies on the fly
felipecrs May 28, 2020
22c6233
remove vscode launch file
felipecrs May 28, 2020
42e8651
Add newline in gitignore
felipecrs May 28, 2020
d72fcac
Fix when there is no temp folder
felipecrs May 28, 2020
c3a12a0
Add Test in Sandbox to README
felipecrs May 28, 2020
c825b3e
Add some comments and enhance some messages
felipecrs May 28, 2020
be375f9
Properly accept mandatory parameter
felipecrs May 28, 2020
f799acb
Check if Windows Sandbox exist or not
felipecrs May 28, 2020
7074857
Fix Sandbox install snippet and typo
felipecrs May 28, 2020
b20c3f2
Merge branch 'master' of https://github.com/microsoft/winget-pkgs int…
felipecrs Jun 22, 2020
5c9d1d9
Merge branch 'master' of https://github.com/microsoft/winget-pkgs int…
felipecrs Jul 10, 2020
799bdde
Fix wrong conflict resolution in README
felipecrs Jul 10, 2020
8cb5a7a
Upgrade WinGet to v0.1.41821-preview
felipecrs Jul 10, 2020
73fc9a0
Merge branch 'master' of https://github.com/microsoft/winget-pkgs int…
felipecrs Aug 23, 2020
e875fa9
Rewrite the whole script
felipecrs Aug 23, 2020
f72b42a
Add tip for Update-Environment
felipecrs Aug 23, 2020
3693035
Fix relative paths on map folder
felipecrs Aug 28, 2020
3060469
Copy the manifest instead of installing from the source
felipecrs Aug 28, 2020
c3cccd8
Fix call without manifest param and enhance logs
felipecrs Sep 11, 2020
b1319ef
Merge branch 'master' of https://github.com/microsoft/winget-pkgs int…
felipecrs Sep 11, 2020
22d04a4
Use VCLibs UWPDesktop from official source and update winget
felipecrs Sep 28, 2020
7228aa7
Merge branch 'master' of https://github.com/microsoft/winget-pkgs int…
felipecrs Sep 28, 2020
553a646
Add some missing new lines
felipecrs Oct 8, 2020
dd7408a
Merge branch 'master' of https://github.com/microsoft/winget-pkgs int…
felipecrs Oct 8, 2020
d438e94
Fetch latest WinGet release automatically
felipecrs Dec 8, 2020
a85396a
Merge branch 'master' of https://github.com/microsoft/winget-pkgs int…
felipecrs Dec 8, 2020
b607189
Merge branch 'master' of https://github.com/microsoft/winget-pkgs int…
felipecrs Apr 27, 2021
cb06ad1
Add -SkipManifestValidation switch and fix checksum
felipecrs Apr 27, 2021
498374a
Fix TLS issues, handle PowerShell 7, remove unneeded VCLibs dep and f…
felipecrs Apr 27, 2021
76b44aa
Restore file/folder check for manifest
felipecrs Apr 27, 2021
25df74c
Change checksum output filename
felipecrs Apr 27, 2021
ac75e09
Update Update-EnvironmentVariables function
felipecrs May 1, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,20 @@ Although the Windows Package Manager YAML Generator can create YAML files with m

## Test your manifest
Now that you have authored your manifest, you should make sure it works as expected.

### Locally
1) Verify the syntax. You can do that by typing the following command: `winget validate <manifest>`
2) Test the install. You can do that by installing the manifest: `winget install -m <manifest>`
For more details, see [packages](https://docs.microsoft.com/windows/package-manager/package).

### In Windows Sandbox
You can use the [`Tools\SandboxTest.ps1`](Tools/SandboxTest.ps1) script for testing a manifest installation in [Windows Sandbox](https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-sandbox/windows-sandbox-overview). The manifest will be also validated.

Just provide the path to manifest as parameter:
```powershell
.\Tools\SandboxTest.ps1 <path-to-manifest>
Copy link
Contributor

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

Copy link
Contributor Author

@felipecrs felipecrs May 1, 2021

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.

Copy link
Contributor

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.

Copy link
Contributor

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

```

## Submit your PR
With the manifest verified, you will need to submit a PR. Your manifest should be located in the folder path matching `manifests\<publisher>\<package>\<version>.yaml`

Expand Down
264 changes: 264 additions & 0 deletions Tools/SandboxTest.ps1
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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be declared mandatory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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

Copy link
Contributor

Choose a reason for hiding this comment

The 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)

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

$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'
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

@felipecrs felipecrs May 30, 2020

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

@felipecrs felipecrs Jun 4, 2020

Choose a reason for hiding this comment

The 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.

#827 (comment)

hash = 'fe660c46a3ff8462d9574902e735687e92eeb835f75ec462a41ef76b54ef13ed'
}

$vcLibsUwp = @{
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'
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rename this for a little more clarity. Maybe Update-EnvironmentVariables?

Copy link
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update-EnvironmentVariables

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.

Also, this implementation seems a little less fraught to me? https://stackoverflow.com/a/22670892/892770

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





Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why so much whitespace?

Copy link
Contributor Author

@felipecrs felipecrs May 1, 2021

Choose a reason for hiding this comment

The 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):

U6ho8iV7OL

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?

Copy link
Contributor

Choose a reason for hiding this comment

The 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