mirror of
https://github.com/ScoopInstaller/Scoop.git
synced 2025-10-30 06:07:56 +00:00
Compare commits
7 Commits
9a6cdc7328
...
887f79d2d7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
887f79d2d7 | ||
|
|
5bae77eebb | ||
|
|
210b5e45a0 | ||
|
|
3aa64baeb1 | ||
|
|
0470ba2d04 | ||
|
|
398aaa2330 | ||
|
|
4585f50a54 |
@@ -264,9 +264,11 @@ function Select-ScoopDBItem {
|
||||
[void]$dbAdapter.Fill($result)
|
||||
}
|
||||
end {
|
||||
$resultCopy = $result.Copy()
|
||||
$dbAdapter.Dispose()
|
||||
$db.Dispose()
|
||||
return $result
|
||||
# Use Write-Output to ensure PowerShell properly returns the DataTable
|
||||
Write-Output $resultCopy -NoEnumerate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,13 +295,13 @@ function Select-ScoopDBItem {
|
||||
function Get-ScoopDBItem {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory, Position = 0, ValueFromPipeline)]
|
||||
[Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
|
||||
[string]
|
||||
$Name,
|
||||
[Parameter(Mandatory, Position = 1)]
|
||||
[Parameter(Mandatory, Position = 1, ValueFromPipelineByPropertyName)]
|
||||
[string]
|
||||
$Bucket,
|
||||
[Parameter(Position = 2)]
|
||||
[Parameter(Position = 2, ValueFromPipelineByPropertyName)]
|
||||
[string]
|
||||
$Version
|
||||
)
|
||||
@@ -307,28 +309,46 @@ function Get-ScoopDBItem {
|
||||
begin {
|
||||
$db = Open-ScoopDB
|
||||
$dbAdapter = New-Object -TypeName System.Data.SQLite.SQLiteDataAdapter
|
||||
$dbCommand = $db.CreateCommand()
|
||||
$dbCommand.CommandType = [System.Data.CommandType]::Text
|
||||
$dbAdapter.SelectCommand = $dbCommand
|
||||
}
|
||||
|
||||
process {
|
||||
$result = New-Object System.Data.DataTable
|
||||
|
||||
# Build query based on whether version is specified
|
||||
$dbQuery = 'SELECT * FROM app WHERE name = @Name AND bucket = @Bucket'
|
||||
if ($Version) {
|
||||
$dbQuery += ' AND version = @Version'
|
||||
} else {
|
||||
$dbQuery += ' ORDER BY version DESC LIMIT 1'
|
||||
}
|
||||
$dbCommand = $db.CreateCommand()
|
||||
|
||||
$dbCommand.CommandText = $dbQuery
|
||||
$dbCommand.CommandType = [System.Data.CommandType]::Text
|
||||
$dbAdapter.SelectCommand = $dbCommand
|
||||
}
|
||||
process {
|
||||
$dbCommand.Parameters.Clear()
|
||||
|
||||
# Add parameters for this specific query
|
||||
$dbCommand.Parameters.AddWithValue('@Name', $Name) | Out-Null
|
||||
$dbCommand.Parameters.AddWithValue('@Bucket', $Bucket) | Out-Null
|
||||
$dbCommand.Parameters.AddWithValue('@Version', $Version) | Out-Null
|
||||
if ($Version) {
|
||||
$dbCommand.Parameters.AddWithValue('@Version', $Version) | Out-Null
|
||||
}
|
||||
|
||||
[void]$dbAdapter.Fill($result)
|
||||
|
||||
# Create a copy of the DataTable to avoid disposal issues
|
||||
$resultCopy = $result.Copy()
|
||||
|
||||
# Use Write-Output to ensure PowerShell properly returns the DataTable
|
||||
Write-Output $resultCopy -NoEnumerate
|
||||
}
|
||||
|
||||
end {
|
||||
# Clean up all resources
|
||||
$dbAdapter.Dispose()
|
||||
$dbCommand.Dispose()
|
||||
$db.Dispose()
|
||||
return $result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
216
lib/manifest.ps1
216
lib/manifest.ps1
@@ -180,35 +180,20 @@ function Get-HistoricalManifestFromDB($app, $bucket, $requestedVersion) {
|
||||
ensure (usermanifestsdir) | Out-Null
|
||||
$tempManifestPath = "$(usermanifestsdir)\$app.json"
|
||||
$row.manifest | Out-UTF8File -FilePath $tempManifestPath
|
||||
return @{ path = $tempManifestPath; version = $requestedVersion; source = "sqlite_exact_match" }
|
||||
|
||||
# Parse the manifest to get its actual version
|
||||
$manifest = $row.manifest | ConvertFrom-Json
|
||||
$manifestVersion = if ($manifest.version) { $manifest.version } else { $requestedVersion }
|
||||
return @{ path = $tempManifestPath; version = $manifestVersion; source = "sqlite_exact_match" }
|
||||
}
|
||||
|
||||
# If no exact match, try to find best match from all versions of this app
|
||||
$allVersionsResult = Get-ScoopDBItem -Name $app -Bucket $bucket
|
||||
if ($allVersionsResult.Rows.Count -gt 0) {
|
||||
$availableVersions = $allVersionsResult.Rows | ForEach-Object { $_.version }
|
||||
$bestMatch = Find-BestVersionMatch -RequestedVersion $requestedVersion -AvailableVersions $availableVersions
|
||||
|
||||
if ($bestMatch) {
|
||||
$matchedRow = $allVersionsResult.Rows | Where-Object { $_.version -eq $bestMatch } | Select-Object -First 1
|
||||
ensure (usermanifestsdir) | Out-Null
|
||||
$tempManifestPath = "$(usermanifestsdir)\$app.json"
|
||||
$matchedRow.manifest | Out-UTF8File -FilePath $tempManifestPath
|
||||
return @{ path = $tempManifestPath; version = $bestMatch; source = "sqlite_best_match" }
|
||||
}
|
||||
}
|
||||
# No exact match found, return null (no compatibility matching)
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-HistoricalManifest($app, $bucket, $requestedVersion) {
|
||||
# First try to get historical manifest from SQLite
|
||||
$manifestFromDB = Get-HistoricalManifestFromDB $app $bucket $requestedVersion
|
||||
if ($manifestFromDB) {
|
||||
return $manifestFromDB
|
||||
}
|
||||
|
||||
# Fall back to git history if not found in SQLite
|
||||
function Get-HistoricalManifestFromGitHistory($app, $bucket, $requestedVersion) {
|
||||
# Only proceed if git history is enabled
|
||||
if (!(get_config USE_GIT_HISTORY $true)) {
|
||||
return $null
|
||||
}
|
||||
@@ -351,38 +336,30 @@ function Get-HistoricalManifest($app, $bucket, $requestedVersion) {
|
||||
return $null
|
||||
}
|
||||
|
||||
info "Found $($foundVersions.Count) distinct historical versions for '$app'. Finding best match for '$requestedVersion'..."
|
||||
# No exact match found - display all available versions to help user choose
|
||||
$allAvailableVersions = ($foundVersions | Sort-Object {
|
||||
try { [version]($_.version -replace '[^\d\.].*$', '') } catch { $_.version }
|
||||
} -Descending).version
|
||||
|
||||
$matchedVersionData = $null
|
||||
$bestMatchVersionString = Find-BestVersionMatch -RequestedVersion $requestedVersion -AvailableVersions ($foundVersions.version)
|
||||
Write-Host ""
|
||||
Write-Host "No exact match found for version '$requestedVersion' for app '$app'."
|
||||
Write-Host "Available versions in git history (newest to oldest):"
|
||||
Write-Host ""
|
||||
|
||||
if ($bestMatchVersionString) {
|
||||
$matchedVersionData = $foundVersions | Where-Object { $_.version -eq $bestMatchVersionString } | Select-Object -First 1
|
||||
# Group versions for better display
|
||||
$displayCount = [Math]::Min(50, $allAvailableVersions.Count) # Show up to 50 versions
|
||||
for ($i = 0; $i -lt $displayCount; $i++) {
|
||||
Write-Host " $($allAvailableVersions[$i])"
|
||||
}
|
||||
|
||||
if ($matchedVersionData) {
|
||||
info "Best match for '$requestedVersion' is '$($matchedVersionData.version)' (commit $($matchedVersionData.hash))."
|
||||
ensure (usermanifestsdir) | Out-Null
|
||||
$tempManifestPath = "$(usermanifestsdir)\$app.json"
|
||||
if ($PSVersionTable.PSVersion.Major -ge 6) {
|
||||
$utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($false)
|
||||
Set-Content -Path $tempManifestPath -Value $matchedVersionData.manifest -Encoding $utf8NoBomEncoding -NoNewline:$false
|
||||
} else {
|
||||
# PowerShell 5 compatibility
|
||||
$matchedVersionData.manifest | Out-UTF8File -FilePath $tempManifestPath
|
||||
}
|
||||
return @{
|
||||
path = $tempManifestPath
|
||||
version = $matchedVersionData.version
|
||||
source = "git_best_match:$($matchedVersionData.hash)"
|
||||
}
|
||||
} else {
|
||||
$availableVersionsForLog = ($foundVersions | Sort-Object {
|
||||
try { [version]($_.version -replace '[^\d\.].*$', '') } catch { $_.version }
|
||||
} -Descending | Select-Object -First 10).version
|
||||
info "Could not find a suitable match for '$requestedVersion' for app '$app'. Available (latest 10): $($availableVersionsForLog -join ', ')"
|
||||
if ($allAvailableVersions.Count -gt $displayCount) {
|
||||
Write-Host " ... and $($allAvailableVersions.Count - $displayCount) more versions"
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "To install a specific version, use: scoop install $app@<version>"
|
||||
Write-Host ""
|
||||
|
||||
return $null
|
||||
|
||||
} catch {
|
||||
@@ -391,71 +368,6 @@ function Get-HistoricalManifest($app, $bucket, $requestedVersion) {
|
||||
}
|
||||
}
|
||||
|
||||
function Find-BestVersionMatch($requestedVersion, $availableVersions) {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Find the best matching version from available versions
|
||||
.PARAMETER requestedVersion
|
||||
The version requested by user (e.g., "3.7", "3.7.4")
|
||||
.PARAMETER availableVersions
|
||||
Array of available version strings
|
||||
.RETURNS
|
||||
Best matching version string, or $null if no match
|
||||
#>
|
||||
|
||||
if (!$availableVersions -or $availableVersions.Count -eq 0) {
|
||||
return $null
|
||||
}
|
||||
|
||||
debug "Searching for version '$requestedVersion' among available versions: $($availableVersions -join ', ')"
|
||||
|
||||
# First try exact match
|
||||
if ($availableVersions -contains $requestedVersion) {
|
||||
debug "Found exact match for version '$requestedVersion'"
|
||||
return $requestedVersion
|
||||
}
|
||||
|
||||
# If no exact match, try to find compatible versions
|
||||
# Split requested version into parts
|
||||
$requestedParts = $requestedVersion -split '\.'
|
||||
|
||||
# Filter versions that start with the requested version pattern
|
||||
$compatibleVersions = $availableVersions | Where-Object {
|
||||
$versionParts = $_ -split '\.'
|
||||
$isCompatible = $true
|
||||
|
||||
for ($i = 0; $i -lt $requestedParts.Count; $i++) {
|
||||
if ($i -ge $versionParts.Count -or $versionParts[$i] -ne $requestedParts[$i]) {
|
||||
$isCompatible = $false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return $isCompatible
|
||||
}
|
||||
|
||||
debug "Found $($compatibleVersions.Count) compatible versions: $($compatibleVersions -join ', ')"
|
||||
|
||||
if ($compatibleVersions.Count -eq 0) {
|
||||
debug "No compatible versions found for '$requestedVersion'"
|
||||
return $null
|
||||
}
|
||||
|
||||
# Sort compatible versions and return the highest one
|
||||
$sortedVersions = $compatibleVersions | Sort-Object {
|
||||
# Convert to version object for proper sorting
|
||||
try {
|
||||
[version]($_ -replace '[^\d\.].*$', '') # Remove non-numeric suffixes for sorting
|
||||
} catch {
|
||||
$_ # Fallback to string sorting
|
||||
}
|
||||
} -Descending
|
||||
|
||||
$selectedVersion = $sortedVersions[0]
|
||||
debug "Selected best match: '$selectedVersion' for requested version '$requestedVersion'"
|
||||
|
||||
return $selectedVersion
|
||||
}
|
||||
|
||||
function generate_user_manifest($app, $bucket, $version) {
|
||||
# 'autoupdate.ps1' 'buckets.ps1' 'manifest.ps1'
|
||||
@@ -466,22 +378,48 @@ function generate_user_manifest($app, $bucket, $version) {
|
||||
|
||||
warn "Given version ($version) does not match manifest ($($manifest.version))"
|
||||
|
||||
# Try to find the version using SQLite cache first, then git history
|
||||
$historicalResult = $null
|
||||
|
||||
# Try SQLite cache first if enabled
|
||||
if (get_config USE_SQLITE_CACHE) {
|
||||
info "Searching for version '$version' in cache..."
|
||||
} else {
|
||||
info "Searching for version '$version' in git history..."
|
||||
$historicalResult = Get-HistoricalManifestFromDB $app $bucket $version
|
||||
if ($historicalResult) {
|
||||
info "Found version '$($historicalResult.version)' for '$app' in cache."
|
||||
return $historicalResult.path
|
||||
}
|
||||
}
|
||||
|
||||
$historicalResult = Get-HistoricalManifest $app $bucket $version
|
||||
|
||||
if ($historicalResult) {
|
||||
if ($historicalResult.source -match '^sqlite') {
|
||||
info "Found version '$($historicalResult.version)' for '$app' in cache."
|
||||
} else {
|
||||
info "Found version '$($historicalResult.version)' for '$app' in git history (source: $($historicalResult.source))."
|
||||
# Try git history if cache didn't find it
|
||||
if (!$historicalResult) {
|
||||
info "Searching for version '$version' in git history..."
|
||||
$historicalResult = Get-HistoricalManifestFromGitHistory $app $bucket $version
|
||||
if ($historicalResult) {
|
||||
return $historicalResult.path
|
||||
}
|
||||
}
|
||||
|
||||
# If no historical version found, provide helpful guidance
|
||||
if (!$historicalResult) {
|
||||
# Try to provide additional context about what versions are available
|
||||
$currentVersion = $manifest.version
|
||||
if ($currentVersion) {
|
||||
info "Current version available: $currentVersion"
|
||||
info "To install the current version, use: scoop install $app"
|
||||
}
|
||||
|
||||
# Check if we have autoupdate capability for fallback
|
||||
if ($manifest.autoupdate) {
|
||||
info "This app supports autoupdate - attempting to generate manifest for version $version"
|
||||
} else {
|
||||
warn "'$app' does not have autoupdate capability."
|
||||
Write-Host "Available options:"
|
||||
Write-Host " 1. Install current version: scoop install $app"
|
||||
Write-Host " 2. Check if the requested version exists in other buckets"
|
||||
Write-Host " 3. Contact the bucket maintainer to add historical version support"
|
||||
Write-Host ""
|
||||
abort "Could not find manifest for '$app@$version' and no autoupdate available"
|
||||
}
|
||||
return $historicalResult.path
|
||||
}
|
||||
|
||||
# Fallback to autoupdate generation
|
||||
@@ -490,6 +428,7 @@ function generate_user_manifest($app, $bucket, $version) {
|
||||
ensure (usermanifestsdir) | Out-Null
|
||||
$manifest_path = "$(usermanifestsdir)\$app.json"
|
||||
|
||||
# Check SQLite cache for exact cached manifest (this is different from historical search)
|
||||
if (get_config USE_SQLITE_CACHE) {
|
||||
$cached_manifest = (Get-ScoopDBItem -Name $app -Bucket $bucket -Version $version).manifest
|
||||
if ($cached_manifest) {
|
||||
@@ -507,16 +446,33 @@ function generate_user_manifest($app, $bucket, $version) {
|
||||
return $manifest_path
|
||||
} catch {
|
||||
Write-Host -ForegroundColor DarkRed "Could not install $app@$version"
|
||||
Write-Host -ForegroundColor Yellow "Autoupdate failed for version $version"
|
||||
|
||||
# Provide helpful guidance when autoupdate fails
|
||||
Write-Host "Possible reasons:"
|
||||
Write-Host " - Version $version may not exist or be available for download"
|
||||
Write-Host " - Download URLs may have changed or be inaccessible"
|
||||
Write-Host " - The version format may be incompatible with autoupdate patterns"
|
||||
Write-Host ""
|
||||
Write-Host "Suggestions:"
|
||||
Write-Host " 1. Install current version: scoop install $app"
|
||||
Write-Host " 2. Try a different version that was shown in the available list"
|
||||
Write-Host " 3. Check the app's official releases or download page"
|
||||
Write-Host ""
|
||||
|
||||
# If autoupdate fails and we haven't tried git history yet, try it as final fallback
|
||||
if (!$historicalResult) {
|
||||
if (!$historicalResult -and !(get_config USE_SQLITE_CACHE)) {
|
||||
warn 'Autoupdate failed. Trying git history as final fallback...'
|
||||
$historicalResult = Get-HistoricalManifest $app $bucket $version
|
||||
if ($historicalResult) {
|
||||
warn "Using historical manifest as fallback for '$app' version '$($historicalResult.version)'"
|
||||
return $historicalResult.path
|
||||
$fallbackResult = Get-HistoricalManifestFromGitHistory $app $bucket $version
|
||||
if ($fallbackResult) {
|
||||
warn "Using historical manifest as fallback for '$app' version '$($fallbackResult.version)'"
|
||||
return $fallbackResult.path
|
||||
}
|
||||
}
|
||||
|
||||
# Final failure - provide comprehensive guidance
|
||||
Write-Host -ForegroundColor Red "All attempts to find or generate manifest for '$app@$version' failed."
|
||||
abort "Installation of '$app@$version' is not possible"
|
||||
}
|
||||
|
||||
return $null
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
# scoop install git
|
||||
#
|
||||
# To install a different version of the app
|
||||
# (will search git history first, then auto-generate manifest as fallback):
|
||||
# (will search sqlite cache if enabled, then git history (if use_git_history is $true), then auto-generate manifest as fallback):
|
||||
# scoop install gh@2.7.0
|
||||
# scoop install python@3.12 # Install latest 3.12.x version if available
|
||||
#
|
||||
# To install an app from a manifest at a URL:
|
||||
# scoop install https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/runat.json
|
||||
|
||||
@@ -4,55 +4,14 @@ BeforeAll {
|
||||
. "$PSScriptRoot\..\lib\system.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\download.ps1"
|
||||
. "$PSScriptRoot\..\lib\decompress.ps1"
|
||||
. "$PSScriptRoot\..\lib\json.ps1"
|
||||
. "$PSScriptRoot\..\lib\shortcuts.ps1"
|
||||
. "$PSScriptRoot\..\lib\database.ps1"
|
||||
. "$PSScriptRoot\..\lib\autoupdate.ps1"
|
||||
. "$PSScriptRoot\..\lib\psmodules.ps1"
|
||||
}
|
||||
Describe 'install_app' -Tag 'Scoop' {
|
||||
BeforeAll {
|
||||
# Mock all required functions and external dependencies
|
||||
Mock Get-Manifest { return @('testapp', @{ version = '1.0.0' }, 'testbucket', $null) }
|
||||
Mock Get-SupportedArchitecture { return '64bit' }
|
||||
Mock ensure { return "C:\test\dir" }
|
||||
Mock versiondir { return "C:\test\dir" }
|
||||
Mock persistdir { return "C:\test\persist" }
|
||||
Mock usermanifestsdir { return "C:\test\manifests" }
|
||||
Mock ensure_install_dir_not_in_path {}
|
||||
Mock link_current { return "C:\test\dir" }
|
||||
Mock create_shims {}
|
||||
Mock create_startmenu_shortcuts {}
|
||||
Mock Invoke-AutoUpdate { return "C:\temp\manifests\testapp.json" }
|
||||
Mock Get-ScoopDBItem { return [PSCustomObject]@{ Rows = @() } }
|
||||
Mock install_psmodule {}
|
||||
Mock env_add_path {}
|
||||
Mock env_set {}
|
||||
Mock persist_data {}
|
||||
Mock persist_permission {}
|
||||
Mock save_installed_manifest {}
|
||||
Mock save_install_info {}
|
||||
Mock show_notes {}
|
||||
Mock Write-Output {}
|
||||
Mock success {}
|
||||
}
|
||||
It 'should handle try-catch block successfully' {
|
||||
Mock Invoke-ScoopDownload { "file.txt" }
|
||||
Mock Invoke-Extraction {}
|
||||
Mock Invoke-HookScript {}
|
||||
Mock Invoke-Installer {}
|
||||
{ install_app 'testapp' '64bit' $false $false } | Should -Not -Throw
|
||||
Should -Invoke -CommandName Invoke-ScoopDownload -Times 1
|
||||
Should -Invoke -CommandName Invoke-Extraction -Times 1
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'appname_from_url' -Tag 'Scoop' {
|
||||
It 'should extract the correct name' {
|
||||
appname_from_url 'https://example.org/directory/foobar.json' | Should -Be 'foobar'
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'is_in_dir' -Tag 'Scoop', 'Windows' {
|
||||
It 'should work correctly' {
|
||||
is_in_dir 'C:\test' 'C:\foo' | Should -BeFalse
|
||||
@@ -60,6 +19,7 @@ Describe 'is_in_dir' -Tag 'Scoop', 'Windows' {
|
||||
is_in_dir "$PSScriptRoot\..\" "$PSScriptRoot" | Should -BeFalse
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'env add and remove path' -Tag 'Scoop', 'Windows' {
|
||||
BeforeAll {
|
||||
# test data
|
||||
@@ -69,15 +29,18 @@ Describe 'env add and remove path' -Tag 'Scoop', 'Windows' {
|
||||
$testdir = Join-Path $PSScriptRoot 'path-test-directory'
|
||||
$global = $false
|
||||
}
|
||||
|
||||
It 'should concat the correct path' {
|
||||
Mock Add-Path {}
|
||||
Mock Remove-Path {}
|
||||
|
||||
# adding
|
||||
env_add_path $manifest $testdir $global
|
||||
Should -Invoke -CommandName Add-Path -Times 1 -ParameterFilter { $Path -like "$testdir\foo" }
|
||||
Should -Invoke -CommandName Add-Path -Times 1 -ParameterFilter { $Path -like "$testdir\bar" }
|
||||
Should -Invoke -CommandName Add-Path -Times 1 -ParameterFilter { $Path -like $testdir }
|
||||
Should -Invoke -CommandName Add-Path -Times 0 -ParameterFilter { $Path -like $PSScriptRoot }
|
||||
|
||||
env_rm_path $manifest $testdir $global
|
||||
Should -Invoke -CommandName Remove-Path -Times 1 -ParameterFilter { $Path -like "$testdir\foo" }
|
||||
Should -Invoke -CommandName Remove-Path -Times 1 -ParameterFilter { $Path -like "$testdir\bar" }
|
||||
@@ -85,6 +48,7 @@ Describe 'env add and remove path' -Tag 'Scoop', 'Windows' {
|
||||
Should -Invoke -CommandName Remove-Path -Times 0 -ParameterFilter { $Path -like $PSScriptRoot }
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'shim_def' -Tag 'Scoop' {
|
||||
It 'should use strings correctly' {
|
||||
$target, $name, $shimArgs = shim_def 'command.exe'
|
||||
@@ -92,182 +56,47 @@ Describe 'shim_def' -Tag 'Scoop' {
|
||||
$name | Should -Be 'command'
|
||||
$shimArgs | Should -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'should expand the array correctly' {
|
||||
$target, $name, $shimArgs = shim_def @('foo.exe', 'bar')
|
||||
$target | Should -Be 'foo.exe'
|
||||
$name | Should -Be 'bar'
|
||||
$shimArgs | Should -BeNullOrEmpty
|
||||
|
||||
$target, $name, $shimArgs = shim_def @('foo.exe', 'bar', '--test')
|
||||
$target | Should -Be 'foo.exe'
|
||||
$name | Should -Be 'bar'
|
||||
$shimArgs | Should -Be '--test'
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'persist_def' -Tag 'Scoop' {
|
||||
It 'parses string correctly' {
|
||||
$source, $target = persist_def 'test'
|
||||
$source | Should -Be 'test'
|
||||
$target | Should -Be 'test'
|
||||
}
|
||||
|
||||
It 'should handle sub-folder' {
|
||||
$source, $target = persist_def 'foo/bar'
|
||||
$source | Should -Be 'foo/bar'
|
||||
$target | Should -Be 'foo/bar'
|
||||
}
|
||||
|
||||
It 'should handle arrays' {
|
||||
# both specified
|
||||
$source, $target = persist_def @('foo', 'bar')
|
||||
$source | Should -Be 'foo'
|
||||
$target | Should -Be 'bar'
|
||||
|
||||
# only first specified
|
||||
$source, $target = persist_def @('foo')
|
||||
$source | Should -Be 'foo'
|
||||
$target | Should -Be 'foo'
|
||||
|
||||
# null value specified
|
||||
$source, $target = persist_def @('foo', $null)
|
||||
$source | Should -Be 'foo'
|
||||
$target | Should -Be 'foo'
|
||||
}
|
||||
}
|
||||
Describe 'Get-RelativePathCompat' -Tag 'Scoop' {
|
||||
It 'should return relative path using PowerShell Core method when available' {
|
||||
if ($PSVersionTable.PSVersion.Major -ge 6) {
|
||||
$result = Get-RelativePathCompat "C:\test\from" "C:\test\from\to\file.txt"
|
||||
$result | Should -Match "to[\\\/]file\.txt"
|
||||
}
|
||||
}
|
||||
It 'should fallback to URI method when PowerShell Core method fails or on Windows PowerShell' {
|
||||
$result = Get-RelativePathCompat "C:\test\from\" "C:\test\from\to\file.txt"
|
||||
$result | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
It 'should handle different schemes gracefully' {
|
||||
# This test may behave differently on different systems
|
||||
# The function should return something meaningful when schemes differ
|
||||
$result = Get-RelativePathCompat "file:///C:/test/from/" "http://example.com/file.txt"
|
||||
$result | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
Describe 'Get-HistoricalManifestFromDB' -Tag 'Scoop' {
|
||||
BeforeAll {
|
||||
Mock get_config {
|
||||
param($key)
|
||||
if ($key -eq 'USE_SQLITE_CACHE') { return $true }
|
||||
return $false
|
||||
}
|
||||
Mock Get-Command { return $true }
|
||||
Mock Get-ScoopDBItem { return [PSCustomObject]@{ Rows = @() } }
|
||||
Mock ensure { return "C:\temp\manifests" }
|
||||
Mock usermanifestsdir { return "C:\temp\manifests" }
|
||||
Mock Out-UTF8File {}
|
||||
}
|
||||
It 'should return null when SQLite cache is disabled' {
|
||||
Mock get_config { return $false }
|
||||
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1.0.0'
|
||||
$result | Should -BeNullOrEmpty
|
||||
}
|
||||
It 'should return null when Get-ScoopDBItem command not available' {
|
||||
Mock Get-Command { return $null }
|
||||
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1.0.0'
|
||||
$result | Should -BeNullOrEmpty
|
||||
}
|
||||
It 'should return manifest for exact version match' {
|
||||
Mock Get-ScoopDBItem {
|
||||
$mockRow = [PSCustomObject]@{ manifest = '{"version": "1.0.0", "description": "Test app"}'; version = '1.0.0' }
|
||||
return [PSCustomObject]@{ Rows = @($mockRow) }
|
||||
}
|
||||
Mock ConvertFrom-Json { return [PSCustomObject]@{ version = '1.0.0'; description = 'Test app' } }
|
||||
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1.0.0'
|
||||
$result.version | Should -Be '1.0.0'
|
||||
$result.source | Should -Be 'sqlite_exact_match'
|
||||
$result.path | Should -Match 'testapp\.json'
|
||||
}
|
||||
It 'should return null when exact version not available' {
|
||||
Mock Get-ScoopDBItem {
|
||||
param($Name, $Bucket, $Version)
|
||||
# Always return empty results since we only support exact matches now
|
||||
return [PSCustomObject]@{ Rows = @() }
|
||||
}
|
||||
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1.0'
|
||||
$result | Should -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
Describe 'Get-HistoricalManifest' -Tag 'Scoop' {
|
||||
BeforeAll {
|
||||
Mock Get-HistoricalManifestFromDB { return $null }
|
||||
Mock get_config {
|
||||
param($key, $default)
|
||||
if ($key -eq 'USE_GIT_HISTORY') { return $default }
|
||||
return $false
|
||||
}
|
||||
}
|
||||
It 'should return database result when available' {
|
||||
Mock Get-HistoricalManifestFromDB {
|
||||
return @{
|
||||
path = "C:\temp\testapp.json"
|
||||
version = "1.0.0"
|
||||
source = "sqlite_exact_match"
|
||||
}
|
||||
}
|
||||
$result = Get-HistoricalManifest 'testapp' 'main' '1.0.0'
|
||||
$result.source | Should -Be 'sqlite_exact_match'
|
||||
$result.version | Should -Be '1.0.0'
|
||||
}
|
||||
It 'should return null when no bucket provided' {
|
||||
$result = Get-HistoricalManifest 'testapp' $null '1.0.0'
|
||||
$result | Should -BeNullOrEmpty
|
||||
}
|
||||
It 'should return null when git history disabled' {
|
||||
Mock get_config { return $false }
|
||||
$result = Get-HistoricalManifest 'testapp' 'main' '1.0.0'
|
||||
$result | Should -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
Describe 'generate_user_manifest Enhanced Functionality' -Tag 'Scoop' {
|
||||
BeforeAll {
|
||||
Mock Get-Manifest { return @('testapp', @{ version = '2.0.0' }, 'main', $null) }
|
||||
Mock manifest_path { return "C:\test\manifest.json" }
|
||||
Mock warn {}
|
||||
Mock info {}
|
||||
Mock abort { throw "aborted" }
|
||||
Mock Get-HistoricalManifest { return $null }
|
||||
Mock ensure { return "C:\temp" }
|
||||
Mock usermanifestsdir { return "C:\temp\manifests" }
|
||||
Mock Invoke-AutoUpdate { return "C:\temp\manifests\testapp.json" }
|
||||
}
|
||||
It 'should return existing manifest path when versions match' {
|
||||
Mock Get-Manifest { return @('testapp', @{ version = '1.5.0' }, 'main', $null) }
|
||||
$result = generate_user_manifest 'testapp' 'main' '1.5.0'
|
||||
$result | Should -Be "C:\test\manifest.json"
|
||||
}
|
||||
It 'should attempt historical manifest search when versions do not match' {
|
||||
Mock Get-HistoricalManifest {
|
||||
return @{
|
||||
path = "C:\temp\manifests\testapp.json"
|
||||
version = "1.0.0"
|
||||
source = "git_exact_match:abc123"
|
||||
}
|
||||
}
|
||||
Mock get_config {
|
||||
param($key)
|
||||
if ($key -eq 'USE_SQLITE_CACHE') { return $true }
|
||||
return $false
|
||||
}
|
||||
Mock warn {}
|
||||
$result = generate_user_manifest 'testapp' 'main' '1.0.0'
|
||||
$result | Should -Be "C:\temp\manifests\testapp.json"
|
||||
Should -Invoke -CommandName info -Times 1 -ParameterFilter {
|
||||
$args[0] -like "*Searching for version*in cache*"
|
||||
}
|
||||
}
|
||||
It 'should fallback to autoupdate when historical manifest not found' {
|
||||
Mock Get-HistoricalManifest { return $null }
|
||||
Mock Invoke-AutoUpdate { return "C:\temp\manifests\testapp.json" }
|
||||
Mock Get-Manifest { return @('testapp', @{ version = '2.0.0'; autoupdate = @{} }, 'main', $null) }
|
||||
Mock get_config { return $false } # USE_SQLITE_CACHE disabled
|
||||
Mock Get-ScoopDBItem { return [PSCustomObject]@{ manifest = $null; Rows = @() } }
|
||||
$result = generate_user_manifest 'testapp' 'main' '1.0.0'
|
||||
Should -Invoke -CommandName warn -Times 1 -ParameterFilter {
|
||||
$args[0] -like "*No historical version found*"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,17 @@
|
||||
BeforeAll {
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\json.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\database.ps1"
|
||||
}
|
||||
|
||||
Describe 'JSON parse and beautify' -Tag 'Scoop' {
|
||||
Context 'Parse JSON' {
|
||||
It 'success with valid json' {
|
||||
{ parse_json "$PSScriptRoot\fixtures\manifest\wget.json" } | Should -Not -Throw
|
||||
}
|
||||
It 'fails with invalid json' {
|
||||
Mock warn {}
|
||||
Mock ConvertFrom-Json { throw "Invalid JSON" }
|
||||
$result = parse_json "$PSScriptRoot\fixtures\manifest\broken_wget.json"
|
||||
$result | Should -BeNullOrEmpty
|
||||
Should -Invoke -CommandName warn -Times 1 -ParameterFilter {
|
||||
$args[0] -like "*Error parsing JSON*"
|
||||
It 'fails with invalid json' {
|
||||
{ parse_json "$PSScriptRoot\fixtures\manifest\broken_wget.json" } | Should -Throw
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Describe 'Get-RelativePathCompat' -Tag 'Scoop' {
|
||||
It 'should return relative path for valid URIs' {
|
||||
Get-RelativePathCompat "C:\\path\from" "C:\\path\from\to" | Should -Be "to"
|
||||
}
|
||||
It 'should return absolute path for different schemes' {
|
||||
$result = Get-RelativePathCompat "C:\\path\from" "D:\\path\to"
|
||||
$result | Should -Be "D:\path\to"
|
||||
}
|
||||
}
|
||||
Describe 'Get-HistoricalManifestFromDB' -Tag 'Scoop' {
|
||||
BeforeAll {
|
||||
Mock ensure { return "C:\\test\\dir" }
|
||||
Mock usermanifestsdir { return "C:\\test\\manifests" }
|
||||
Mock Out-UTF8File {}
|
||||
Mock get_config { return $true }
|
||||
}
|
||||
It 'should return manifest from database if available' {
|
||||
Mock Get-ScoopDBItem { $mockDbResult = [PSCustomObject] @{ Rows = @( [PSCustomObject] @{ manifest = '{"version": "1.2.3"}' }) } ; return $mockDbResult }
|
||||
$result = Get-HistoricalManifestFromDB 'testapp' 'testbucket' '1.2.3'
|
||||
$result.path | Should -Match 'testapp.json'
|
||||
$result.source | Should -Be "sqlite_exact_match"
|
||||
}
|
||||
It 'should return null if database disabled' {
|
||||
Mock get_config { return $false }
|
||||
$result = Get-HistoricalManifestFromDB 'testapp' 'testbucket' '1.2.3'
|
||||
$result | Should -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
Describe 'Get-HistoricalManifest' -Tag 'Scoop' {
|
||||
BeforeAll {
|
||||
Mock ensure { return "C:\\test\\dir" }
|
||||
Mock usermanifestsdir { return "C:\\test\\manifests" }
|
||||
Mock Out-UTF8File {}
|
||||
Mock get_config { return $true }
|
||||
}
|
||||
It 'should return manifest using database if available' {
|
||||
Mock Get-ScoopDBItem { $mockDbResult = [PSCustomObject] @{ Rows = @( [PSCustomObject] @{ manifest = '{"version": "2.3.4"}' }) } ; return $mockDbResult }
|
||||
$result = Get-HistoricalManifest 'testapp' 'testbucket' '2.3.4'
|
||||
$result.path | Should -Match 'testapp.json'
|
||||
$result.source | Should -Be "sqlite_exact_match"
|
||||
}
|
||||
It 'should return null if no bucket provided' {
|
||||
$result = Get-HistoricalManifest 'testapp' $null '2.3.4'
|
||||
$result | Should -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
Describe 'JSON parse and beautify' -Tag 'Scoop' {
|
||||
Context 'Beautify JSON' {
|
||||
BeforeDiscovery {
|
||||
$manifests = (Get-ChildItem "$PSScriptRoot\fixtures\format\formatted" -File -Filter '*.json').Name
|
||||
@@ -81,6 +26,7 @@ Describe 'JSON parse and beautify' -Tag 'Scoop' {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Handle ARM64 and correctly fallback' -Tag 'Scoop' {
|
||||
It 'Should return "arm64" if supported' {
|
||||
$manifest1 = @{ url = 'test'; architecture = @{ 'arm64' = @{ pre_install = 'test' } } }
|
||||
@@ -107,12 +53,14 @@ Describe 'Handle ARM64 and correctly fallback' -Tag 'Scoop' {
|
||||
Get-SupportedArchitecture $manifest3 'arm64' | Should -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Manifest Validator' -Tag 'Validator' {
|
||||
# Could not use backslash '\' in Linux/macOS for .NET object 'Scoop.Validator'
|
||||
BeforeAll {
|
||||
Add-Type -Path "$PSScriptRoot\..\supporting\validator\bin\Scoop.Validator.dll"
|
||||
$schema = "$PSScriptRoot/../schema.json"
|
||||
}
|
||||
|
||||
It 'Scoop.Validator is available' {
|
||||
([System.Management.Automation.PSTypeName]'Scoop.Validator').Type | Should -Be 'Scoop.Validator'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user