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 all 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 @@ -35,10 +35,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\<first lower case letter of publisher>\<publisher>\<package>\<version>.yaml`

Expand Down
284 changes: 284 additions & 0 deletions Tools/SandboxTest.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
# 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,
[switch] $SkipManifestValidation
)

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

Choose a reason for hiding this comment

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

I believe this should have -PathType Container now too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Depends on the other conversation also.

throw 'The Manifest does not exist.'
}

winget.exe validate $Manifest
if (-Not $?) {
throw 'Manifest validation failed.'
}

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/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 '--> 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 = '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.appxbundle$' }).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.appxbundle'
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 = '6602159c341bafea747d0edf15669ac72df8817299fbfaa90469909e06794256'
}

$dependencies = @($desktopAppInstaller, $vcLibsUwp)

# Clean temp directory

Get-ChildItem $tempFolder -Recurse -Exclude $dependencies.fileName | Remove-Item -Force -Recurse

if (-Not [String]::IsNullOrWhiteSpace($Manifest)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Aside from making this mandatory in the params, I'd check this once up top and fail out if it's whitespace, then you don't have to keep checking it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also depends on the other conversation, I guess.

Copy link
Contributor

Choose a reason for hiding this comment

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

Leaving manifest optional is fine

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 {
throw "Error downloading $($dependency.url)."
}
if (-not ($dependency.hash -eq $(get-filehash $dependency.file).Hash)) {
throw 'Hashes do not match, try gain.'
}
}
}

Write-Host

# 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)" }
}
}


'@

$bootstrapPs1Content += @"
Write-Host @'
--> Installing WinGet

'@
Add-AppxPackage -Path '$($desktopAppInstaller.pathInSandbox)' -DependencyPath '$($vcLibsUwp.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 @'

--> Installing the Manifest $manifestFileName

'@
winget install -m '$manifestPathInSandbox'

Write-Host @'

--> Refreshing environment variables
'@
Update-EnvironmentVariables


"@
}

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