10 Commits

Author SHA1 Message Date
Ross Smith II
2745b0f42b sandbox.ps1: Add --no-update-scoop install option 2023-01-24 16:43:59 -08:00
Ross Smith II
212dc3d2ee Fix URL in changelog 2023-01-20 08:37:22 -08:00
Ross Smith II
39da19bcfc Add bin/sandbox.ps1 2023-01-19 22:16:00 -08:00
HUMORCE
68760de1e8 fix(shortcuts): Output correctly formatted path (#5333) 2023-01-09 11:01:40 +08:00
Hsiao-nan Cheung
257304bbc7 fix(decompress): Exclude '*.nsis' that may cause error (#5294) 2022-12-18 23:22:41 +08:00
Hsiao-nan Cheung
52f9ce3a81 fix(autoupdate): Fix file hash extraction (#5295) 2022-12-18 23:15:03 +08:00
HUMORCE
6369ba60ba refactor(scoop-download): Output more detailed manifest information (#5277) 2022-12-10 23:38:30 +08:00
Hsiao-nan Cheung
af5ffcddab test(bucket): Skip manifest validation if no manifest changes (#5270) 2022-11-30 13:32:35 +08:00
Richard Kuhnt
360daa706a feat(chore): Improve git.exe execution and add parallel bucket updates (#5122) 2022-11-23 13:58:51 +08:00
Hsiao-nan Cheung
f93028001f chore(release): Bump to version 0.3.1 (#5247) 2022-11-15 10:50:18 +08:00
13 changed files with 519 additions and 160 deletions

View File

@@ -1,3 +1,25 @@
## [Unreleased](https://github.com/ScoopInstaller/Scoop/compare/master...develop)
### Features
- **scoop-update:** Add support for parallel syncing buckets in PowerShell 7 and improve output ([#5122](https://github.com/ScoopInstaller/Scoop/issues/5122))
- **testing:** Add ability to test manifests inside a Windows sandbox ([#5349](https://github.com/(https://github.com/ScoopInstaller/Scoop/pulls/5349))
### Bug Fixes
- **decompress:** Exclude '*.nsis' that may cause error ([#5294](https://github.com/ScoopInstaller/Scoop/issues/5294))
- **autoupdate:** Fix file hash extraction ([#5295](https://github.com/ScoopInstaller/Scoop/issues/5295))
- **shortcuts:** Output correctly formatted path ([#5333](https://github.com/ScoopInstaller/Scoop/issues/5333))
### Code Refactoring
- **git:** Use Invoke-Git() with direct path to git.exe to prevent spawning shim subprocesses ([#5122](https://github.com/ScoopInstaller/Scoop/issues/5122))
- **scoop-download:** Output more detailed manifest information ([#5277](https://github.com/ScoopInstaller/Scoop/issues/5277))
### Tests
- **bucket:** Skip manifest validation if no manifest changes ([#5270](https://github.com/ScoopInstaller/Scoop/issues/5270))
## [v0.3.1](https://github.com/ScoopInstaller/Scoop/compare/v0.3.0...v0.3.1) - 2022-11-15
### Features

237
bin/sandbox.ps1 Normal file
View File

@@ -0,0 +1,237 @@
# SPDX-License-Identifier: MIT
# Portions Copyright (c) Microsoft Corporation. All rights reserved.
# Parse arguments
Param(
[Parameter(Position = 0, HelpMessage = 'The Manifest to install in the Sandbox.')]
[String] $Manifest,
[Parameter(Position = 1, HelpMessage = 'Options to pass to scoop.')]
[String] $Options,
[Parameter(Position = 2, 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 = (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.'
}
# 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
Remove-Item $tempFolder -Force -Recurse
New-Item $tempFolder -ItemType Directory | Out-Null
if (-Not [String]::IsNullOrWhiteSpace($Manifest)) {
Copy-Item -Path $Manifest -Recurse -Destination $tempFolder
}
if ($null -eq $env:SCOOP_HOME) { $env:SCOOP_HOME = "$env:USERPROFILE\scoop" }
$scoopCache = $env:SCOOP_HOME + '\cache'
Write-Host "Copying $scoopCache to $tempFolder\cache"
Copy-Item -Path $scoopCache -Recurse -Destination $tempFolder | Out-Null
$userprofileInSandbox = 'C:\Users\WDAGUtilityAccount'
$desktopInSandbox = $userprofileInSandbox + '\Desktop'
$sandboxTestInSandbox = $desktopInSandbox + '\' + $tempFolderName
$copiedCacheInSandbox = $sandboxTestInSandbox + "\cache"
$scoopCacheInSandbox = $userprofileInSandbox + "\scoop\cache"
# 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 Scoop, 7zip, git, innounp, dark and lessmsi
'@
`$ProgressPreference = 'SilentlyContinue'
irm get.scoop.sh -outfile 'install.ps1'
.\install.ps1 -RunAsAdmin
Update-EnvironmentVariables
xcopy /I /Q /Y $copiedCacheInSandbox\*.* $scoopCacheInSandbox\
scoop install --no-update-scoop main/7zip
scoop install --no-update-scoop main/git
scoop install --no-update-scoop main/innounp
scoop install --no-update-scoop main/dark
scoop install --no-update-scoop main/lessmsi
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 @'
--> Saving current ARP entries
'@
`$originalARP = Get-ARPTable
Write-Host @'
--> Running: scoop install $Options $Manifest
'@
scoop install $Options --no-update-scoop $manifestPathInSandbox
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 = @"
<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 Scoop
"@
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

View File

@@ -20,8 +20,8 @@ switch ($subCommand) {
}
({ $subCommand -in @('-v', '--version') }) {
Write-Host 'Current Scoop version:'
if ((Test-CommandAvailable git) -and (Test-Path "$PSScriptRoot\..\.git") -and (get_config SCOOP_BRANCH 'master') -ne 'master') {
git -C "$PSScriptRoot\.." --no-pager log --oneline HEAD -n 1
if (Test-GitAvailable -and (Test-Path "$PSScriptRoot\..\.git") -and (get_config SCOOP_BRANCH 'master') -ne 'master') {
Invoke-Git -Path "$PSScriptRoot\.." -ArgumentList @('log', 'HEAD', '-1', '--oneline')
} else {
$version = Select-String -Pattern '^## \[(v[\d.]+)\].*?([\d-]+)$' -Path "$PSScriptRoot\..\CHANGELOG.md"
Write-Host $version.Matches.Groups[1].Value -ForegroundColor Cyan -NoNewline
@@ -31,9 +31,9 @@ switch ($subCommand) {
Get-LocalBucket | ForEach-Object {
$bucketLoc = Find-BucketDirectory $_ -Root
if ((Test-Path "$bucketLoc\.git") -and (Test-CommandAvailable git)) {
if (Test-GitAvailable -and (Test-Path "$bucketLoc\.git")) {
Write-Host "'$_' bucket:"
git -C "$bucketLoc" --no-pager log --oneline HEAD -n 1
Invoke-Git -Path $bucketLoc -ArgumentList @('log', 'HEAD', '-1', '--oneline')
Write-Host ''
}
}

View File

@@ -8,9 +8,9 @@ function find_hash_in_rdf([String] $url, [String] $basename) {
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$data = $wc.DownloadData($url)
[xml]$xml = (Get-Encoding($wc)).GetString($data)
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"
} catch [System.Net.WebException] {
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
return $null
}
@@ -24,12 +24,12 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
$hashfile = $null
$templates = @{
'$md5' = '([a-fA-F0-9]{32})';
'$sha1' = '([a-fA-F0-9]{40})';
'$sha256' = '([a-fA-F0-9]{64})';
'$sha512' = '([a-fA-F0-9]{128})';
'$checksum' = '([a-fA-F0-9]{32,128})';
'$base64' = '([a-zA-Z0-9+\/=]{24,88})';
'$md5' = '([a-fA-F0-9]{32})'
'$sha1' = '([a-fA-F0-9]{40})'
'$sha256' = '([a-fA-F0-9]{64})'
'$sha512' = '([a-fA-F0-9]{128})'
'$checksum' = '([a-fA-F0-9]{32,128})'
'$base64' = '([a-zA-Z0-9+\/=]{24,88})'
}
try {
@@ -39,8 +39,8 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
$data = $wc.DownloadData($url)
$hashfile = (Get-Encoding($wc)).GetString($data)
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
return
}
@@ -50,15 +50,15 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
$regex = substitute $regex $templates $false
$regex = substitute $regex $substitutions $true
debug $regex
if ($hashfile -match $regex) {
$hash = $matches[1] -replace '\s',''
debug $regex
$hash = $matches[1] -replace '\s', ''
}
# convert base64 encoded hash values
if ($hash -match '^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$') {
$base64 = $matches[0]
if(!($hash -match '^[a-fA-F0-9]+$') -and $hash.length -notin @(32, 40, 64, 128)) {
if (!($hash -match '^[a-fA-F0-9]+$') -and $hash.Length -notin @(32, 40, 64, 128)) {
try {
$hash = ([System.Convert]::FromBase64String($base64) | ForEach-Object { $_.ToString('x2') }) -join ''
} catch {
@@ -69,13 +69,15 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
# find hash with filename in $hashfile
if ($hash.Length -eq 0) {
$filenameRegex = "([a-fA-F0-9]{32,128})[\x20\t]+.*`$basename(?:[\x20\t]+\d+)?"
$filenameRegex = "([a-fA-F0-9]{32,128})[\x20\t]+.*`$basename(?:\s|$)|`$basename[\x20\t]+.*?([a-fA-F0-9]{32,128})"
$filenameRegex = substitute $filenameRegex $substitutions $true
if ($hashfile -match $filenameRegex) {
debug $filenameRegex
$hash = $matches[1]
}
$metalinkRegex = "<hash[^>]+>([a-fA-F0-9]{64})"
$metalinkRegex = '<hash[^>]+>([a-fA-F0-9]{64})'
if ($hashfile -match $metalinkRegex) {
debug $metalinkRegex
$hash = $matches[1]
}
}
@@ -92,13 +94,14 @@ function find_hash_in_json([String] $url, [Hashtable] $substitutions, [String] $
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$data = $wc.DownloadData($url)
$json = (Get-Encoding($wc)).GetString($data)
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"
} catch [System.Net.WebException] {
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
return
}
debug $jsonpath
$hash = json_path $json $jsonpath $substitutions
if(!$hash) {
if (!$hash) {
$hash = json_path_legacy $json $jsonpath $substitutions
}
return format_hash $hash
@@ -114,8 +117,8 @@ function find_hash_in_xml([String] $url, [Hashtable] $substitutions, [String] $x
$data = $wc.DownloadData($url)
$xml = [xml]((Get-Encoding($wc)).GetString($data))
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
return
}
@@ -125,13 +128,15 @@ function find_hash_in_xml([String] $url, [Hashtable] $substitutions, [String] $x
}
# Find all `significant namespace declarations` from the XML file
$nsList = $xml.SelectNodes("//namespace::*[not(. = ../../namespace::*)]")
$nsList = $xml.SelectNodes('//namespace::*[not(. = ../../namespace::*)]')
# Then add them into the NamespaceManager
$nsmgr = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
$nsList | ForEach-Object {
$nsmgr.AddNamespace($_.LocalName, $_.Value)
}
debug $xpath
debug $nsmgr
# Getting hash from XML, using XPath
$hash = $xml.SelectSingleNode($xpath, $nsmgr).'#text'
return format_hash $hash
@@ -148,16 +153,16 @@ function find_hash_in_headers([String] $url) {
$req.Timeout = 2000
$req.Method = 'HEAD'
$res = $req.GetResponse()
if(([int]$res.StatusCode -ge 300) -and ([int]$res.StatusCode -lt 400)) {
if($res.Headers['Digest'] -match 'SHA-256=([^,]+)' -or $res.Headers['Digest'] -match 'SHA=([^,]+)' -or $res.Headers['Digest'] -match 'MD5=([^,]+)') {
if (([int]$res.StatusCode -ge 300) -and ([int]$res.StatusCode -lt 400)) {
if ($res.Headers['Digest'] -match 'SHA-256=([^,]+)' -or $res.Headers['Digest'] -match 'SHA=([^,]+)' -or $res.Headers['Digest'] -match 'MD5=([^,]+)') {
$hash = ([System.Convert]::FromBase64String($matches[1]) | ForEach-Object { $_.ToString('x2') }) -join ''
debug $hash
}
}
$res.Close()
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"
} catch [System.Net.WebException] {
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
return
}
@@ -182,10 +187,10 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
$hashfile_url = substitute $config.url $substitutions
debug $hashfile_url
if ($hashfile_url) {
write-host -f DarkYellow 'Searching hash for ' -NoNewline
write-host -f Green $basename -NoNewline
write-host -f DarkYellow ' in ' -NoNewline
write-host -f Green $hashfile_url
Write-Host 'Searching hash for ' -ForegroundColor DarkYellow -NoNewline
Write-Host $basename -ForegroundColor Green -NoNewline
Write-Host ' in ' -ForegroundColor DarkYellow -NoNewline
Write-Host $hashfile_url -ForegroundColor Green
}
if ($hashmode.Length -eq 0 -and $config.url.Length -ne 0) {
@@ -215,11 +220,11 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
$hashmode = 'xpath'
}
if (!$hashfile_url -and $url -match "^(?:.*fosshub.com\/).*(?:\/|\?dwl=)(?<filename>.*)$") {
if (!$hashfile_url -and $url -match '^(?:.*fosshub.com\/).*(?:\/|\?dwl=)(?<filename>.*)$') {
$hashmode = 'fosshub'
}
if (!$hashfile_url -and $url -match "(?:downloads\.)?sourceforge.net\/projects?\/(?<project>[^\/]+)\/(?:files\/)?(?<file>.*)") {
if (!$hashfile_url -and $url -match '(?:downloads\.)?sourceforge.net\/projects?\/(?<project>[^\/]+)\/(?:files\/)?(?<file>.*)') {
$hashmode = 'sourceforge'
}
@@ -243,7 +248,7 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
}
}
'fosshub' {
$hash = find_hash_in_textfile $url $substitutions ($Matches.filename+'.*?"sha256":"([a-fA-F0-9]{64})"')
$hash = find_hash_in_textfile $url $substitutions ($matches.filename + '.*?"sha256":"([a-fA-F0-9]{64})"')
}
'sourceforge' {
# change the URL because downloads.sourceforge.net doesn't have checksums
@@ -254,29 +259,29 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
if ($hash) {
# got one!
write-host -f DarkYellow 'Found: ' -NoNewline
write-host -f Green $hash -NoNewline
write-host -f DarkYellow ' using ' -NoNewline
write-host -f Green "$((Get-Culture).TextInfo.ToTitleCase($hashmode)) Mode"
Write-Host 'Found: ' -ForegroundColor DarkYellow -NoNewline
Write-Host $hash -ForegroundColor Green -NoNewline
Write-Host ' using ' -ForegroundColor DarkYellow -NoNewline
Write-Host "$((Get-Culture).TextInfo.ToTitleCase($hashmode)) Mode" -ForegroundColor Green
return $hash
} elseif ($hashfile_url) {
write-host -f DarkYellow "Could not find hash in $hashfile_url"
Write-Host -f DarkYellow "Could not find hash in $hashfile_url"
}
write-host -f DarkYellow 'Downloading ' -NoNewline
write-host -f Green $basename -NoNewline
write-host -f DarkYellow ' to compute hashes!'
Write-Host 'Downloading ' -ForegroundColor DarkYellow -NoNewline
Write-Host $basename -ForegroundColor Green -NoNewline
Write-Host ' to compute hashes!' -ForegroundColor DarkYellow
try {
Invoke-CachedDownload $app $version $url $null $null $true
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
return $null
}
$file = fullpath (cache_path $app $version $url)
$hash = (Get-FileHash -Path $file -Algorithm SHA256).Hash.ToLower()
write-host -f DarkYellow 'Computed hash: ' -NoNewline
write-host -f Green $hash
Write-Host 'Computed hash: ' -ForegroundColor DarkYellow -NoNewline
Write-Host $hash -ForegroundColor Green
return $hash
}
@@ -346,7 +351,7 @@ function Update-ManifestProperty {
$newValue = substitute $autoupdateProperty $Substitutions
if (($autoupdateProperty.GetType().Name -eq 'Object[]') -and ($autoupdateProperty.Length -eq 1)) {
# Make sure it's an array
$newValue = ,$newValue
$newValue = , $newValue
}
$Manifest.$currentProperty, $hasPropertyChanged = PropertyHelper -Property $Manifest.$currentProperty -Value $newValue
$hasManifestChanged = $hasManifestChanged -or $hasPropertyChanged
@@ -359,7 +364,7 @@ function Update-ManifestProperty {
$newValue = substitute $autoupdateProperty $Substitutions
if (($autoupdateProperty.GetType().Name -eq 'Object[]') -and ($autoupdateProperty.Length -eq 1)) {
# Make sure it's an array
$newValue = ,$newValue
$newValue = , $newValue
}
$Manifest.architecture.$arch.$currentProperty, $hasPropertyChanged = PropertyHelper -Property $Manifest.architecture.$arch.$currentProperty -Value $newValue
$hasManifestChanged = $hasManifestChanged -or $hasPropertyChanged
@@ -388,25 +393,25 @@ function Get-VersionSubstitution {
$firstPart = $Version.Split('-') | Select-Object -First 1
$lastPart = $Version.Split('-') | Select-Object -Last 1
$versionVariables = @{
'$version' = $Version;
'$dotVersion' = ($Version -replace '[._-]', '.');
'$underscoreVersion' = ($Version -replace '[._-]', '_');
'$dashVersion' = ($Version -replace '[._-]', '-');
'$cleanVersion' = ($Version -replace '[._-]', '');
'$majorVersion' = $firstPart.Split('.') | Select-Object -First 1;
'$minorVersion' = $firstPart.Split('.') | Select-Object -Skip 1 -First 1;
'$patchVersion' = $firstPart.Split('.') | Select-Object -Skip 2 -First 1;
'$buildVersion' = $firstPart.Split('.') | Select-Object -Skip 3 -First 1;
'$preReleaseVersion' = $lastPart;
'$version' = $Version
'$dotVersion' = ($Version -replace '[._-]', '.')
'$underscoreVersion' = ($Version -replace '[._-]', '_')
'$dashVersion' = ($Version -replace '[._-]', '-')
'$cleanVersion' = ($Version -replace '[._-]', '')
'$majorVersion' = $firstPart.Split('.') | Select-Object -First 1
'$minorVersion' = $firstPart.Split('.') | Select-Object -Skip 1 -First 1
'$patchVersion' = $firstPart.Split('.') | Select-Object -Skip 2 -First 1
'$buildVersion' = $firstPart.Split('.') | Select-Object -Skip 3 -First 1
'$preReleaseVersion' = $lastPart
}
if($Version -match "(?<head>\d+\.\d+(?:\.\d+)?)(?<tail>.*)") {
$versionVariables.Set_Item('$matchHead', $Matches['head'])
$versionVariables.Set_Item('$matchTail', $Matches['tail'])
if ($Version -match '(?<head>\d+\.\d+(?:\.\d+)?)(?<tail>.*)') {
$versionVariables.Add('$matchHead', $Matches['head'])
$versionVariables.Add('$matchTail', $Matches['tail'])
}
if($CustomMatches) {
if ($CustomMatches) {
$CustomMatches.GetEnumerator() | ForEach-Object {
if($_.Name -ne "0") {
$versionVariables.Set_Item('$match' + (Get-Culture).TextInfo.ToTitleCase($_.Name), $_.Value)
if ($_.Name -ne '0') {
$versionVariables.Add('$match' + (Get-Culture).TextInfo.ToTitleCase($_.Name), $_.Value)
}
}
}

View File

@@ -99,8 +99,8 @@ function list_buckets {
$bucket = [Ordered]@{ Name = $_ }
$path = Find-BucketDirectory $_ -Root
if ((Test-Path (Join-Path $path '.git')) -and (Get-Command git -ErrorAction SilentlyContinue)) {
$bucket.Source = git -C $path config remote.origin.url
$bucket.Updated = git -C $path log --format='%aD' -n 1 | Get-Date
$bucket.Source = Invoke-Git -Path $path -ArgumentList @('config', 'remote.origin.url')
$bucket.Updated = Invoke-Git -Path $path -ArgumentList @('log', "--format='%aD'", '-n', '1')
} else {
$bucket.Source = friendly_path $path
$bucket.Updated = (Get-Item "$path\bucket").LastWriteTime
@@ -113,7 +113,7 @@ function list_buckets {
}
function add_bucket($name, $repo) {
if (!(Test-CommandAvailable git)) {
if (!(Test-GitAvailable)) {
error "Git is required for buckets. Run 'scoop install git' and try again."
return 1
}
@@ -130,7 +130,7 @@ function add_bucket($name, $repo) {
}
foreach ($bucket in Get-LocalBucket) {
if (Test-Path -Path "$bucketsdir\$bucket\.git") {
$remote = git -C "$bucketsdir\$bucket" config --get remote.origin.url
$remote = Invoke-Git -Path "$bucketsdir\$bucket" -ArgumentList @('config', '--get', 'remote.origin.url')
if ((Convert-RepositoryUri -Uri $remote) -eq $uni_repo) {
warn "Bucket $bucket already exists for $repo"
return 2
@@ -139,14 +139,14 @@ function add_bucket($name, $repo) {
}
Write-Host 'Checking repo... ' -NoNewline
$out = git_cmd ls-remote $repo 2>&1
$out = Invoke-Git -ArgumentList @('ls-remote', $repo) 2>&1
if ($LASTEXITCODE -ne 0) {
error "'$repo' doesn't look like a valid git repository`n`nError given:`n$out"
return 1
}
ensure $bucketsdir | Out-Null
$dir = ensure $dir
git_cmd clone "$repo" "`"$dir`"" -q
Invoke-Git -ArgumentList @('clone', $repo, $dir, '-q')
Write-Host 'OK'
success "The $name bucket was added successfully."
return 0
@@ -169,7 +169,7 @@ function new_issue_msg($app, $bucket, $title, $body) {
$bucket_path = "$bucketsdir\$bucket"
if (Test-Path $bucket_path) {
$remote = git -C "$bucket_path" config --get remote.origin.url
$remote = Invoke-Git -Path $bucket_path -ArgumentList @('config', '--get', 'remote.origin.url')
# Support ssh and http syntax
# git@PROVIDER:USER/REPO.git
# https://PROVIDER/USER/REPO.git

View File

@@ -128,13 +128,78 @@ function setup_proxy() {
}
}
function git_cmd {
function Invoke-Git {
[CmdletBinding()]
[OutputType([String])]
param(
[Parameter(Mandatory = $false, Position = 0)]
[Alias('PSPath', 'Path')]
[ValidateNotNullOrEmpty()]
[String]
$WorkingDirectory,
[Parameter(Mandatory = $true, Position = 1)]
[Alias('Args')]
[String[]]
$ArgumentList
)
$proxy = get_config PROXY
$cmd = "git $($args | ForEach-Object { "$_ " })"
if ($proxy -and $proxy -ne 'none') {
$cmd = "SET HTTPS_PROXY=$proxy&&SET HTTP_PROXY=$proxy&&$cmd"
$git = Get-HelperPath -Helper Git
$arguments = $ArgumentList -join ' '
$cmd = "`"$git`" $arguments"
if ($WorkingDirectory) {
$cmd = "`"$git`" -C `"$WorkingDirectory`" $arguments"
}
$sb = [scriptblock]::Create("& $cmd")
if([String]::IsNullOrEmpty($proxy) -or $proxy -eq 'none') {
return Invoke-Command $sb
}
if($arguments -Match '\b(clone|checkout|pull|fetch|ls-remote)\b') {
$old_https = $env:HTTPS_PROXY
$old_http = $env:HTTP_PROXY
try {
# convert proxy setting for git
if ($proxy.StartsWith('currentuser@')) {
$proxy = $proxy.Replace('currentuser@', ':@')
}
$env:HTTPS_PROXY = $proxy
$env:HTTP_PROXY = $proxy
return Invoke-Command $sb
}
catch {
error $_
return
}
finally {
$env:HTTPS_PROXY = $old_https
$env:HTTP_PROXY = $old_http
}
}
return Invoke-Command $sb
}
function Invoke-GitLog {
[CmdletBinding()]
Param (
[Parameter(Mandatory, ValueFromPipeline)]
[String]$Path,
[Parameter(Mandatory, ValueFromPipeline)]
[String]$CommitHash,
[String]$Name = ''
)
Process {
if ($Name) {
if ($Name.Length -gt 12) {
$Name = "$($Name.Substring(0, 10)).."
}
$Name = "%Cgreen$($Name.PadRight(12, ' ').Substring(0, 12))%Creset "
}
Invoke-Git -Path $Path -ArgumentList @('--no-pager', 'log', '--color', '--no-decorate', "--grep='^(chore)'", '--invert-grep', '--abbrev=12', "--format='tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s $Name%C(cyan)%cr%Creset'", "$CommitHash..HEAD")
}
cmd.exe /d /c $cmd
}
# helper functions
@@ -293,12 +358,16 @@ Function Test-CommandAvailable {
Return [Boolean](Get-Command $Name -ErrorAction Ignore)
}
Function Test-GitAvailable {
Return [Boolean](Test-Path (Get-HelperPath -Helper Git) -ErrorAction Ignore)
}
function Get-HelperPath {
[CmdletBinding()]
[OutputType([String])]
param(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[ValidateSet('7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2', 'Zstd')]
[ValidateSet('Git', '7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2', 'Zstd')]
[String]
$Helper
)
@@ -307,6 +376,14 @@ function Get-HelperPath {
}
process {
switch ($Helper) {
'Git' {
$internalgit = "$(versiondir 'git' 'current')\mingw64\bin\git.exe"
if (Test-Path $internalgit) {
$HelperPath = $internalgit
} else {
$HelperPath = (Get-Command git -ErrorAction Ignore).Source
}
}
'7zip' {
$HelperPath = Get-AppFilePath '7zip' '7z.exe'
if ([String]::IsNullOrEmpty($HelperPath)) {

View File

@@ -29,7 +29,7 @@ function Expand-7zipArchive {
}
$LogPath = "$(Split-Path $Path)\7zip.log"
$DestinationPath = $DestinationPath.TrimEnd('\')
$ArgList = @('x', $Path, "-o$DestinationPath", '-y')
$ArgList = @('x', $Path, "-o$DestinationPath", '-xr!*.nsis', '-y')
$IsTar = ((strip_ext $Path) -match '\.tar$') -or ($Path -match '\.t[abgpx]z2?$')
if (!$IsTar -and $ExtractDir) {
$ArgList += "-ir!$ExtractDir\*"

View File

@@ -5,16 +5,16 @@ function create_startmenu_shortcuts($manifest, $dir, $global, $arch) {
$target = [System.IO.Path]::Combine($dir, $_.item(0))
$target = New-Object System.IO.FileInfo($target)
$name = $_.item(1)
$arguments = ""
$arguments = ''
$icon = $null
if($_.length -ge 3) {
if ($_.length -ge 3) {
$arguments = $_.item(2)
}
if($_.length -ge 4) {
if ($_.length -ge 4) {
$icon = [System.IO.Path]::Combine($dir, $_.item(3))
$icon = New-Object System.IO.FileInfo($icon)
}
$arguments = (substitute $arguments @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir})
$arguments = (substitute $arguments @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir })
startmenu_shortcut $target $name $arguments $icon $global
}
}
@@ -29,11 +29,11 @@ function shortcut_folder($global) {
}
function startmenu_shortcut([System.IO.FileInfo] $target, $shortcutName, $arguments, [System.IO.FileInfo]$icon, $global) {
if(!$target.Exists) {
if (!$target.Exists) {
Write-Host -f DarkRed "Creating shortcut for $shortcutName ($(fname $target)) failed: Couldn't find $target"
return
}
if($icon -and !$icon.Exists) {
if ($icon -and !$icon.Exists) {
Write-Host -f DarkRed "Creating shortcut for $shortcutName ($(fname $target)) failed: Couldn't find icon $icon"
return
}
@@ -51,11 +51,11 @@ function startmenu_shortcut([System.IO.FileInfo] $target, $shortcutName, $argume
if ($arguments) {
$wsShell.Arguments = $arguments
}
if($icon -and $icon.Exists) {
if ($icon -and $icon.Exists) {
$wsShell.IconLocation = $icon.FullName
}
$wsShell.Save()
write-host "Creating shortcut for $shortcutName ($(fname $target))"
Write-Host "Creating shortcut for $shortcutName ($(fname $target))"
}
# Removes the Startmenu shortcut if it exists
@@ -63,10 +63,10 @@ function rm_startmenu_shortcuts($manifest, $global, $arch) {
$shortcuts = @(arch_specific 'shortcuts' $manifest $arch)
$shortcuts | Where-Object { $_ -ne $null } | ForEach-Object {
$name = $_.item(1)
$shortcut = "$(shortcut_folder $global)\$name.lnk"
write-host "Removing shortcut $(friendly_path $shortcut)"
if(Test-Path -Path $shortcut) {
Remove-Item $shortcut
$shortcut = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$(shortcut_folder $global)\$name.lnk")
Write-Host "Removing shortcut $(friendly_path $shortcut)"
if (Test-Path -Path $shortcut) {
Remove-Item $shortcut
}
}
}

View File

@@ -57,7 +57,7 @@ foreach ($curr_app in $apps) {
$app, $bucket, $version = parse_app $curr_app
$app, $manifest, $bucket, $url = Get-Manifest "$bucket/$app"
info "Starting download for $app..."
info "Downloading '$app'$(if ($version) { " ($version)" }) [$architecture]$(if ($bucket) { " from $bucket bucket" })"
# Generate manifest if there is different version in manifest
if (($null -ne $version) -and ($manifest.version -ne $version)) {

View File

@@ -84,7 +84,7 @@ if ($manifest.depends) {
if (Test-Path $manifest_file) {
if (Get-Command git -ErrorAction Ignore) {
$gitinfo = (git -C (Split-Path $manifest_file) log -1 -s --format='%aD#%an' $manifest_file 2> $null) -Split '#'
$gitinfo = (Invoke-Git -Path (Split-Path $manifest_file) -ArgumentList @('log', '-1', '-s', "--format='%aD#%an'", $manifest_file) 2> $null) -Split '#'
}
if ($gitinfo) {
$item.'Updated at' = $gitinfo[0] | Get-Date

View File

@@ -21,10 +21,10 @@ if (!(Get-FormatData ScoopStatus)) {
function Test-UpdateStatus($repopath) {
if (Test-Path "$repopath\.git") {
git_cmd -C "`"$repopath`"" fetch -q origin
Invoke-Git -Path $repopath -ArgumentList @('fetch', '-q', 'origin')
$script:network_failure = 128 -eq $LASTEXITCODE
$branch = git -C $repopath branch --show-current
$commits = git -C $repopath log "HEAD..origin/$branch" --oneline
$branch = Invoke-Git -Path $repopath -ArgumentList @('branch', '--show-current')
$commits = Invoke-Git -Path $repopath -ArgumentList @('log', "HEAD..origin/$branch", '--oneline')
if ($commits) { return $true }
else { return $false }
} else {

View File

@@ -56,14 +56,18 @@ if(($PSVersionTable.PSVersion.Major) -lt 5) {
}
$show_update_log = get_config SHOW_UPDATE_LOG $true
function update_scoop($show_update_log) {
function Sync-Scoop {
[CmdletBinding()]
Param (
[Switch]$Log
)
# Test if Scoop Core is hold
if(Test-ScoopCoreOnHold) {
return
}
# check for git
if (!(Test-CommandAvailable git)) { abort "Scoop uses Git to update itself. Run 'scoop install git' and try again." }
if (!(Test-GitAvailable)) { abort "Scoop uses Git to update itself. Run 'scoop install git' and try again." }
Write-Host "Updating Scoop..."
$currentdir = fullpath $(versiondir 'scoop' 'current')
@@ -72,7 +76,7 @@ function update_scoop($show_update_log) {
$olddir = "$currentdir\..\old"
# get git scoop
git_cmd clone -q $configRepo --branch $configBranch --single-branch "`"$newdir`""
Invoke-Git -ArgumentList @('clone', '-q', $configRepo, '--branch', $configBranch, '--single-branch', $newdir)
# check if scoop was successful downloaded
if (!(Test-Path "$newdir\bin\scoop.ps1")) {
@@ -93,18 +97,18 @@ function update_scoop($show_update_log) {
Remove-Item "$currentdir\..\old" -Recurse -Force -ErrorAction SilentlyContinue
}
$previousCommit = git -C "$currentdir" rev-parse HEAD
$currentRepo = git -C "$currentdir" config remote.origin.url
$currentBranch = git -C "$currentdir" branch
$previousCommit = Invoke-Git -Path $currentdir -ArgumentList @('rev-parse', 'HEAD')
$currentRepo = Invoke-Git -Path $currentdir -ArgumentList @('config', 'remote.origin.url')
$currentBranch = Invoke-Git -Path $currentdir -ArgumentList @('branch')
$isRepoChanged = !($currentRepo -match $configRepo)
$isBranchChanged = !($currentBranch -match "\*\s+$configBranch")
# Stash uncommitted changes
if (git -C "$currentdir" diff HEAD --name-only) {
if (Invoke-Git -Path $currentdir -ArgumentList @('diff', 'HEAD', '--name-only')) {
if (get_config AUTOSTASH_ON_CONFLICT) {
warn "Uncommitted changes detected. Stashing..."
git -C "$currentdir" stash push -m "WIP at $([System.DateTime]::Now.ToString('o'))" -u -q
Invoke-Git -Path $currentdir -ArgumentList @('stash', 'push', '-m', "WIP at $([System.DateTime]::Now.ToString('o'))", '-u', '-q')
} else {
warn "Uncommitted changes detected. Update aborted."
return
@@ -113,26 +117,26 @@ function update_scoop($show_update_log) {
# Change remote url if the repo is changed
if ($isRepoChanged) {
git -C "$currentdir" config remote.origin.url "$configRepo"
Invoke-Git -Path $currentdir -ArgumentList @('config', 'remote.origin.url', $configRepo)
}
# Fetch and reset local repo if the repo or the branch is changed
if ($isRepoChanged -or $isBranchChanged) {
# Reset git fetch refs, so that it can fetch all branches (GH-3368)
git -C "$currentdir" config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
Invoke-Git -Path $currentdir -ArgumentList @('config', 'remote.origin.fetch', '+refs/heads/*:refs/remotes/origin/*')
# fetch remote branch
git_cmd -C "`"$currentdir`"" fetch --force origin "refs/heads/`"$configBranch`":refs/remotes/origin/$configBranch" -q
Invoke-Git -Path $currentdir -ArgumentList @('fetch', '--force', 'origin', "refs/heads/$configBranch`:refs/remotes/origin/$configBranch", '-q')
# checkout and track the branch
git_cmd -C "`"$currentdir`"" checkout -B $configBranch -t origin/$configBranch -q
Invoke-Git -Path $currentdir -ArgumentList @('checkout', '-B', $configBranch, '-t', "origin/$configBranch", '-q')
# reset branch HEAD
git -C "$currentdir" reset --hard origin/$configBranch -q
Invoke-Git -Path $currentdir -ArgumentList @('reset', '--hard', "origin/$configBranch", '-q')
} else {
git_cmd -C "`"$currentdir`"" pull -q
Invoke-Git -Path $currentdir -ArgumentList @('pull', '-q')
}
$res = $lastexitcode
if ($show_update_log) {
git -C "$currentdir" --no-pager log --no-decorate --grep='^(chore)' --invert-grep --format='tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset' "$previousCommit..HEAD"
if ($Log) {
Invoke-GitLog -Path $currentdir -CommitHash $previousCommit
}
if ($res -ne 0) {
@@ -140,47 +144,63 @@ function update_scoop($show_update_log) {
}
}
# This should have been deprecated after 2019-05-12
# if ((Get-LocalBucket) -notcontains 'main') {
# info "The main bucket of Scoop has been separated to 'https://github.com/ScoopInstaller/Main'"
# info "Adding main bucket..."
# add_bucket 'main'
# }
shim "$currentdir\bin\scoop.ps1" $false
}
function update_bucket($show_update_log) {
# check for git
if (!(Test-CommandAvailable git)) { abort "Scoop uses Git to update main bucket and others. Run 'scoop install git' and try again." }
function Sync-Bucket {
Param (
[Switch]$Log
)
Write-Host "Updating Buckets..."
foreach ($bucket in Get-LocalBucket) {
Write-Host "Updating '$bucket' bucket..."
$bucketLoc = Find-BucketDirectory $bucket -Root
if (!(Test-Path (Join-Path $bucketLoc '.git'))) {
if ($bucket -eq 'main') {
# Make sure main bucket, which was downloaded as zip, will be properly "converted" into git
Write-Host " Converting 'main' bucket to git repo..."
$status = rm_bucket 'main'
if ($status -ne 0) {
abort "Failed to remove local 'main' bucket."
}
$status = add_bucket 'main' (known_bucket_repo 'main')
if ($status -ne 0) {
abort "Failed to add remote 'main' bucket."
}
} else {
Write-Host "'$bucket' is not a git repository. Skipped."
}
continue
if (!(Test-Path (Join-Path (Find-BucketDirectory 'main' -Root) '.git'))) {
info "Converting 'main' bucket to git repo..."
$status = rm_bucket 'main'
if ($status -ne 0) {
abort "Failed to remove local 'main' bucket."
}
$status = add_bucket 'main' (known_bucket_repo 'main')
if ($status -ne 0) {
abort "Failed to add remote 'main' bucket."
}
}
$previousCommit = git -C "$bucketLoc" rev-parse HEAD
git_cmd -C "`"$bucketLoc`"" pull -q
if ($show_update_log) {
git -C "$bucketLoc" --no-pager log --no-decorate --grep='^(chore)' --invert-grep --format='tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset' "$previousCommit..HEAD"
$buckets = Get-LocalBucket | ForEach-Object {
$path = Find-BucketDirectory $_ -Root
return @{
name = $_
valid = Test-Path (Join-Path $path '.git')
path = $path
}
}
$buckets | Where-Object { !$_.valid } | ForEach-Object { Write-Host "'$($_.name)' is not a git repository. Skipped." }
if ($PSVersionTable.PSVersion.Major -ge 7) {
# Parallel parameter is available since PowerShell 7
$buckets | Where-Object { $_.valid } | ForEach-Object -ThrottleLimit 5 -Parallel {
. "$using:PSScriptRoot\..\lib\core.ps1"
$bucketLoc = $_.path
$name = $_.name
$previousCommit = Invoke-Git -Path $bucketLoc -ArgumentList @('rev-parse', 'HEAD')
Invoke-Git -Path $bucketLoc -ArgumentList @('pull', '-q')
if ($using:Log) {
Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit
}
}
} else {
$buckets | Where-Object { $_.valid } | ForEach-Object {
$bucketLoc = $_.path
$name = $_.name
$previousCommit = Invoke-Git -Path $bucketLoc -ArgumentList @('rev-parse', 'HEAD')
Invoke-Git -Path $bucketLoc -ArgumentList @('pull', '-q')
if ($Log) {
Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit
}
}
}
}
@@ -321,8 +341,8 @@ if (-not ($apps -or $all)) {
error 'scoop update: --no-cache is invalid when <app> is not specified.'
exit 1
}
update_scoop $show_update_log
update_bucket $show_update_log
Sync-Scoop -Log:$show_update_log
Sync-Bucket -Log:$show_update_log
set_config LAST_UPDATE ([System.DateTime]::Now.ToString('o')) | Out-Null
success 'Scoop was updated successfully!'
} else {
@@ -336,8 +356,8 @@ if (-not ($apps -or $all)) {
$apps_param = $apps
if ($updateScoop) {
update_scoop $show_update_log
update_bucket $show_update_log
Sync-Scoop -Log:$show_update_log
Sync-Bucket -Log:$show_update_log
set_config LAST_UPDATE ([System.DateTime]::Now.ToString('o')) | Out-Null
success 'Scoop was updated successfully!'
}

View File

@@ -16,11 +16,9 @@ Describe 'Manifest validates against the schema' {
}
if ($env:CI -eq $true) {
Set-BuildEnvironment -Force
$changedManifests = @(Get-GitChangedFile -Path $bucketDir -Include '*.json' -Commit $env:BHCommitHash)
}
$manifestFiles = (Get-ChildItem $bucketDir -Filter '*.json' -Recurse).FullName
if ($changedManifests) {
$manifestFiles = $manifestFiles | Where-Object { $_ -in $changedManifests }
$manifestFiles = @(Get-GitChangedFile -Path $bucketDir -Include '*.json' -Commit $env:BHCommitHash)
} else {
$manifestFiles = (Get-ChildItem $bucketDir -Filter '*.json' -Recurse).FullName
}
}
BeforeAll {