From 2dd91d5ba3c05f1828447c43c3e7cae37b0cc933 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Fri, 19 Apr 2024 14:02:50 +0800 Subject: [PATCH 01/33] feat(sqlite): Use SQLite for caching apps to speed up local search (#5851) --- .gitignore | 1 + CHANGELOG.md | 6 + lib/core.ps1 | 7 + lib/database.ps1 | 359 +++++++++++++++++++++++++++++++++++++ lib/manifest.ps1 | 24 ++- libexec/scoop-config.ps1 | 3 + libexec/scoop-download.ps1 | 3 + libexec/scoop-install.ps1 | 3 + libexec/scoop-search.ps1 | 98 +++++----- libexec/scoop-update.ps1 | 54 ++++-- 10 files changed, 494 insertions(+), 64 deletions(-) create mode 100644 lib/database.ps1 diff --git a/.gitignore b/.gitignore index fdc96d2a52..67ecc85d82 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ test/installer/tmp/* test/tmp/* *~ TestResults.xml +supporting/sqlite/* diff --git a/CHANGELOG.md b/CHANGELOG.md index 00e336583e..8b6c2957d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [Unreleased](https://github.com/ScoopInstaller/Scoop/compare/master...develop) + +### Features + +- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851)) + ## [v0.4.0](https://github.com/ScoopInstaller/Scoop/compare/v0.3.1...v0.4.0) - 2024-04-18 ### Features diff --git a/lib/core.ps1 b/lib/core.ps1 index cf60f27e4c..f86f9956c9 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -216,6 +216,13 @@ function Complete-ConfigChange { } } } + + if ($Name -eq 'use_sqlite_cache' -and $Value -eq $true) { + . "$PSScriptRoot\..\lib\database.ps1" + . "$PSScriptRoot\..\lib\manifest.ps1" + info 'Initializing SQLite cache in progress... This may take a while, please wait.' + Set-ScoopDB + } } function setup_proxy() { diff --git a/lib/database.ps1 b/lib/database.ps1 new file mode 100644 index 0000000000..059e36d9aa --- /dev/null +++ b/lib/database.ps1 @@ -0,0 +1,359 @@ +# Description: Functions for interacting with the Scoop database cache + +<# +.SYNOPSIS + Get SQLite .NET driver +.DESCRIPTION + Download and extract the SQLite .NET driver from NuGet. +.PARAMETER Version + System.String + The version of the SQLite .NET driver to download. +.INPUTS + None +.OUTPUTS + System.Boolean + True if the SQLite .NET driver was successfully downloaded and extracted, otherwise false. +#> +function Get-SQLite { + param ( + [string]$Version = '1.0.118' + ) + # Install SQLite + try { + Write-Host "Downloading SQLite $Version..." -ForegroundColor DarkYellow + $sqlitePkgPath = "$env:TEMP\sqlite.nupkg" + $sqliteTempPath = "$env:TEMP\sqlite" + $sqlitePath = "$PSScriptRoot\..\supporting\sqlite" + Invoke-WebRequest -Uri "https://api.nuget.org/v3-flatcontainer/stub.system.data.sqlite.core.netframework/$version/stub.system.data.sqlite.core.netframework.$version.nupkg" -OutFile $sqlitePkgPath + Write-Host "Extracting SQLite $Version..." -ForegroundColor DarkYellow -NoNewline + Expand-Archive -Path $sqlitePkgPath -DestinationPath $sqliteTempPath -Force + New-Item -Path $sqlitePath -ItemType Directory -Force | Out-Null + Move-Item -Path "$sqliteTempPath\build\net45\*" -Destination $sqlitePath -Exclude '*.targets' -Force + Move-Item -Path "$sqliteTempPath\lib\net45\System.Data.SQLite.dll" -Destination $sqlitePath -Force + Remove-Item -Path $sqlitePkgPath, $sqliteTempPath -Recurse -Force + Write-Host ' Done' -ForegroundColor DarkYellow + return $true + } catch { + return $false + } +} + +<# +.SYNOPSIS + Close a SQLite database. +.DESCRIPTION + Close a SQLite database connection. +.PARAMETER InputObject + System.Data.SQLite.SQLiteConnection + The SQLite database connection to close. +.INPUTS + System.Data.SQLite.SQLiteConnection +.OUTPUTS + None +#> +function Close-ScoopDB { + [CmdletBinding()] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [System.Data.SQLite.SQLiteConnection] + $InputObject + ) + process { + $InputObject.Dispose() + } +} + +<# +.SYNOPSIS + Create a new SQLite database. +.DESCRIPTION + Create a new SQLite database connection and create the necessary tables. +.PARAMETER PassThru + System.Management.Automation.SwitchParameter + Return the SQLite database connection. +.INPUTS + None +.OUTPUTS + None + Default + + System.Data.SQLite.SQLiteConnection + The SQLite database connection if **PassThru** is used. +#> +function New-ScoopDB ([switch]$PassThru) { + # Load System.Data.SQLite + if (!('System.Data.SQLite.SQLiteConnection' -as [Type])) { + try { + if (!(Test-Path -Path "$PSScriptRoot\..\supporting\sqlite\System.Data.SQLite.dll")) { + Get-SQLite | Out-Null + } + Add-Type -Path "$PSScriptRoot\..\supporting\sqlite\System.Data.SQLite.dll" + } catch { + throw "Scoop's Database cache requires the ADO.NET driver:`n`thttp://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki" + } + } + $dbPath = Join-Path $scoopdir 'scoop.db' + $db = New-Object -TypeName System.Data.SQLite.SQLiteConnection + $db.ConnectionString = "Data Source=$dbPath" + $db.ParseViaFramework = $true # Allow UNC path + $db.Open() + $tableCommand = $db.CreateCommand() + $tableCommand.CommandText = "CREATE TABLE IF NOT EXISTS 'app' ( + name TEXT NOT NULL COLLATE NOCASE, + description TEXT NOT NULL, + version TEXT NOT NULL, + bucket VARCHAR NOT NULL, + manifest JSON NOT NULL, + binary TEXT, + shortcut TEXT, + dependency TEXT, + suggest TEXT, + PRIMARY KEY (name, version, bucket) + )" + $tableCommand.ExecuteNonQuery() | Out-Null + $tableCommand.Dispose() + if ($PassThru) { + return $db + } else { + $db.Dispose() + } +} + +<# +.SYNOPSIS + Set Scoop database item(s). +.DESCRIPTION + Insert or replace Scoop database item(s) into the database. +.PARAMETER InputObject + System.Object[] + The database item(s) to insert or replace. +.INPUTS + System.Object[] +.OUTPUTS + None +#> +function Set-ScoopDBItem { + [CmdletBinding()] + param ( + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [psobject[]] + $InputObject + ) + + begin { + $db = New-ScoopDB -PassThru + $dbTrans = $db.BeginTransaction() + # TODO Support [hashtable]$InputObject + $colName = @($InputObject | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name) + $dbQuery = "INSERT OR REPLACE INTO app ($($colName -join ', ')) VALUES ($('@' + ($colName -join ', @')))" + $dbCommand = $db.CreateCommand() + $dbCommand.CommandText = $dbQuery + } + process { + foreach ($item in $InputObject) { + $item.PSObject.Properties | ForEach-Object { + $dbCommand.Parameters.AddWithValue("@$($_.Name)", $_.Value) | Out-Null + } + $dbCommand.ExecuteNonQuery() | Out-Null + } + } + end { + try { + $dbTrans.Commit() + } catch { + $dbTrans.Rollback() + throw $_ + } finally { + $db.Dispose() + } + } +} + +<# +.SYNOPSIS + Set Scoop app database item(s). +.DESCRIPTION + Insert or replace Scoop app(s) into the database. +.PARAMETER Path + System.String + The path to the bucket. +.PARAMETER CommitHash + System.String + The commit hash to compare with the HEAD. +.INPUTS + None +.OUTPUTS + None +#> +function Set-ScoopDB { + [CmdletBinding()] + param ( + [Parameter(Position = 0, ValueFromPipeline)] + [string[]] + $Path + ) + + begin { + $list = [System.Collections.Generic.List[PSCustomObject]]::new() + $arch = Get-DefaultArchitecture + } + process { + if ($Path.Count -eq 0) { + $bucketPath = Get-LocalBucket | ForEach-Object { Join-Path $bucketsdir $_ } + $Path = (Get-ChildItem $bucketPath -Filter '*.json' -Recurse).FullName + } + $Path | ForEach-Object { + $manifestRaw = [System.IO.File]::ReadAllText($_) + $manifest = $manifestRaw | ConvertFrom-Json -ErrorAction Continue + if ($null -ne $manifest.version) { + $list.Add([pscustomobject]@{ + name = $($_ -replace '.*[\\/]([^\\/]+)\.json$', '$1') + description = if ($manifest.description) { $manifest.description } else { '' } + version = $manifest.version + bucket = $($_ -replace '.*buckets[\\/]([^\\/]+)(?:[\\/].*)', '$1') + manifest = $manifestRaw + binary = $( + $result = @() + @(arch_specific 'bin' $manifest $arch) | ForEach-Object { + if ($_ -is [System.Array]) { + $result += "$($_[1]).$($_[0].Split('.')[-1])" + } else { + $result += $_ + } + } + $result -replace '.*?([^\\/]+)?(\.(exe|bat|cmd|ps1|jar|py))$', '$1' -join ' | ' + ) + shortcut = $( + $result = @() + @(arch_specific 'shortcuts' $manifest $arch) | ForEach-Object { + $result += $_[1] + } + $result -replace '.*?([^\\/]+$)', '$1' -join ' | ' + ) + dependency = $manifest.depends -join ' | ' + suggest = $( + $suggest_output = @() + $manifest.suggest.PSObject.Properties | ForEach-Object { + $suggest_output += $_.Value -join ' | ' + } + $suggest_output -join ' | ' + ) + }) + } + } + } + end { + if ($list.Count -ne 0) { + Set-ScoopDBItem $list + } + } +} + +<# +.SYNOPSIS + Select Scoop database item(s). +.DESCRIPTION + Select Scoop database item(s) from the database. + The pattern is matched against the name, binaries, and shortcuts columns for apps. +.PARAMETER Pattern + System.String + The pattern to search for. If is an empty string, all items will be returned. +.INPUTS + System.String +.OUTPUTS + System.Data.DataTable + The selected database item(s). +#> +function Select-ScoopDBItem { + [CmdletBinding()] + param ( + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [AllowEmptyString()] + [string] + $Pattern, + [Parameter(Mandatory, Position = 1)] + [string[]] + $From + ) + + begin { + $db = New-ScoopDB -PassThru + $dbAdapter = New-Object -TypeName System.Data.SQLite.SQLiteDataAdapter + $result = New-Object System.Data.DataTable + $dbQuery = "SELECT * FROM app WHERE $(($From -join ' LIKE @Pattern OR ') + ' LIKE @Pattern')" + $dbQuery = "SELECT * FROM ($($dbQuery + ' ORDER BY version DESC')) GROUP BY name, bucket" + $dbCommand = $db.CreateCommand() + $dbCommand.CommandText = $dbQuery + $dbCommand.CommandType = [System.Data.CommandType]::Text + } + process { + $dbCommand.Parameters.AddWithValue('@Pattern', $(if ($Pattern -eq '') { '%' } else { '%' + $Pattern + '%' })) | Out-Null + $dbAdapter.SelectCommand = $dbCommand + [void]$dbAdapter.Fill($result) + } + end { + $db.Dispose() + return $result + } +} + +<# +.SYNOPSIS + Get Scoop database item. +.DESCRIPTION + Get Scoop database item from the database. +.PARAMETER Name + System.String + The name of the item to get. +.PARAMETER Bucket + System.String + The bucket of the item to get. +.PARAMETER Version + System.String + The version of the item to get. If not provided, the latest version will be returned. +.INPUTS + System.String +.OUTPUTS + System.Data.DataTable + The selected database item. +#> +function Get-ScoopDBItem { + [CmdletBinding()] + param ( + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [string] + $Name, + [Parameter(Mandatory, Position = 1)] + [string] + $Bucket, + [Parameter(Position = 2)] + [string] + $Version + ) + + begin { + $db = New-ScoopDB -PassThru + $dbAdapter = New-Object -TypeName System.Data.SQLite.SQLiteDataAdapter + $result = New-Object System.Data.DataTable + $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 + } + process { + $dbCommand.Parameters.AddWithValue('@Name', $Name) | Out-Null + $dbCommand.Parameters.AddWithValue('@Bucket', $Bucket) | Out-Null + $dbCommand.Parameters.AddWithValue('@Version', $Version) | Out-Null + $dbAdapter.SelectCommand = $dbCommand + [void]$dbAdapter.Fill($result) + } + end { + $db.Dispose() + return $result + } +} diff --git a/lib/manifest.ps1 b/lib/manifest.ps1 index f66755c6f4..9ca618158b 100644 --- a/lib/manifest.ps1 +++ b/lib/manifest.ps1 @@ -23,7 +23,7 @@ function url_manifest($url) { } catch { throw } - if(!$str) { return $null } + if (!$str) { return $null } try { $str | ConvertFrom-Json -ErrorAction Stop } catch { @@ -137,16 +137,26 @@ function generate_user_manifest($app, $bucket, $version) { warn "Given version ($version) does not match manifest ($($manifest.version))" warn "Attempting to generate manifest for '$app' ($version)" + ensure (usermanifestsdir) | Out-Null + $manifest_path = "$(usermanifestsdir)\$app.json" + + if (get_config USE_SQLITE_CACHE) { + $cached_manifest = (Get-ScoopDBItem -Name $app -Bucket $bucket -Version $version).manifest + if ($cached_manifest) { + $cached_manifest | Out-UTF8File $manifest_path + return $manifest_path + } + } + if (!($manifest.autoupdate)) { abort "'$app' does not have autoupdate capability`r`ncouldn't find manifest for '$app@$version'" } - ensure (usermanifestsdir) | out-null try { - Invoke-AutoUpdate $app "$(Convert-Path (usermanifestsdir))\$app.json" $manifest $version $(@{ }) - return Convert-Path (usermanifest $app) + Invoke-AutoUpdate $app $manifest_path $manifest $version $(@{ }) + return $manifest_path } catch { - write-host -f darkred "Could not install $app@$version" + Write-Host -ForegroundColor DarkRed "Could not install $app@$version" } return $null @@ -156,5 +166,5 @@ function url($manifest, $arch) { arch_specific 'url' $manifest $arch } function installer($manifest, $arch) { arch_specific 'installer' $manifest $arch } function uninstaller($manifest, $arch) { arch_specific 'uninstaller' $manifest $arch } function hash($manifest, $arch) { arch_specific 'hash' $manifest $arch } -function extract_dir($manifest, $arch) { arch_specific 'extract_dir' $manifest $arch} -function extract_to($manifest, $arch) { arch_specific 'extract_to' $manifest $arch} +function extract_dir($manifest, $arch) { arch_specific 'extract_dir' $manifest $arch } +function extract_to($manifest, $arch) { arch_specific 'extract_to' $manifest $arch } diff --git a/libexec/scoop-config.ps1 b/libexec/scoop-config.ps1 index ff0bff3eee..6007bd6434 100644 --- a/libexec/scoop-config.ps1 +++ b/libexec/scoop-config.ps1 @@ -27,6 +27,9 @@ # use_lessmsi: $true|$false # Prefer lessmsi utility over native msiexec. # +# use_sqlite_cache: $true|$false +# Use SQLite database for caching. This is useful for speeding up 'scoop search' and 'scoop shim' commands. +# # no_junction: $true|$false # The 'current' version alias will not be used. Shims and shortcuts will point to specific version instead. # diff --git a/libexec/scoop-download.ps1 b/libexec/scoop-download.ps1 index ef43f8f41d..a4ba10d36e 100644 --- a/libexec/scoop-download.ps1 +++ b/libexec/scoop-download.ps1 @@ -24,6 +24,9 @@ . "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly) . "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest' . "$PSScriptRoot\..\lib\install.ps1" +if (get_config USE_SQLITE_CACHE) { + . "$PSScriptRoot\..\lib\database.ps1" +} $opt, $apps, $err = getopt $args 'fhua:' 'force', 'no-hash-check', 'no-update-scoop', 'arch=' if ($err) { error "scoop download: $err"; exit 1 } diff --git a/libexec/scoop-install.ps1 b/libexec/scoop-install.ps1 index fac03d71f4..1605efd2ac 100644 --- a/libexec/scoop-install.ps1 +++ b/libexec/scoop-install.ps1 @@ -32,6 +32,9 @@ . "$PSScriptRoot\..\lib\psmodules.ps1" . "$PSScriptRoot\..\lib\versions.ps1" . "$PSScriptRoot\..\lib\depends.ps1" +if (get_config USE_SQLITE_CACHE) { + . "$PSScriptRoot\..\lib\database.ps1" +} $opt, $apps, $err = getopt $args 'gikusa:' 'global', 'independent', 'no-cache', 'no-update-scoop', 'skip', 'arch=' if ($err) { "scoop install: $err"; exit 1 } diff --git a/libexec/scoop-search.ps1 b/libexec/scoop-search.ps1 index a9013afd02..be044513c1 100644 --- a/libexec/scoop-search.ps1 +++ b/libexec/scoop-search.ps1 @@ -3,6 +3,8 @@ # Help: Searches for apps that are available to install. # # If used with [query], shows app names that match the query. +# - With 'use_sqlite_cache' enabled, [query] is partially matched against app names, binaries, and shortcuts. +# - Without 'use_sqlite_cache', [query] can be a regular expression to match against app names and binaries. # Without [query], shows all the available apps. param($query) @@ -11,16 +13,10 @@ param($query) $list = [System.Collections.Generic.List[PSCustomObject]]::new() -try { - $query = New-Object Regex $query, 'IgnoreCase' -} catch { - abort "Invalid regular expression: $($_.Exception.InnerException.Message)" -} - $githubtoken = Get-GitHubToken $authheader = @{} if ($githubtoken) { - $authheader = @{'Authorization' = "token $githubtoken"} + $authheader = @{'Authorization' = "token $githubtoken" } } function bin_match($manifest, $query) { @@ -39,16 +35,16 @@ function bin_match($manifest, $query) { function bin_match_json($json, $query) { [System.Text.Json.JsonElement]$bin = [System.Text.Json.JsonElement]::new() - if (!$json.RootElement.TryGetProperty("bin", [ref] $bin)) { return $false } + if (!$json.RootElement.TryGetProperty('bin', [ref] $bin)) { return $false } $bins = @() - if($bin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($bin) -match $query) { + if ($bin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($bin) -match $query) { $bins += [System.IO.Path]::GetFileName($bin) } elseif ($bin.ValueKind -eq [System.Text.Json.JsonValueKind]::Array) { - foreach($subbin in $bin.EnumerateArray()) { - if($subbin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($subbin) -match $query) { + foreach ($subbin in $bin.EnumerateArray()) { + if ($subbin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($subbin) -match $query) { $bins += [System.IO.Path]::GetFileName($subbin) } elseif ($subbin.ValueKind -eq [System.Text.Json.JsonValueKind]::Array) { - if([System.IO.Path]::GetFileNameWithoutExtension($subbin[0]) -match $query) { + if ([System.IO.Path]::GetFileNameWithoutExtension($subbin[0]) -match $query) { $bins += [System.IO.Path]::GetFileName($subbin[0]) } elseif ($subbin.GetArrayLength() -ge 2 -and $subbin[1] -match $query) { $bins += $subbin[1] @@ -70,20 +66,20 @@ function search_bucket($bucket, $query) { if ($name -match $query) { $list.Add([PSCustomObject]@{ - Name = $name - Version = $json.RootElement.GetProperty("version") - Source = $bucket - Binaries = "" - }) + Name = $name + Version = $json.RootElement.GetProperty('version') + Source = $bucket + Binaries = '' + }) } else { $bin = bin_match_json $json $query if ($bin) { $list.Add([PSCustomObject]@{ - Name = $name - Version = $json.RootElement.GetProperty("version") - Source = $bucket - Binaries = $bin -join ' | ' - }) + Name = $name + Version = $json.RootElement.GetProperty('version') + Source = $bucket + Binaries = $bin -join ' | ' + }) } } } @@ -99,20 +95,20 @@ function search_bucket_legacy($bucket, $query) { if ($name -match $query) { $list.Add([PSCustomObject]@{ - Name = $name - Version = $manifest.Version - Source = $bucket - Binaries = "" - }) + Name = $name + Version = $manifest.Version + Source = $bucket + Binaries = '' + }) } else { $bin = bin_match $manifest $query if ($bin) { $list.Add([PSCustomObject]@{ - Name = $name - Version = $manifest.Version - Source = $bucket - Binaries = $bin -join ' | ' - }) + Name = $name + Version = $manifest.Version + Source = $bucket + Binaries = $bin -join ' | ' + }) } } } @@ -154,7 +150,7 @@ function search_remotes($query) { $names = $buckets | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name $results = $names | Where-Object { !(Test-Path $(Find-BucketDirectory $_)) } | ForEach-Object { - @{ "bucket" = $_; "results" = (search_remote $_ $query) } + @{ 'bucket' = $_; 'results' = (search_remote $_ $query) } } | Where-Object { $_.results } if ($results.count -gt 0) { @@ -175,25 +171,45 @@ function search_remotes($query) { $remote_list } -$jsonTextAvailable = [System.AppDomain]::CurrentDomain.GetAssemblies() | Where-object { [System.IO.Path]::GetFileNameWithoutExtension($_.Location) -eq "System.Text.Json" } +if (get_config USE_SQLITE_CACHE) { + . "$PSScriptRoot\..\lib\database.ps1" + Select-ScoopDBItem $query -From @('name', 'binary', 'shortcut') | + Select-Object -Property name, version, bucket, binary | + ForEach-Object { + $list.Add([PSCustomObject]@{ + Name = $_.name + Version = $_.version + Source = $_.bucket + Binaries = $_.binary + }) + } +} else { + try { + $query = New-Object Regex $query, 'IgnoreCase' + } catch { + abort "Invalid regular expression: $($_.Exception.InnerException.Message)" + } -Get-LocalBucket | ForEach-Object { - if ($jsonTextAvailable) { - search_bucket $_ $query - } else { - search_bucket_legacy $_ $query + $jsonTextAvailable = [System.AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { [System.IO.Path]::GetFileNameWithoutExtension($_.Location) -eq 'System.Text.Json' } + + Get-LocalBucket | ForEach-Object { + if ($jsonTextAvailable) { + search_bucket $_ $query + } else { + search_bucket_legacy $_ $query + } } } if ($list.Count -gt 0) { - Write-Host "Results from local buckets..." + Write-Host 'Results from local buckets...' $list } if ($list.Count -eq 0 -and !(github_ratelimit_reached)) { $remote_results = search_remotes $query if (!$remote_results) { - warn "No matches found." + warn 'No matches found.' exit 1 } $remote_results diff --git a/libexec/scoop-update.ps1 b/libexec/scoop-update.ps1 index 354fb2e839..fdcf8707a3 100644 --- a/libexec/scoop-update.ps1 +++ b/libexec/scoop-update.ps1 @@ -24,6 +24,9 @@ . "$PSScriptRoot\..\lib\versions.ps1" . "$PSScriptRoot\..\lib\depends.ps1" . "$PSScriptRoot\..\lib\install.ps1" +if (get_config USE_SQLITE_CACHE) { + . "$PSScriptRoot\..\lib\database.ps1" +} $opt, $apps, $err = getopt $args 'gfiksqa' 'global', 'force', 'independent', 'no-cache', 'skip', 'quiet', 'all' if ($err) { "scoop update: $err"; exit 1 } @@ -38,21 +41,21 @@ $all = $opt.a -or $opt.all # load config $configRepo = get_config SCOOP_REPO if (!$configRepo) { - $configRepo = "https://github.com/ScoopInstaller/Scoop" + $configRepo = 'https://github.com/ScoopInstaller/Scoop' set_config SCOOP_REPO $configRepo | Out-Null } # Find current update channel from config $configBranch = get_config SCOOP_BRANCH if (!$configBranch) { - $configBranch = "master" + $configBranch = 'master' set_config SCOOP_BRANCH $configBranch | Out-Null } -if(($PSVersionTable.PSVersion.Major) -lt 5) { +if (($PSVersionTable.PSVersion.Major) -lt 5) { # check powershell version - Write-Output "PowerShell 5 or later is required to run Scoop." - Write-Output "Upgrade PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows" + Write-Output 'PowerShell 5 or later is required to run Scoop.' + Write-Output 'Upgrade PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows' break } $show_update_log = get_config SHOW_UPDATE_LOG $true @@ -63,7 +66,7 @@ function Sync-Scoop { [Switch]$Log ) # Test if Scoop Core is hold - if(Test-ScoopCoreOnHold) { + if (Test-ScoopCoreOnHold) { return } @@ -108,10 +111,10 @@ function Sync-Scoop { # Stash uncommitted changes if (Invoke-Git -Path $currentdir -ArgumentList @('diff', 'HEAD', '--name-only')) { if (get_config AUTOSTASH_ON_CONFLICT) { - warn "Uncommitted changes detected. Stashing..." + warn 'Uncommitted changes detected. Stashing...' Invoke-Git -Path $currentdir -ArgumentList @('stash', 'push', '-m', "WIP at $([System.DateTime]::Now.ToString('o'))", '-u', '-q') } else { - warn "Uncommitted changes detected. Update aborted." + warn 'Uncommitted changes detected. Update aborted.' return } } @@ -152,7 +155,7 @@ function Sync-Bucket { Param ( [Switch]$Log ) - Write-Host "Updating Buckets..." + Write-Host 'Updating Buckets...' if (!(Test-Path (Join-Path (Find-BucketDirectory 'main' -Root) '.git'))) { info "Converting 'main' bucket to git repo..." @@ -170,14 +173,15 @@ function Sync-Bucket { $buckets = Get-LocalBucket | ForEach-Object { $path = Find-BucketDirectory $_ -Root return @{ - name = $_ + name = $_ valid = Test-Path (Join-Path $path '.git') - path = $path + path = $path } } $buckets | Where-Object { !$_.valid } | ForEach-Object { Write-Host "'$($_.name)' is not a git repository. Skipped." } + $updatedFiles = [System.Collections.ArrayList]::Synchronized([System.Collections.ArrayList]::new()) if ($PSVersionTable.PSVersion.Major -ge 7) { # Parallel parameter is available since PowerShell 7 $buckets | Where-Object { $_.valid } | ForEach-Object -ThrottleLimit 5 -Parallel { @@ -191,6 +195,13 @@ function Sync-Bucket { if ($using:Log) { Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit } + if (get_config USE_SQLITE_CACHE) { + Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-only', '--diff-filter=d', $previousCommit) | Where-Object { + $_ -match '^[^.].*\.json$' + } | ForEach-Object { + [void]($using:updatedFiles).Add($(Join-Path $bucketLoc $_)) + } + } } } else { $buckets | Where-Object { $_.valid } | ForEach-Object { @@ -202,8 +213,19 @@ function Sync-Bucket { if ($Log) { Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit } + if (get_config USE_SQLITE_CACHE) { + Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-only', '--diff-filter=d', $previousCommit) | Where-Object { + $_ -match '^[^.].*\.json$' + } | ForEach-Object { + [void]($updatedFiles).Add($(Join-Path $bucketLoc $_)) + } + } } } + if ((get_config USE_SQLITE_CACHE) -and ($updatedFiles.Count -gt 0)) { + info 'Updating cache...' + Set-ScoopDB -Path $updatedFiles + } } function update($app, $global, $quiet = $false, $independent, $suggested, $use_cache = $true, $check_hash = $true) { @@ -251,7 +273,7 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c # region Workaround # Workaround for https://github.com/ScoopInstaller/Scoop/issues/2220 until install is refactored # Remove and replace whole region after proper fix - Write-Host "Downloading new version" + Write-Host 'Downloading new version' if (Test-Aria2Enabled) { Invoke-CachedAria2Download $app $version $manifest $architecture $cachedir $manifest.cookie $true $check_hash } else { @@ -269,12 +291,12 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c error $err if (Test-Path $source) { # rm cached file - Remove-Item -force $source + Remove-Item -Force $source } if ($url.Contains('sourceforge.net')) { Write-Host -f yellow 'SourceForge.net is known for causing hash validation fails. Please try again before opening a ticket.' } - abort $(new_issue_msg $app $bucket "hash check failed") + abort $(new_issue_msg $app $bucket 'hash check failed') } } } @@ -404,11 +426,11 @@ if (-not ($apps -or $all)) { } elseif ($outdated.Length -eq 0) { Write-Host -f Green "Latest versions for all apps are installed! For more information try 'scoop status'" } else { - Write-Host -f DarkCyan "Updating one outdated app:" + Write-Host -f DarkCyan 'Updating one outdated app:' } } - $suggested = @{}; + $suggested = @{} # $outdated is a list of ($app, $global) tuples $outdated | ForEach-Object { update @_ $quiet $independent $suggested $use_cache $check_hash } } From eac58400dbacf772d40aed76f053e099a366fd68 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Thu, 25 Apr 2024 21:32:41 +0800 Subject: [PATCH 02/33] fix(sqlite): Skip `use_sqlite_cache` config on ARM64 platform (#5918) --- CHANGELOG.md | 2 +- lib/core.ps1 | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b6c2957d6..b2e872614f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Features -- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851)) +- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918)) ## [v0.4.0](https://github.com/ScoopInstaller/Scoop/compare/v0.3.1...v0.4.0) - 2024-04-18 diff --git a/lib/core.ps1 b/lib/core.ps1 index f86f9956c9..8d621d7779 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -218,6 +218,9 @@ function Complete-ConfigChange { } if ($Name -eq 'use_sqlite_cache' -and $Value -eq $true) { + if ((Get-DefaultArchitecture) -eq 'arm64') { + abort 'SQLite cache is not supported on ARM64 platform.' + } . "$PSScriptRoot\..\lib\database.ps1" . "$PSScriptRoot\..\lib\manifest.ps1" info 'Initializing SQLite cache in progress... This may take a while, please wait.' From 3b34497eb43ad310bc7c1ecb4181a956caa1c777 Mon Sep 17 00:00:00 2001 From: Chawye Hsu Date: Fri, 26 Apr 2024 18:12:34 +0800 Subject: [PATCH 03/33] fix(json): Serialize jsonpath return (#5921) --- CHANGELOG.md | 4 ++++ lib/json.ps1 | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 089a8987b6..b8c8cd75a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918)) +### Bug Fixes + +- **json:** Serialize jsonpath return ([#5921](https://github.com/ScoopInstaller/Scoop/issues/5921)) + ## [v0.4.1](https://github.com/ScoopInstaller/Scoop/compare/v0.4.0...v0.4.1) - 2024-04-25 ### Bug Fixes diff --git a/lib/json.ps1 b/lib/json.ps1 index 2469204dc5..d8d07f50a6 100644 --- a/lib/json.ps1 +++ b/lib/json.ps1 @@ -116,7 +116,7 @@ function json_path([String] $json, [String] $jsonpath, [Hashtable] $substitution # Convert first value to string $result = $result.ToString() } else { - $result = "$([String]::Join('\n', $result))" + $result = [Newtonsoft.Json.JsonConvert]::SerializeObject($result) } return $result } catch [Exception] { From 36026f1804090b1726fe8a7df699ca739fdb9cbc Mon Sep 17 00:00:00 2001 From: Chawye Hsu Date: Mon, 29 Apr 2024 18:36:19 +0800 Subject: [PATCH 04/33] feat(core): New cache filename format (#5929) Signed-off-by: Chawye Hsu Co-authored-by: Hsiao-nan Cheung --- CHANGELOG.md | 1 + lib/core.ps1 | 17 ++++++++++++++++- libexec/scoop-cache.ps1 | 4 ++-- test/Scoop-Core.Tests.ps1 | 17 +++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8c8cd75a6..3065fa7a72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features - **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918)) +- **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929) ### Bug Fixes diff --git a/lib/core.ps1 b/lib/core.ps1 index 2211a51972..90184fd5b6 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -409,7 +409,22 @@ function currentdir($app, $global) { function persistdir($app, $global) { "$(basedir $global)\persist\$app" } function usermanifestsdir { "$(basedir)\workspace" } function usermanifest($app) { "$(usermanifestsdir)\$app.json" } -function cache_path($app, $version, $url) { "$cachedir\$app#$version#$($url -replace '[^\w\.\-]+', '_')" } +function cache_path($app, $version, $url) { + $underscoredUrl = $url -replace '[^\w\.\-]+', '_' + $filePath = "$cachedir\$app#$version#$underscoredUrl" + + # NOTE: Scoop cache files migration. Remove this 6 months after the feature ships. + if (Test-Path $filePath) { + return $filePath + } + + $urlStream = [System.IO.MemoryStream]::new([System.Text.Encoding]::UTF8.GetBytes($url)) + $sha = (Get-FileHash -Algorithm SHA256 -InputStream $urlStream).Hash.ToLower().Substring(0, 7) + $extension = [System.IO.Path]::GetExtension($url) + $filePath = $filePath -replace "$underscoredUrl", "$sha$extension" + + return $filePath +} # apps function sanitary_path($path) { return [regex]::replace($path, "[/\\?:*<>|]", "") } diff --git a/libexec/scoop-cache.ps1 b/libexec/scoop-cache.ps1 index 959c73fcde..f390c258ad 100644 --- a/libexec/scoop-cache.ps1 +++ b/libexec/scoop-cache.ps1 @@ -16,7 +16,7 @@ param($cmd) function cacheinfo($file) { $app, $version, $url = $file.Name -split '#' - New-Object PSObject -Property @{ Name = $app; Version = $version; Length = $file.Length; URL = $url } + New-Object PSObject -Property @{ Name = $app; Version = $version; Length = $file.Length } } function cacheshow($app) { @@ -28,7 +28,7 @@ function cacheshow($app) { $files = @(Get-ChildItem $cachedir | Where-Object -Property Name -Value "^$app#" -Match) $totalLength = ($files | Measure-Object -Property Length -Sum).Sum - $files | ForEach-Object { cacheinfo $_ } | Select-Object Name, Version, Length, URL + $files | ForEach-Object { cacheinfo $_ } | Select-Object Name, Version, Length Write-Host "Total: $($files.Length) $(pluralize $files.Length 'file' 'files'), $(filesize $totalLength)" -ForegroundColor Yellow } diff --git a/test/Scoop-Core.Tests.ps1 b/test/Scoop-Core.Tests.ps1 index 3fb7fbd663..f6846e5456 100644 --- a/test/Scoop-Core.Tests.ps1 +++ b/test/Scoop-Core.Tests.ps1 @@ -259,6 +259,23 @@ Describe 'get_app_name_from_shim' -Tag 'Scoop', 'Windows' { } } +Describe 'cache_path' -Tag 'Scoop' { + It 'returns the correct cache path for a given input' { + $url = 'https://example.com/git.zip' + $ret = cache_path 'git' '2.44.0' $url + $inputStream = [System.IO.MemoryStream]::new([System.Text.Encoding]::UTF8.GetBytes($url)) + $sha = (Get-FileHash -Algorithm SHA256 -InputStream $inputStream).Hash.ToLower().Substring(0, 7) + $ret | Should -Be "$cachedir\git#2.44.0#$sha.zip" + } + + # # NOTE: Remove this 6 months after the feature ships. + It 'returns the old format cache path for a given input' { + Mock Test-Path { $true } + $ret = cache_path 'git' '2.44.0' 'https://example.com/git.zip' + $ret | Should -Be "$cachedir\git#2.44.0#https_example.com_git.zip" + } +} + Describe 'sanitary_path' -Tag 'Scoop' { It 'removes invalid path characters from a string' { $path = 'test?.json' From 23d55ced727ea954bc42a025ca0f146554efc4dd Mon Sep 17 00:00:00 2001 From: Chawye Hsu Date: Wed, 1 May 2024 00:52:49 +0800 Subject: [PATCH 05/33] fix(scoop-search): Catch error of parsing invalid manifest (#5930) --- CHANGELOG.md | 1 + libexec/scoop-search.ps1 | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3065fa7a72..aa5314ce26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Bug Fixes - **json:** Serialize jsonpath return ([#5921](https://github.com/ScoopInstaller/Scoop/issues/5921)) +- **scoop-search:** Catch error of parsing invalid manifest ([#5930](https://github.com/ScoopInstaller/Scoop/issues/5930)) ## [v0.4.1](https://github.com/ScoopInstaller/Scoop/compare/v0.4.0...v0.4.1) - 2024-04-25 diff --git a/libexec/scoop-search.ps1 b/libexec/scoop-search.ps1 index be044513c1..4254099989 100644 --- a/libexec/scoop-search.ps1 +++ b/libexec/scoop-search.ps1 @@ -61,7 +61,15 @@ function search_bucket($bucket, $query) { $apps = Get-ChildItem (Find-BucketDirectory $bucket) -Filter '*.json' -Recurse $apps | ForEach-Object { - $json = [System.Text.Json.JsonDocument]::Parse([System.IO.File]::ReadAllText($_.FullName)) + $filepath = $_.FullName + + $json = try { + [System.Text.Json.JsonDocument]::Parse([System.IO.File]::ReadAllText($filepath)) + } catch { + debug "Failed to parse manifest file: $filepath (error: $_)" + return + } + $name = $_.BaseName if ($name -match $query) { From b8580aa931bd8d85d5f0f2528b87d469ff7985bf Mon Sep 17 00:00:00 2001 From: Xuesong Date: Wed, 1 May 2024 23:02:13 +0800 Subject: [PATCH 06/33] fix(autoupdate): Copy `PSCustomObject`-type properties within `autoupdate` to prevent reference changes (#5934) --- CHANGELOG.md | 1 + lib/autoupdate.ps1 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa5314ce26..10131c3632 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - **json:** Serialize jsonpath return ([#5921](https://github.com/ScoopInstaller/Scoop/issues/5921)) - **scoop-search:** Catch error of parsing invalid manifest ([#5930](https://github.com/ScoopInstaller/Scoop/issues/5930)) +- **autoupdate:** Copy `PSCustomObject`-type properties within `autoupdate` to prevent reference changes ([#5934](https://github.com/ScoopInstaller/Scoop/issues/5934)) ## [v0.4.1](https://github.com/ScoopInstaller/Scoop/compare/v0.4.0...v0.4.1) - 2024-04-25 diff --git a/lib/autoupdate.ps1 b/lib/autoupdate.ps1 index bd24e04015..705c767451 100644 --- a/lib/autoupdate.ps1 +++ b/lib/autoupdate.ps1 @@ -365,7 +365,7 @@ function Update-ManifestProperty { } } elseif ($Manifest.$currentProperty -and $Manifest.autoupdate.$currentProperty) { # Update other property (global) - $autoupdateProperty = $Manifest.autoupdate.$currentProperty + $autoupdateProperty = $Manifest.autoupdate.$currentProperty.PSObject.Copy() $newValue = substitute $autoupdateProperty $Substitutions if (($autoupdateProperty.GetType().Name -eq 'Object[]') -and ($autoupdateProperty.Length -eq 1)) { # Make sure it's an array From 5b86c302e5c43e2edfcebfe7acec58ee81969c7f Mon Sep 17 00:00:00 2001 From: Inseo Lee Date: Mon, 6 May 2024 11:04:40 +0900 Subject: [PATCH 07/33] fix(system): Fix argument passing to `Split-PathLikeEnvVar()` in deprecated `strip_path()` (#5937) Co-authored-by: Hsiao-nan Cheung --- CHANGELOG.md | 1 + lib/system.ps1 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10131c3632..619b1db94c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - **json:** Serialize jsonpath return ([#5921](https://github.com/ScoopInstaller/Scoop/issues/5921)) - **scoop-search:** Catch error of parsing invalid manifest ([#5930](https://github.com/ScoopInstaller/Scoop/issues/5930)) - **autoupdate:** Copy `PSCustomObject`-type properties within `autoupdate` to prevent reference changes ([#5934](https://github.com/ScoopInstaller/Scoop/issues/5934)) +- **system:** Fix argument passing to `Split-PathLikeEnvVar()` in deprecated `strip_path()` ([#5937](https://github.com/ScoopInstaller/Scoop/issues/5937)) ## [v0.4.1](https://github.com/ScoopInstaller/Scoop/compare/v0.4.0...v0.4.1) - 2024-04-25 diff --git a/lib/system.ps1 b/lib/system.ps1 index affe2c5450..692cb40275 100644 --- a/lib/system.ps1 +++ b/lib/system.ps1 @@ -162,7 +162,7 @@ function env($name, $global, $val) { function strip_path($orig_path, $dir) { Show-DeprecatedWarning $MyInvocation 'Split-PathLikeEnvVar' - Split-PathLikeEnvVar -Name $dir -Path $orig_path + Split-PathLikeEnvVar -Pattern @($dir) -Path $orig_path } function add_first_in_path($dir, $global) { From cc863353e27162f4258bd0147be6c3510ef6611f Mon Sep 17 00:00:00 2001 From: Chawye Hsu Date: Wed, 8 May 2024 11:24:41 +0800 Subject: [PATCH 08/33] fix(scoop-cache): Fix regression in 36026f18 (#5944) Signed-off-by: Chawye Hsu --- CHANGELOG.md | 1 + libexec/scoop-cache.ps1 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 619b1db94c..1b439a5acc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - **scoop-search:** Catch error of parsing invalid manifest ([#5930](https://github.com/ScoopInstaller/Scoop/issues/5930)) - **autoupdate:** Copy `PSCustomObject`-type properties within `autoupdate` to prevent reference changes ([#5934](https://github.com/ScoopInstaller/Scoop/issues/5934)) - **system:** Fix argument passing to `Split-PathLikeEnvVar()` in deprecated `strip_path()` ([#5937](https://github.com/ScoopInstaller/Scoop/issues/5937)) +- **scoop-cache:** Fix regression in 36026f18 ([#5944](https://github.com/ScoopInstaller/Scoop/issues/5944)) ## [v0.4.1](https://github.com/ScoopInstaller/Scoop/compare/v0.4.0...v0.4.1) - 2024-04-25 diff --git a/libexec/scoop-cache.ps1 b/libexec/scoop-cache.ps1 index f390c258ad..30e8354fca 100644 --- a/libexec/scoop-cache.ps1 +++ b/libexec/scoop-cache.ps1 @@ -48,7 +48,7 @@ function cacheremove($app) { $files | ForEach-Object { $curr = cacheinfo $_ - Write-Host "Removing $($curr.URL)..." + Write-Host "Removing $($_.Name)..." Remove-Item $_.FullName if(Test-Path "$cachedir\$($curr.Name).txt") { Remove-Item "$cachedir\$($curr.Name).txt" From 776135e0ab9832e7844d9298de8b810c4f950af0 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Wed, 8 May 2024 18:10:13 +0800 Subject: [PATCH 09/33] fix(database): Update cache when adding bucket (#5946) --- CHANGELOG.md | 4 ++-- lib/buckets.ps1 | 4 ++++ lib/database.ps1 | 4 ++-- libexec/scoop-bucket.ps1 | 5 +++++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b439a5acc..78e7ca5410 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,8 @@ ### Features -- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918)) -- **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929) +- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946)) +- **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929)) ### Bug Fixes diff --git a/lib/buckets.ps1 b/lib/buckets.ps1 index 63f6afc0a7..ca12684ca6 100644 --- a/lib/buckets.ps1 +++ b/lib/buckets.ps1 @@ -156,6 +156,10 @@ function add_bucket($name, $repo) { $dir = ensure $dir Invoke-Git -ArgumentList @('clone', $repo, $dir, '-q') Write-Host 'OK' + if (get_config USE_SQLITE_CACHE) { + info 'Updating cache...' + Set-ScoopDB -Path (Get-ChildItem -Path $dir -Filter '*.json' -Recurse).FullName + } success "The $name bucket was added successfully." return 0 } diff --git a/lib/database.ps1 b/lib/database.ps1 index 059e36d9aa..6a386434fd 100644 --- a/lib/database.ps1 +++ b/lib/database.ps1 @@ -202,9 +202,9 @@ function Set-ScoopDB { $bucketPath = Get-LocalBucket | ForEach-Object { Join-Path $bucketsdir $_ } $Path = (Get-ChildItem $bucketPath -Filter '*.json' -Recurse).FullName } - $Path | ForEach-Object { + $Path | Where-Object { $_ -notmatch '[\\/]\.' } | ForEach-Object { $manifestRaw = [System.IO.File]::ReadAllText($_) - $manifest = $manifestRaw | ConvertFrom-Json -ErrorAction Continue + $manifest = ConvertFrom-Json $manifestRaw -ErrorAction SilentlyContinue if ($null -ne $manifest.version) { $list.Add([pscustomobject]@{ name = $($_ -replace '.*[\\/]([^\\/]+)\.json$', '$1') diff --git a/libexec/scoop-bucket.ps1 b/libexec/scoop-bucket.ps1 index 6a2e90e6cb..ceb28865a5 100644 --- a/libexec/scoop-bucket.ps1 +++ b/libexec/scoop-bucket.ps1 @@ -19,6 +19,11 @@ # scoop bucket known param($cmd, $name, $repo) +if (get_config USE_SQLITE_CACHE) { + . "$PSScriptRoot\..\lib\manifest.ps1" + . "$PSScriptRoot\..\lib\database.ps1" +} + $usage_add = 'usage: scoop bucket add []' $usage_rm = 'usage: scoop bucket rm ' From edaae8d39fad713032f1ef495228b9f92ad47cfb Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Thu, 9 May 2024 20:15:57 +0800 Subject: [PATCH 10/33] fix(database): Skip caching 'deprecated' dir (#5949) --- CHANGELOG.md | 2 +- lib/database.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78e7ca5410..57d97446d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Features -- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946)) +- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5948](https://github.com/ScoopInstaller/Scoop/issues/5948)) - **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929)) ### Bug Fixes diff --git a/lib/database.ps1 b/lib/database.ps1 index 6a386434fd..5e329cd687 100644 --- a/lib/database.ps1 +++ b/lib/database.ps1 @@ -202,7 +202,7 @@ function Set-ScoopDB { $bucketPath = Get-LocalBucket | ForEach-Object { Join-Path $bucketsdir $_ } $Path = (Get-ChildItem $bucketPath -Filter '*.json' -Recurse).FullName } - $Path | Where-Object { $_ -notmatch '[\\/]\.' } | ForEach-Object { + $Path | Where-Object { $_ -notmatch '[\\/]\.|[\\/]deprecated[\\/]' } | ForEach-Object { $manifestRaw = [System.IO.File]::ReadAllText($_) $manifest = ConvertFrom-Json $manifestRaw -ErrorAction SilentlyContinue if ($null -ne $manifest.version) { From 1752050614f2fbfc350b04fa73fb6a25b420b4e8 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Sat, 11 May 2024 14:49:49 +0800 Subject: [PATCH 11/33] fix(core): Fix "Invoke-ExternalCommand" quoting rules (#5945) --- CHANGELOG.md | 1 + lib/core.ps1 | 40 +++++++++++---------- lib/decompress.ps1 | 2 +- test/Scoop-Decompress.Tests.ps1 | 47 ++++++++++++++++++++++--- test/Scoop-TestLib.ps1 | 4 +-- test/fixtures/decompress/TestCases.zip | Bin 499953 -> 532697 bytes 6 files changed, 68 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57d97446d4..313f68c23f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - **autoupdate:** Copy `PSCustomObject`-type properties within `autoupdate` to prevent reference changes ([#5934](https://github.com/ScoopInstaller/Scoop/issues/5934)) - **system:** Fix argument passing to `Split-PathLikeEnvVar()` in deprecated `strip_path()` ([#5937](https://github.com/ScoopInstaller/Scoop/issues/5937)) - **scoop-cache:** Fix regression in 36026f18 ([#5944](https://github.com/ScoopInstaller/Scoop/issues/5944)) +- **core:** Fix "Invoke-ExternalCommand" quoting rules ([#5945](https://github.com/ScoopInstaller/Scoop/issues/5945)) ## [v0.4.1](https://github.com/ScoopInstaller/Scoop/compare/v0.4.0...v0.4.1) - 2024-04-25 diff --git a/lib/core.ps1 b/lib/core.ps1 index 90184fd5b6..bc182464fe 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -771,31 +771,35 @@ function Invoke-ExternalCommand { $Process.StartInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden } if ($ArgumentList.Length -gt 0) { - $ArgumentList = $ArgumentList | ForEach-Object { [regex]::Split($_.Replace('"', ''), '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

npgWblW(sV#WkfX5WHMIQ9U=(o>wHyrAGlC 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 From 1dd479f0be6a90b8734ac99b0396c1232a9c9666 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Sat, 11 May 2024 14:50:20 +0800 Subject: [PATCH 12/33] fix(database): Use 'Find-BucketDirectory()' to locate bucket dir (#5955) --- CHANGELOG.md | 2 +- lib/buckets.ps1 | 2 +- lib/database.ps1 | 4 ++-- libexec/scoop-update.ps1 | 25 +++++++++++++++---------- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 313f68c23f..68f4474059 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Features -- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5948](https://github.com/ScoopInstaller/Scoop/issues/5948)) +- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5949](https://github.com/ScoopInstaller/Scoop/issues/5949), [#5955](https://github.com/ScoopInstaller/Scoop/issues/5955)) - **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929)) ### Bug Fixes diff --git a/lib/buckets.ps1 b/lib/buckets.ps1 index ca12684ca6..2adac4f339 100644 --- a/lib/buckets.ps1 +++ b/lib/buckets.ps1 @@ -158,7 +158,7 @@ function add_bucket($name, $repo) { Write-Host 'OK' if (get_config USE_SQLITE_CACHE) { info 'Updating cache...' - Set-ScoopDB -Path (Get-ChildItem -Path $dir -Filter '*.json' -Recurse).FullName + Set-ScoopDB -Path (Get-ChildItem (Find-BucketDirectory $name) -Filter '*.json' -Recurse).FullName } success "The $name bucket was added successfully." return 0 diff --git a/lib/database.ps1 b/lib/database.ps1 index 5e329cd687..e15720d7da 100644 --- a/lib/database.ps1 +++ b/lib/database.ps1 @@ -199,10 +199,10 @@ function Set-ScoopDB { } process { if ($Path.Count -eq 0) { - $bucketPath = Get-LocalBucket | ForEach-Object { Join-Path $bucketsdir $_ } + $bucketPath = Get-LocalBucket | ForEach-Object { Find-BucketDirectory $_ } $Path = (Get-ChildItem $bucketPath -Filter '*.json' -Recurse).FullName } - $Path | Where-Object { $_ -notmatch '[\\/]\.|[\\/]deprecated[\\/]' } | ForEach-Object { + $Path | ForEach-Object { $manifestRaw = [System.IO.File]::ReadAllText($_) $manifest = ConvertFrom-Json $manifestRaw -ErrorAction SilentlyContinue if ($null -ne $manifest.version) { diff --git a/libexec/scoop-update.ps1 b/libexec/scoop-update.ps1 index fdcf8707a3..79bd706a65 100644 --- a/libexec/scoop-update.ps1 +++ b/libexec/scoop-update.ps1 @@ -186,9 +186,11 @@ function Sync-Bucket { # Parallel parameter is available since PowerShell 7 $buckets | Where-Object { $_.valid } | ForEach-Object -ThrottleLimit 5 -Parallel { . "$using:PSScriptRoot\..\lib\core.ps1" + . "$using:PSScriptRoot\..\lib\buckets.ps1" - $bucketLoc = $_.path $name = $_.name + $bucketLoc = $_.path + $innerBucketLoc = Find-BucketDirectory $name $previousCommit = Invoke-Git -Path $bucketLoc -ArgumentList @('rev-parse', 'HEAD') Invoke-Git -Path $bucketLoc -ArgumentList @('pull', '-q') @@ -196,17 +198,19 @@ function Sync-Bucket { Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit } if (get_config USE_SQLITE_CACHE) { - Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-only', '--diff-filter=d', $previousCommit) | Where-Object { - $_ -match '^[^.].*\.json$' - } | ForEach-Object { - [void]($using:updatedFiles).Add($(Join-Path $bucketLoc $_)) + Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-only', '--diff-filter=d', $previousCommit) | ForEach-Object { + $filePath = Join-Path $bucketLoc $_ + if ($filePath -match "^$([regex]::Escape($innerBucketLoc)).*\.json$") { + [void]($using:updatedFiles).Add($filePath) + } } } } } else { $buckets | Where-Object { $_.valid } | ForEach-Object { - $bucketLoc = $_.path $name = $_.name + $bucketLoc = $_.path + $innerBucketLoc = Find-BucketDirectory $name $previousCommit = Invoke-Git -Path $bucketLoc -ArgumentList @('rev-parse', 'HEAD') Invoke-Git -Path $bucketLoc -ArgumentList @('pull', '-q') @@ -214,10 +218,11 @@ function Sync-Bucket { Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit } if (get_config USE_SQLITE_CACHE) { - Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-only', '--diff-filter=d', $previousCommit) | Where-Object { - $_ -match '^[^.].*\.json$' - } | ForEach-Object { - [void]($updatedFiles).Add($(Join-Path $bucketLoc $_)) + Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-only', '--diff-filter=d', $previousCommit) | ForEach-Object { + $filePath = Join-Path $bucketLoc $_ + if ($filePath -match "^$([regex]::Escape($innerBucketLoc)).*\.json$") { + [void]($updatedFiles).Add($filePath) + } } } } From b710ff6c0ad96682743a1d4af0c41961a375f9b0 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Sat, 11 May 2024 18:22:31 +0800 Subject: [PATCH 13/33] fix(scoop-info): Fix download size estimating (#5958) --- CHANGELOG.md | 1 + libexec/scoop-info.ps1 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68f4474059..f3cc670c56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - **system:** Fix argument passing to `Split-PathLikeEnvVar()` in deprecated `strip_path()` ([#5937](https://github.com/ScoopInstaller/Scoop/issues/5937)) - **scoop-cache:** Fix regression in 36026f18 ([#5944](https://github.com/ScoopInstaller/Scoop/issues/5944)) - **core:** Fix "Invoke-ExternalCommand" quoting rules ([#5945](https://github.com/ScoopInstaller/Scoop/issues/5945)) +- **scoop-info:** Fix download size estimating ([#5958](https://github.com/ScoopInstaller/Scoop/issues/5958)) ## [v0.4.1](https://github.com/ScoopInstaller/Scoop/compare/v0.4.0...v0.4.1) - 2024-04-25 diff --git a/libexec/scoop-info.ps1 b/libexec/scoop-info.ps1 index 853de647f9..b7cf7cdeb0 100644 --- a/libexec/scoop-info.ps1 +++ b/libexec/scoop-info.ps1 @@ -166,7 +166,7 @@ if ($status.installed) { $cached = $null } - [int]$urlLength = (Invoke-WebRequest $url -Method Head).Headers.'Content-Length'[0] + $urlLength = (Invoke-WebRequest $url -Method Head).Headers.'Content-Length' | ForEach-Object { [int]$_ } $totalPackage += $urlLength } catch [System.Management.Automation.RuntimeException] { $totalPackage = 0 From a5bd2297c6227e5b09869acafaa12cbdc73f6132 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Sun, 12 May 2024 18:47:16 +0800 Subject: [PATCH 14/33] refactor(install): Separate archive extraction from downloader (#5951) --- CHANGELOG.md | 4 +++ lib/decompress.ps1 | 68 ++++++++++++++++++++++++++++++++++++++++++++++ lib/install.ps1 | 55 ++++--------------------------------- 3 files changed, 77 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3cc670c56..db72b20961 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ - **core:** Fix "Invoke-ExternalCommand" quoting rules ([#5945](https://github.com/ScoopInstaller/Scoop/issues/5945)) - **scoop-info:** Fix download size estimating ([#5958](https://github.com/ScoopInstaller/Scoop/issues/5958)) +### Code Refactoring + +- **install:** Separate archive extraction from downloader ([#5951](https://github.com/ScoopInstaller/Scoop/issues/5951)) + ## [v0.4.1](https://github.com/ScoopInstaller/Scoop/compare/v0.4.0...v0.4.1) - 2024-04-25 ### Bug Fixes diff --git a/lib/decompress.ps1 b/lib/decompress.ps1 index c2b04800d5..3553621ced 100644 --- a/lib/decompress.ps1 +++ b/lib/decompress.ps1 @@ -1,3 +1,71 @@ +# Description: Functions for decompressing archives or installers + +function Invoke-Extraction { + param ( + [string] + $Path, + [string[]] + $Name, + [psobject] + $Manifest, + [Alias('Arch', 'Architecture')] + [string] + $ProcessorArchitecture + ) + + # 'url', 'extract_dir' and 'extract_to' are paired + $uri = @(url $Manifest $ProcessorArchitecture) + $extractDir = @(extract_dir $Manifest $ProcessorArchitecture) + $extractTo = @(extract_to $Manifest $ProcessorArchitecture) + + for ($i = 0; $i -lt $Name.Length; $i++) { + $fnArgs = @{ + Path = Join-Path $Path $Name[$i] + DestinationPath = Join-Path $Path $extractTo[$i] + ExtractDir = $extractDir[$i] + } + # work out extraction method, if applicable + $extractFn = $null + switch -regex ($fnArgs.Path) { + '\.zip$' { + if ((Test-HelperInstalled -Helper 7zip) -or ((get_config 7ZIPEXTRACT_USE_EXTERNAL) -and (Test-CommandAvailable 7z))) { + $extractFn = 'Expand-7zipArchive' + } else { + $extractFn = 'Expand-ZipArchive' + } + continue + } + '\.msi$' { + $extractFn = 'Expand-MsiArchive' + continue + } + '\.exe$' { + if ($Manifest.innosetup) { + $extractFn = 'Expand-InnoArchive' + } + continue + } + { Test-ZstdRequirement -Uri $_ } { + # Check Zstd first + $extractFn = 'Expand-ZstdArchive' + continue + } + { Test-7zipRequirement -Uri $_ } { + # Then check 7zip + $extractFn = 'Expand-7zipArchive' + continue + } + } + if ($extractFn) { + Write-Host 'Extracting ' -NoNewline + Write-Host $(url_remote_filename $uri[$i]) -ForegroundColor Cyan -NoNewline + Write-Host ' ... ' -NoNewline + & $extractFn @fnArgs -Removal + Write-Host 'done.' -ForegroundColor Green + } + } +} + function Expand-7zipArchive { [CmdletBinding()] param ( diff --git a/lib/install.ps1 b/lib/install.ps1 index 12d4220015..f70b1bf9e0 100644 --- a/lib/install.ps1 +++ b/lib/install.ps1 @@ -50,9 +50,10 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru $persist_dir = persistdir $app $global $fname = Invoke-ScoopDownload $app $version $manifest $bucket $architecture $dir $use_cache $check_hash + Invoke-Extraction -Path $dir -Name $fname -Manifest $manifest -ProcessorArchitecture $architecture Invoke-HookScript -HookType 'pre_install' -Manifest $manifest -Arch $architecture - run_installer $fname $manifest $architecture $dir $global + run_installer @($fname)[-1] $manifest $architecture $dir $global ensure_install_dir_not_in_path $dir $global $dir = link_current $dir create_shims $manifest $dir $global $architecture @@ -539,21 +540,12 @@ function Invoke-ScoopDownload ($app, $version, $manifest, $bucket, $architecture # we only want to show this warning once if (!$use_cache) { warn 'Cache is being ignored.' } - # can be multiple urls: if there are, then installer should go last, - # so that $fname is set properly + # can be multiple urls: if there are, then installer should go last to make 'installer.args' section work $urls = @(script:url $manifest $architecture) # can be multiple cookies: they will be used for all HTTP requests. $cookies = $manifest.cookie - $fname = $null - - # extract_dir and extract_to in manifest are like queues: for each url that - # needs to be extracted, will get the next dir from the queue - $extract_dirs = @(extract_dir $manifest $architecture) - $extract_tos = @(extract_to $manifest $architecture) - $extracted = 0 - # download first if (Test-Aria2Enabled) { Invoke-CachedAria2Download $app $version $manifest $architecture $dir $cookies $use_cache $check_hash @@ -587,44 +579,7 @@ function Invoke-ScoopDownload ($app, $version, $manifest, $bucket, $architecture } } - foreach ($url in $urls) { - $fname = url_filename $url - - $extract_dir = $extract_dirs[$extracted] - $extract_to = $extract_tos[$extracted] - - # work out extraction method, if applicable - $extract_fn = $null - if ($manifest.innosetup) { - $extract_fn = 'Expand-InnoArchive' - } elseif ($fname -match '\.zip$') { - # Use 7zip when available (more fast) - if (((get_config USE_EXTERNAL_7ZIP) -and (Test-CommandAvailable 7z)) -or (Test-HelperInstalled -Helper 7zip)) { - $extract_fn = 'Expand-7zipArchive' - } else { - $extract_fn = 'Expand-ZipArchive' - } - } elseif ($fname -match '\.msi$') { - $extract_fn = 'Expand-MsiArchive' - } elseif (Test-ZstdRequirement -Uri $fname) { - # Zstd first - $extract_fn = 'Expand-ZstdArchive' - } elseif (Test-7zipRequirement -Uri $fname) { - # 7zip - $extract_fn = 'Expand-7zipArchive' - } - - if ($extract_fn) { - Write-Host 'Extracting ' -NoNewline - Write-Host $fname -f Cyan -NoNewline - Write-Host ' ... ' -NoNewline - & $extract_fn -Path "$dir\$fname" -DestinationPath "$dir\$extract_to" -ExtractDir $extract_dir -Removal - Write-Host 'done.' -f Green - $extracted++ - } - } - - $fname # returns the last downloaded file + return $urls.ForEach({ url_filename $_ }) } function cookie_header($cookies) { @@ -710,7 +665,7 @@ function run_installer($fname, $manifest, $architecture, $dir, $global) { return } if ($installer) { - $prog = "$dir\$(coalesce $installer.file "$fname")" + $prog = "$dir\$(coalesce $installer.file $fname)" if (!(is_in_dir $dir $prog)) { abort "Error in manifest: Installer $prog is outside the app directory." } From 4dd2cfdc5f5cc347c1220c7fb769b9af58f01dc3 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Mon, 13 May 2024 13:46:59 +0800 Subject: [PATCH 15/33] fix(core): Add 'PSObject.Copy()' to 'substitute()' (#5962) --- CHANGELOG.md | 2 +- lib/autoupdate.ps1 | 2 +- lib/core.ps1 | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db72b20961..53d07d6f51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ - **json:** Serialize jsonpath return ([#5921](https://github.com/ScoopInstaller/Scoop/issues/5921)) - **scoop-search:** Catch error of parsing invalid manifest ([#5930](https://github.com/ScoopInstaller/Scoop/issues/5930)) -- **autoupdate:** Copy `PSCustomObject`-type properties within `autoupdate` to prevent reference changes ([#5934](https://github.com/ScoopInstaller/Scoop/issues/5934)) +- **autoupdate:** Copy `PSCustomObject`-type properties within `substitute()` to prevent reference changes ([#5934](https://github.com/ScoopInstaller/Scoop/issues/5934), [#5962](https://github.com/ScoopInstaller/Scoop/issues/5962)) - **system:** Fix argument passing to `Split-PathLikeEnvVar()` in deprecated `strip_path()` ([#5937](https://github.com/ScoopInstaller/Scoop/issues/5937)) - **scoop-cache:** Fix regression in 36026f18 ([#5944](https://github.com/ScoopInstaller/Scoop/issues/5944)) - **core:** Fix "Invoke-ExternalCommand" quoting rules ([#5945](https://github.com/ScoopInstaller/Scoop/issues/5945)) diff --git a/lib/autoupdate.ps1 b/lib/autoupdate.ps1 index 705c767451..bd24e04015 100644 --- a/lib/autoupdate.ps1 +++ b/lib/autoupdate.ps1 @@ -365,7 +365,7 @@ function Update-ManifestProperty { } } elseif ($Manifest.$currentProperty -and $Manifest.autoupdate.$currentProperty) { # Update other property (global) - $autoupdateProperty = $Manifest.autoupdate.$currentProperty.PSObject.Copy() + $autoupdateProperty = $Manifest.autoupdate.$currentProperty $newValue = substitute $autoupdateProperty $Substitutions if (($autoupdateProperty.GetType().Name -eq 'Object[]') -and ($autoupdateProperty.Length -eq 1)) { # Make sure it's an array diff --git a/lib/core.ps1 b/lib/core.ps1 index bc182464fe..1e91212685 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -1250,8 +1250,8 @@ function Test-ScoopCoreOnHold() { } function substitute($entity, [Hashtable] $params, [Bool]$regexEscape = $false) { - $newentity = $entity - if ($null -ne $newentity) { + if ($null -ne $entity) { + $newentity = $entity.PSObject.Copy() switch ($entity.GetType().Name) { 'String' { $params.GetEnumerator() | ForEach-Object { @@ -1263,7 +1263,7 @@ function substitute($entity, [Hashtable] $params, [Bool]$regexEscape = $false) { } } 'Object[]' { - $newentity = $entity | ForEach-Object { ,(substitute $_ $params $regexEscape) } + $newentity = $entity | ForEach-Object { , (substitute $_ $params $regexEscape) } } 'PSCustomObject' { $newentity.PSObject.Properties | ForEach-Object { $_.Value = substitute $_.Value $params $regexEscape } From f6f46f6cf44f8296111bced9cab4565059c1b4f6 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Tue, 14 May 2024 16:38:05 +0800 Subject: [PATCH 16/33] fix(sqlite): Dispose all command to release database file handle (#5966) --- CHANGELOG.md | 2 +- lib/database.ps1 | 53 ++++++++++-------------------------------------- 2 files changed, 12 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53d07d6f51..e8c3322789 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Features -- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5949](https://github.com/ScoopInstaller/Scoop/issues/5949), [#5955](https://github.com/ScoopInstaller/Scoop/issues/5955)) +- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5949](https://github.com/ScoopInstaller/Scoop/issues/5949), [#5955](https://github.com/ScoopInstaller/Scoop/issues/5955), [#5966](https://github.com/ScoopInstaller/Scoop/issues/5966)) - **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929)) ### Bug Fixes diff --git a/lib/database.ps1 b/lib/database.ps1 index e15720d7da..6ca35ce1a4 100644 --- a/lib/database.ps1 +++ b/lib/database.ps1 @@ -40,47 +40,16 @@ function Get-SQLite { <# .SYNOPSIS - Close a SQLite database. + Open Scoop SQLite database. .DESCRIPTION - Close a SQLite database connection. -.PARAMETER InputObject - System.Data.SQLite.SQLiteConnection - The SQLite database connection to close. -.INPUTS - System.Data.SQLite.SQLiteConnection -.OUTPUTS - None -#> -function Close-ScoopDB { - [CmdletBinding()] - param ( - [Parameter(Mandatory, ValueFromPipeline)] - [System.Data.SQLite.SQLiteConnection] - $InputObject - ) - process { - $InputObject.Dispose() - } -} - -<# -.SYNOPSIS - Create a new SQLite database. -.DESCRIPTION - Create a new SQLite database connection and create the necessary tables. -.PARAMETER PassThru - System.Management.Automation.SwitchParameter - Return the SQLite database connection. + Open Scoop SQLite database connection and create the necessary tables if not exists. .INPUTS None .OUTPUTS - None - Default - System.Data.SQLite.SQLiteConnection The SQLite database connection if **PassThru** is used. #> -function New-ScoopDB ([switch]$PassThru) { +function Open-ScoopDB { # Load System.Data.SQLite if (!('System.Data.SQLite.SQLiteConnection' -as [Type])) { try { @@ -112,11 +81,7 @@ function New-ScoopDB ([switch]$PassThru) { )" $tableCommand.ExecuteNonQuery() | Out-Null $tableCommand.Dispose() - if ($PassThru) { - return $db - } else { - $db.Dispose() - } + return $db } <# @@ -141,7 +106,7 @@ function Set-ScoopDBItem { ) begin { - $db = New-ScoopDB -PassThru + $db = Open-ScoopDB $dbTrans = $db.BeginTransaction() # TODO Support [hashtable]$InputObject $colName = @($InputObject | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name) @@ -164,6 +129,8 @@ function Set-ScoopDBItem { $dbTrans.Rollback() throw $_ } finally { + $dbCommand.Dispose() + $dbTrans.Dispose() $db.Dispose() } } @@ -277,7 +244,7 @@ function Select-ScoopDBItem { ) begin { - $db = New-ScoopDB -PassThru + $db = Open-ScoopDB $dbAdapter = New-Object -TypeName System.Data.SQLite.SQLiteDataAdapter $result = New-Object System.Data.DataTable $dbQuery = "SELECT * FROM app WHERE $(($From -join ' LIKE @Pattern OR ') + ' LIKE @Pattern')" @@ -292,6 +259,7 @@ function Select-ScoopDBItem { [void]$dbAdapter.Fill($result) } end { + $dbAdapter.Dispose() $db.Dispose() return $result } @@ -332,7 +300,7 @@ function Get-ScoopDBItem { ) begin { - $db = New-ScoopDB -PassThru + $db = Open-ScoopDB $dbAdapter = New-Object -TypeName System.Data.SQLite.SQLiteDataAdapter $result = New-Object System.Data.DataTable $dbQuery = 'SELECT * FROM app WHERE name = @Name AND bucket = @Bucket' @@ -353,6 +321,7 @@ function Get-ScoopDBItem { [void]$dbAdapter.Fill($result) } end { + $dbAdapter.Dispose() $db.Dispose() return $result } From 2d50a02a321b7e95113234bddcecea6e913f6d83 Mon Sep 17 00:00:00 2001 From: "L. Yeung" Date: Tue, 14 May 2024 22:43:53 +0800 Subject: [PATCH 17/33] fix(scoop-download|install|update): Use consistent options (#5956) --- CHANGELOG.md | 1 + libexec/scoop-download.ps1 | 6 +++--- libexec/scoop-install.ps1 | 6 +++--- libexec/scoop-update.ps1 | 18 +++++++++--------- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8c3322789..44ae2f21b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - **autoupdate:** Copy `PSCustomObject`-type properties within `substitute()` to prevent reference changes ([#5934](https://github.com/ScoopInstaller/Scoop/issues/5934), [#5962](https://github.com/ScoopInstaller/Scoop/issues/5962)) - **system:** Fix argument passing to `Split-PathLikeEnvVar()` in deprecated `strip_path()` ([#5937](https://github.com/ScoopInstaller/Scoop/issues/5937)) - **scoop-cache:** Fix regression in 36026f18 ([#5944](https://github.com/ScoopInstaller/Scoop/issues/5944)) +- **scoop-download|install|update:** Use consistent options ([#5956](https://github.com/ScoopInstaller/Scoop/issues/5956)) - **core:** Fix "Invoke-ExternalCommand" quoting rules ([#5945](https://github.com/ScoopInstaller/Scoop/issues/5945)) - **scoop-info:** Fix download size estimating ([#5958](https://github.com/ScoopInstaller/Scoop/issues/5958)) diff --git a/libexec/scoop-download.ps1 b/libexec/scoop-download.ps1 index a4ba10d36e..1901527b09 100644 --- a/libexec/scoop-download.ps1 +++ b/libexec/scoop-download.ps1 @@ -15,7 +15,7 @@ # # Options: # -f, --force Force download (overwrite cache) -# -h, --no-hash-check Skip hash verification (use with caution!) +# -s, --skip-hash-check Skip hash verification (use with caution!) # -u, --no-update-scoop Don't update Scoop before downloading if it's outdated # -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it @@ -28,10 +28,10 @@ if (get_config USE_SQLITE_CACHE) { . "$PSScriptRoot\..\lib\database.ps1" } -$opt, $apps, $err = getopt $args 'fhua:' 'force', 'no-hash-check', 'no-update-scoop', 'arch=' +$opt, $apps, $err = getopt $args 'fsua:' 'force', 'skip-hash-check', 'no-update-scoop', 'arch=' if ($err) { error "scoop download: $err"; exit 1 } -$check_hash = !($opt.h -or $opt.'no-hash-check') +$check_hash = !($opt.s -or $opt.'skip-hash-check') $use_cache = !($opt.f -or $opt.force) $architecture = Get-DefaultArchitecture try { diff --git a/libexec/scoop-install.ps1 b/libexec/scoop-install.ps1 index 1605efd2ac..8d12a9fee9 100644 --- a/libexec/scoop-install.ps1 +++ b/libexec/scoop-install.ps1 @@ -17,8 +17,8 @@ # -g, --global Install the app globally # -i, --independent Don't install dependencies automatically # -k, --no-cache Don't use the download cache +# -s, --skip-hash-check Skip hash validation (use with caution!) # -u, --no-update-scoop Don't update Scoop before installing if it's outdated -# -s, --skip Skip hash validation (use with caution!) # -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it . "$PSScriptRoot\..\lib\getopt.ps1" @@ -36,11 +36,11 @@ if (get_config USE_SQLITE_CACHE) { . "$PSScriptRoot\..\lib\database.ps1" } -$opt, $apps, $err = getopt $args 'gikusa:' 'global', 'independent', 'no-cache', 'no-update-scoop', 'skip', 'arch=' +$opt, $apps, $err = getopt $args 'giksua:' 'global', 'independent', 'no-cache', 'skip-hash-check', 'no-update-scoop', 'arch=' if ($err) { "scoop install: $err"; exit 1 } $global = $opt.g -or $opt.global -$check_hash = !($opt.s -or $opt.skip) +$check_hash = !($opt.s -or $opt.'skip-hash-check') $independent = $opt.i -or $opt.independent $use_cache = !($opt.k -or $opt.'no-cache') $architecture = Get-DefaultArchitecture diff --git a/libexec/scoop-update.ps1 b/libexec/scoop-update.ps1 index 79bd706a65..92326c4948 100644 --- a/libexec/scoop-update.ps1 +++ b/libexec/scoop-update.ps1 @@ -6,13 +6,13 @@ # You can use '*' in place of to update all apps. # # Options: -# -f, --force Force update even when there isn't a newer version -# -g, --global Update a globally installed app -# -i, --independent Don't install dependencies automatically -# -k, --no-cache Don't use the download cache -# -s, --skip Skip hash validation (use with caution!) -# -q, --quiet Hide extraneous messages -# -a, --all Update all apps (alternative to '*') +# -f, --force Force update even when there isn't a newer version +# -g, --global Update a globally installed app +# -i, --independent Don't install dependencies automatically +# -k, --no-cache Don't use the download cache +# -s, --skip-hash-check Skip hash validation (use with caution!) +# -q, --quiet Hide extraneous messages +# -a, --all Update all apps (alternative to '*') . "$PSScriptRoot\..\lib\getopt.ps1" . "$PSScriptRoot\..\lib\json.ps1" # 'save_install_info' in 'manifest.ps1' (indirectly) @@ -28,11 +28,11 @@ if (get_config USE_SQLITE_CACHE) { . "$PSScriptRoot\..\lib\database.ps1" } -$opt, $apps, $err = getopt $args 'gfiksqa' 'global', 'force', 'independent', 'no-cache', 'skip', 'quiet', 'all' +$opt, $apps, $err = getopt $args 'gfiksqa' 'global', 'force', 'independent', 'no-cache', 'skip-hash-check', 'quiet', 'all' if ($err) { "scoop update: $err"; exit 1 } $global = $opt.g -or $opt.global $force = $opt.f -or $opt.force -$check_hash = !($opt.s -or $opt.skip) +$check_hash = !($opt.s -or $opt.'skip-hash-check') $use_cache = !($opt.k -or $opt.'no-cache') $quiet = $opt.q -or $opt.quiet $independent = $opt.i -or $opt.independent From 5ce70c41397c728e10974df3541d20d1119db0af Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Wed, 15 May 2024 17:07:37 +0800 Subject: [PATCH 18/33] fix(sqlite): Update cache after removing bucket or manifests (#5967) --- CHANGELOG.md | 2 +- lib/buckets.ps1 | 5 +++ lib/database.ps1 | 75 ++++++++++++++++++++++++++++++++++++---- libexec/scoop-update.ps1 | 40 ++++++++++++++++----- 4 files changed, 107 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44ae2f21b8..47301059dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Features -- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5949](https://github.com/ScoopInstaller/Scoop/issues/5949), [#5955](https://github.com/ScoopInstaller/Scoop/issues/5955), [#5966](https://github.com/ScoopInstaller/Scoop/issues/5966)) +- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5949](https://github.com/ScoopInstaller/Scoop/issues/5949), [#5955](https://github.com/ScoopInstaller/Scoop/issues/5955), [#5966](https://github.com/ScoopInstaller/Scoop/issues/5966), [#5967](https://github.com/ScoopInstaller/Scoop/issues/5967)) - **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929)) ### Bug Fixes diff --git a/lib/buckets.ps1 b/lib/buckets.ps1 index 2adac4f339..566cbd7119 100644 --- a/lib/buckets.ps1 +++ b/lib/buckets.ps1 @@ -172,6 +172,11 @@ function rm_bucket($name) { } Remove-Item $dir -Recurse -Force -ErrorAction Stop + if (get_config USE_SQLITE_CACHE) { + info 'Updating cache...' + Remove-ScoopDBItem -Bucket $name + } + success "The $name bucket was removed successfully." return 0 } diff --git a/lib/database.ps1 b/lib/database.ps1 index 6ca35ce1a4..ae45f46dc1 100644 --- a/lib/database.ps1 +++ b/lib/database.ps1 @@ -79,6 +79,7 @@ function Open-ScoopDB { suggest TEXT, PRIMARY KEY (name, version, bucket) )" + $tableCommand.CommandType = [System.Data.CommandType]::Text $tableCommand.ExecuteNonQuery() | Out-Null $tableCommand.Dispose() return $db @@ -88,7 +89,7 @@ function Open-ScoopDB { .SYNOPSIS Set Scoop database item(s). .DESCRIPTION - Insert or replace Scoop database item(s) into the database. + Insert or replace item(s) into the Scoop SQLite database. .PARAMETER InputObject System.Object[] The database item(s) to insert or replace. @@ -113,6 +114,7 @@ function Set-ScoopDBItem { $dbQuery = "INSERT OR REPLACE INTO app ($($colName -join ', ')) VALUES ($('@' + ($colName -join ', @')))" $dbCommand = $db.CreateCommand() $dbCommand.CommandText = $dbQuery + $dbCommand.CommandType = [System.Data.CommandType]::Text } process { foreach ($item in $InputObject) { @@ -161,7 +163,7 @@ function Set-ScoopDB { ) begin { - $list = [System.Collections.Generic.List[PSCustomObject]]::new() + $list = [System.Collections.Generic.List[psobject]]::new() $arch = Get-DefaultArchitecture } process { @@ -220,11 +222,14 @@ function Set-ScoopDB { .SYNOPSIS Select Scoop database item(s). .DESCRIPTION - Select Scoop database item(s) from the database. + Select item(s) from the Scoop SQLite database. The pattern is matched against the name, binaries, and shortcuts columns for apps. .PARAMETER Pattern System.String The pattern to search for. If is an empty string, all items will be returned. +.PARAMETER From + System.String[] + The fields to search from. .INPUTS System.String .OUTPUTS @@ -239,6 +244,7 @@ function Select-ScoopDBItem { [string] $Pattern, [Parameter(Mandatory, Position = 1)] + [ValidateNotNullOrEmpty()] [string[]] $From ) @@ -252,10 +258,10 @@ function Select-ScoopDBItem { $dbCommand = $db.CreateCommand() $dbCommand.CommandText = $dbQuery $dbCommand.CommandType = [System.Data.CommandType]::Text + $dbAdapter.SelectCommand = $dbCommand } process { $dbCommand.Parameters.AddWithValue('@Pattern', $(if ($Pattern -eq '') { '%' } else { '%' + $Pattern + '%' })) | Out-Null - $dbAdapter.SelectCommand = $dbCommand [void]$dbAdapter.Fill($result) } end { @@ -269,7 +275,7 @@ function Select-ScoopDBItem { .SYNOPSIS Get Scoop database item. .DESCRIPTION - Get Scoop database item from the database. + Get item from the Scoop SQLite database. .PARAMETER Name System.String The name of the item to get. @@ -312,12 +318,12 @@ function Get-ScoopDBItem { $dbCommand = $db.CreateCommand() $dbCommand.CommandText = $dbQuery $dbCommand.CommandType = [System.Data.CommandType]::Text + $dbAdapter.SelectCommand = $dbCommand } process { $dbCommand.Parameters.AddWithValue('@Name', $Name) | Out-Null $dbCommand.Parameters.AddWithValue('@Bucket', $Bucket) | Out-Null $dbCommand.Parameters.AddWithValue('@Version', $Version) | Out-Null - $dbAdapter.SelectCommand = $dbCommand [void]$dbAdapter.Fill($result) } end { @@ -326,3 +332,60 @@ function Get-ScoopDBItem { return $result } } + +<# +.SYNOPSIS + Remove Scoop database item(s). +.DESCRIPTION + Remove item(s) from the Scoop SQLite database. +.PARAMETER Name + System.String + The name of the item to remove. +.PARAMETER Bucket + System.String + The bucket of the item to remove. +.INPUTS + System.String +.OUTPUTS + None +#> +function Remove-ScoopDBItem { + [CmdletBinding()] + param ( + [Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [string] + $Name, + [Parameter(Mandatory, Position = 1, ValueFromPipelineByPropertyName)] + [string] + $Bucket + ) + + begin { + $db = Open-ScoopDB + $dbTrans = $db.BeginTransaction() + $dbQuery = 'DELETE FROM app WHERE bucket = @Bucket' + $dbCommand = $db.CreateCommand() + $dbCommand.CommandText = $dbQuery + $dbCommand.CommandType = [System.Data.CommandType]::Text + } + process { + $dbCommand.Parameters.AddWithValue('@Bucket', $Bucket) | Out-Null + if ($Name) { + $dbCommand.CommandText = $dbQuery + ' AND name = @Name' + $dbCommand.Parameters.AddWithValue('@Name', $Name) | Out-Null + } + $dbCommand.ExecuteNonQuery() | Out-Null + } + end { + try { + $dbTrans.Commit() + } catch { + $dbTrans.Rollback() + throw $_ + } finally { + $dbCommand.Dispose() + $dbTrans.Dispose() + $db.Dispose() + } + } +} diff --git a/libexec/scoop-update.ps1 b/libexec/scoop-update.ps1 index 92326c4948..a7f7786553 100644 --- a/libexec/scoop-update.ps1 +++ b/libexec/scoop-update.ps1 @@ -181,7 +181,8 @@ function Sync-Bucket { $buckets | Where-Object { !$_.valid } | ForEach-Object { Write-Host "'$($_.name)' is not a git repository. Skipped." } - $updatedFiles = [System.Collections.ArrayList]::Synchronized([System.Collections.ArrayList]::new()) + $updatedFiles = [System.Collections.Generic.SynchronizedCollection[string]]::new() + $removedFiles = [System.Collections.Generic.SynchronizedCollection[psobject]]::new() if ($PSVersionTable.PSVersion.Major -ge 7) { # Parallel parameter is available since PowerShell 7 $buckets | Where-Object { $_.valid } | ForEach-Object -ThrottleLimit 5 -Parallel { @@ -198,10 +199,21 @@ function Sync-Bucket { Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit } if (get_config USE_SQLITE_CACHE) { - Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-only', '--diff-filter=d', $previousCommit) | ForEach-Object { - $filePath = Join-Path $bucketLoc $_ + Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-status', $previousCommit) | ForEach-Object { + $status, $file = $_ -split '\s+', 2 + $filePath = Join-Path $bucketLoc $file if ($filePath -match "^$([regex]::Escape($innerBucketLoc)).*\.json$") { - [void]($using:updatedFiles).Add($filePath) + switch ($status) { + { $_ -in 'A', 'M', 'R' } { + [void]($using:updatedFiles).Add($filePath) + } + 'D' { + [void]($using:removedFiles).Add([pscustomobject]@{ + Name = ([System.IO.FileInfo]$file).BaseName + Bucket = $name + }) + } + } } } } @@ -218,18 +230,30 @@ function Sync-Bucket { Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit } if (get_config USE_SQLITE_CACHE) { - Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-only', '--diff-filter=d', $previousCommit) | ForEach-Object { - $filePath = Join-Path $bucketLoc $_ + Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-status', $previousCommit) | ForEach-Object { + $status, $file = $_ -split '\s+', 2 + $filePath = Join-Path $bucketLoc $file if ($filePath -match "^$([regex]::Escape($innerBucketLoc)).*\.json$") { - [void]($updatedFiles).Add($filePath) + switch ($status) { + { $_ -in 'A', 'M', 'R' } { + [void]($updatedFiles).Add($filePath) + } + 'D' { + [void]($removedFiles).Add([pscustomobject]@{ + Name = ([System.IO.FileInfo]$file).BaseName + Bucket = $name + }) + } + } } } } } } - if ((get_config USE_SQLITE_CACHE) -and ($updatedFiles.Count -gt 0)) { + if ((get_config USE_SQLITE_CACHE) -and ($updatedFiles.Count -gt 0 -or $removedFiles.Count -gt 0)) { info 'Updating cache...' Set-ScoopDB -Path $updatedFiles + $removedFiles | Remove-ScoopDBItem } } From 2544745695c25e7a9a9254765539e35661a3f67e Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Wed, 15 May 2024 19:03:54 +0800 Subject: [PATCH 19/33] refactor(install): Replace 'run_(un)installer()' with 'Invoke-Installer()' (#5968) --- CHANGELOG.md | 1 + bin/uninstall.ps1 | 2 +- lib/core.ps1 | 4 - lib/install.ps1 | 154 +++++++++++++++++------------------- libexec/scoop-info.ps1 | 2 +- libexec/scoop-uninstall.ps1 | 2 +- libexec/scoop-update.ps1 | 2 +- 7 files changed, 78 insertions(+), 89 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47301059dd..8ad3462ad5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ ### Code Refactoring - **install:** Separate archive extraction from downloader ([#5951](https://github.com/ScoopInstaller/Scoop/issues/5951)) +- **install:** Replace 'run_(un)installer()' with 'Invoke-Installer()' ([#5968](https://github.com/ScoopInstaller/Scoop/issues/5968)) ## [v0.4.1](https://github.com/ScoopInstaller/Scoop/compare/v0.4.0...v0.4.1) - 2024-04-25 diff --git a/bin/uninstall.ps1 b/bin/uninstall.ps1 index 98b5c8d513..2e2dc36ea8 100644 --- a/bin/uninstall.ps1 +++ b/bin/uninstall.ps1 @@ -42,7 +42,7 @@ function do_uninstall($app, $global) { $architecture = $install.architecture Write-Output "Uninstalling '$app'" - run_uninstaller $manifest $architecture $dir + Invoke-Installer -Path $dir -Manifest $manifest -ProcessorArchitecture $architecture -Uninstall rm_shims $app $manifest $global $architecture # If a junction was used during install, that will have been used diff --git a/lib/core.ps1 b/lib/core.ps1 index 1e91212685..22bac33920 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -324,10 +324,6 @@ function Invoke-GitLog { # helper functions function coalesce($a, $b) { if($a) { return $a } $b } -function format($str, $hash) { - $hash.keys | ForEach-Object { set-variable $_ $hash[$_] } - $executionContext.invokeCommand.expandString($str) -} function is_admin { $admin = [security.principal.windowsbuiltinrole]::administrator $id = [security.principal.windowsidentity]::getcurrent() diff --git a/lib/install.ps1 b/lib/install.ps1 index f70b1bf9e0..466b562d33 100644 --- a/lib/install.ps1 +++ b/lib/install.ps1 @@ -51,9 +51,9 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru $fname = Invoke-ScoopDownload $app $version $manifest $bucket $architecture $dir $use_cache $check_hash Invoke-Extraction -Path $dir -Name $fname -Manifest $manifest -ProcessorArchitecture $architecture - Invoke-HookScript -HookType 'pre_install' -Manifest $manifest -Arch $architecture + Invoke-HookScript -HookType 'pre_install' -Manifest $manifest -ProcessorArchitecture $architecture - run_installer @($fname)[-1] $manifest $architecture $dir $global + Invoke-Installer -Path $dir -Manifest $manifest -ProcessorArchitecture $architecture -AppName $app -Global:$global ensure_install_dir_not_in_path $dir $global $dir = link_current $dir create_shims $manifest $dir $global $architecture @@ -66,7 +66,7 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru persist_data $manifest $original_dir $persist_dir persist_permission $manifest $global - Invoke-HookScript -HookType 'post_install' -Manifest $manifest -Arch $architecture + Invoke-HookScript -HookType 'post_install' -Manifest $manifest -ProcessorArchitecture $architecture # save info for uninstall save_installed_manifest $app $bucket $dir $url @@ -540,7 +540,7 @@ function Invoke-ScoopDownload ($app, $version, $manifest, $bucket, $architecture # we only want to show this warning once if (!$use_cache) { warn 'Cache is being ignored.' } - # can be multiple urls: if there are, then installer should go last to make 'installer.args' section work + # can be multiple urls: if there are, then installer should go first to make 'installer.args' section work $urls = @(script:url $manifest $architecture) # can be multiple cookies: they will be used for all HTTP requests. @@ -651,70 +651,84 @@ function check_hash($file, $hash, $app_name) { return $true, $null } -# for dealing with installers -function args($config, $dir, $global) { - if ($config) { return $config | ForEach-Object { (format $_ @{'dir' = $dir; 'global' = $global }) } } - @() -} - -function run_installer($fname, $manifest, $architecture, $dir, $global) { - $installer = installer $manifest $architecture - if ($installer.script) { - Write-Output 'Running installer script...' - Invoke-Command ([scriptblock]::Create($installer.script -join "`r`n")) - return - } - if ($installer) { - $prog = "$dir\$(coalesce $installer.file $fname)" - if (!(is_in_dir $dir $prog)) { - abort "Error in manifest: Installer $prog is outside the app directory." +function Invoke-Installer { + [CmdletBinding()] + param ( + [string] + $Path, + [psobject] + $Manifest, + [Alias('Arch', 'Architecture')] + [ValidateSet('32bit', '64bit', 'arm64')] + [string] + $ProcessorArchitecture, + [string] + $AppName, + [switch] + $Global, + [switch] + $Uninstall + ) + $type = if ($Uninstall) { 'uninstaller' } else { 'installer' } + $installer = arch_specific $type $Manifest $ProcessorArchitecture + if ($installer.file -or $installer.args) { + # Installer filename is either explicit defined ('installer.file') or file name in the first URL + $progName = "$Path\$(coalesce $installer.file (url_filename @(url $manifest $architecture)[0]))" + if (!(is_in_dir $Path $progName)) { + abort "Error in manifest: $((Get-Culture).TextInfo.ToTitleCase($type)) $progName is outside the app directory." + } elseif (!(Test-Path $progName)) { + abort "$((Get-Culture).TextInfo.ToTitleCase($type)) $progName is missing." } - $arg = @(args $installer.args $dir $global) - if ($prog.endswith('.ps1')) { - & $prog @arg + $substitutions = @{ + '$dir' = $Path + '$global' = $Global + '$version' = $Manifest.version + } + $fnArgs = substitute $installer.args $substitutions + if ($progName.EndsWith('.ps1')) { + & $progName @fnArgs } else { - $installed = Invoke-ExternalCommand $prog $arg -Activity 'Running installer...' - if (!$installed) { - abort "Installation aborted. You might need to run 'scoop uninstall $app' before trying again." + $status = Invoke-ExternalCommand $progName -ArgumentList $fnArgs -Activity "Running $type ..." + if (!$status) { + if ($Uninstall) { + abort 'Uninstallation aborted.' + } else { + abort "Installation aborted. You might need to run 'scoop uninstall $AppName' before trying again." + } } # Don't remove installer if "keep" flag is set to true - if (!($installer.keep -eq 'true')) { - Remove-Item $prog + if (!$installer.keep) { + Remove-Item $progName } } } + Invoke-HookScript -HookType $type -Manifest $Manifest -ProcessorArchitecture $ProcessorArchitecture } -function run_uninstaller($manifest, $architecture, $dir) { - $uninstaller = uninstaller $manifest $architecture - $version = $manifest.version - if ($uninstaller.script) { - Write-Output 'Running uninstaller script...' - Invoke-Command ([scriptblock]::Create($uninstaller.script -join "`r`n")) - return - } - - if ($uninstaller.file) { - $prog = "$dir\$($uninstaller.file)" - $arg = args $uninstaller.args - if (!(is_in_dir $dir $prog)) { - warn "Error in manifest: Installer $prog is outside the app directory, skipping." - $prog = $null - } elseif (!(Test-Path $prog)) { - warn "Uninstaller $prog is missing, skipping." - $prog = $null - } +function Invoke-HookScript { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [ValidateSet('installer', 'pre_install', 'post_install', 'uninstaller', 'pre_uninstall', 'post_uninstall')] + [String] $HookType, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [PSCustomObject] $Manifest, + [Parameter(Mandatory = $true)] + [Alias('Arch', 'Architecture')] + [ValidateSet('32bit', '64bit', 'arm64')] + [string] + $ProcessorArchitecture + ) - if ($prog) { - if ($prog.endswith('.ps1')) { - & $prog @arg - } else { - $uninstalled = Invoke-ExternalCommand $prog $arg -Activity 'Running uninstaller...' - if (!$uninstalled) { - abort 'Uninstallation aborted.' - } - } - } + $script = arch_specific $HookType $Manifest $ProcessorArchitecture + if ($HookType -in @('installer', 'uninstaller')) { + $script = $script.script + } + if ($script) { + Write-Host "Running $HookType script..." -NoNewline + Invoke-Command ([scriptblock]::Create($script -join "`r`n")) + Write-Host 'done.' -ForegroundColor Green } } @@ -884,7 +898,7 @@ function env_set($manifest, $dir, $global, $arch) { if ($env_set) { $env_set | Get-Member -Member NoteProperty | ForEach-Object { $name = $_.name - $val = format $env_set.$($_.name) @{ 'dir' = $dir } + $val = substitute $env_set.$($_.name) @{ '$dir' = $dir } Set-EnvVar -Name $name -Value $val -Global:$global Set-Content env:\$name $val } @@ -901,28 +915,6 @@ function env_rm($manifest, $global, $arch) { } } -function Invoke-HookScript { - [CmdletBinding()] - param( - [Parameter(Mandatory = $true)] - [ValidateSet('pre_install', 'post_install', - 'pre_uninstall', 'post_uninstall')] - [String] $HookType, - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [PSCustomObject] $Manifest, - [Parameter(Mandatory = $true)] - [ValidateSet('32bit', '64bit', 'arm64')] - [String] $Arch - ) - - $script = arch_specific $HookType $Manifest $Arch - if ($script) { - Write-Output "Running $HookType script..." - Invoke-Command ([scriptblock]::Create($script -join "`r`n")) - } -} - function show_notes($manifest, $dir, $original_dir, $persist_dir) { if ($manifest.notes) { Write-Output 'Notes' diff --git a/libexec/scoop-info.ps1 b/libexec/scoop-info.ps1 index b7cf7cdeb0..3907b9f71a 100644 --- a/libexec/scoop-info.ps1 +++ b/libexec/scoop-info.ps1 @@ -210,7 +210,7 @@ $env_set = arch_specific 'env_set' $manifest $install.architecture if ($env_set) { $env_vars = @() $env_set | Get-Member -member noteproperty | ForEach-Object { - $env_vars += "$($_.name) = $(format $env_set.$($_.name) @{ "dir" = $dir })" + $env_vars += "$($_.name) = $(substitute $env_set.$($_.name) @{ '$dir' = $dir })" } $item.Environment = $env_vars -join "`n" } diff --git a/libexec/scoop-uninstall.ps1 b/libexec/scoop-uninstall.ps1 index 7931158eec..5ad606a461 100644 --- a/libexec/scoop-uninstall.ps1 +++ b/libexec/scoop-uninstall.ps1 @@ -74,7 +74,7 @@ if (!$apps) { exit 0 } continue } - run_uninstaller $manifest $architecture $dir + Invoke-Installer -Path $dir -Manifest $manifest -ProcessorArchitecture $architecture -Uninstall rm_shims $app $manifest $global $architecture rm_startmenu_shortcuts $manifest $global $architecture diff --git a/libexec/scoop-update.ps1 b/libexec/scoop-update.ps1 index a7f7786553..507dd0583f 100644 --- a/libexec/scoop-update.ps1 +++ b/libexec/scoop-update.ps1 @@ -340,7 +340,7 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c Invoke-HookScript -HookType 'pre_uninstall' -Manifest $old_manifest -Arch $architecture Write-Host "Uninstalling '$app' ($old_version)" - run_uninstaller $old_manifest $architecture $dir + Invoke-Installer -Path $dir -Manifest $old_manifest -ProcessorArchitecture $architecture -Uninstall rm_shims $app $old_manifest $global $architecture # If a junction was used during install, that will have been used From 8ea37387aeba27d0ce4cb0fbbc41506d193562b1 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Wed, 15 May 2024 19:25:35 +0800 Subject: [PATCH 20/33] fix(install): Add back arg `$Name` in `Invoke-Installer()` (#5971) --- CHANGELOG.md | 2 +- lib/install.ps1 | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ad3462ad5..2788949974 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ ### Code Refactoring - **install:** Separate archive extraction from downloader ([#5951](https://github.com/ScoopInstaller/Scoop/issues/5951)) -- **install:** Replace 'run_(un)installer()' with 'Invoke-Installer()' ([#5968](https://github.com/ScoopInstaller/Scoop/issues/5968)) +- **install:** Replace 'run_(un)installer()' with 'Invoke-Installer()' ([#5968](https://github.com/ScoopInstaller/Scoop/issues/5968), [#5971](https://github.com/ScoopInstaller/Scoop/issues/5971)) ## [v0.4.1](https://github.com/ScoopInstaller/Scoop/compare/v0.4.0...v0.4.1) - 2024-04-25 diff --git a/lib/install.ps1 b/lib/install.ps1 index 466b562d33..cbe2b2f8b2 100644 --- a/lib/install.ps1 +++ b/lib/install.ps1 @@ -53,7 +53,7 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru Invoke-Extraction -Path $dir -Name $fname -Manifest $manifest -ProcessorArchitecture $architecture Invoke-HookScript -HookType 'pre_install' -Manifest $manifest -ProcessorArchitecture $architecture - Invoke-Installer -Path $dir -Manifest $manifest -ProcessorArchitecture $architecture -AppName $app -Global:$global + Invoke-Installer -Path $dir -Name $fname -Manifest $manifest -ProcessorArchitecture $architecture -AppName $app -Global:$global ensure_install_dir_not_in_path $dir $global $dir = link_current $dir create_shims $manifest $dir $global $architecture @@ -656,6 +656,8 @@ function Invoke-Installer { param ( [string] $Path, + [string[]] + $Name, [psobject] $Manifest, [Alias('Arch', 'Architecture')] @@ -673,7 +675,10 @@ function Invoke-Installer { $installer = arch_specific $type $Manifest $ProcessorArchitecture if ($installer.file -or $installer.args) { # Installer filename is either explicit defined ('installer.file') or file name in the first URL - $progName = "$Path\$(coalesce $installer.file (url_filename @(url $manifest $architecture)[0]))" + if (!$Name) { + $Name = url_filename @(url $manifest $architecture) + } + $progName = "$Path\$(coalesce $installer.file $Name[0])" if (!(is_in_dir $Path $progName)) { abort "Error in manifest: $((Get-Culture).TextInfo.ToTitleCase($type)) $progName is outside the app directory." } elseif (!(Test-Path $progName)) { From 5c896e901fafbe371b39673129120e3c88496a39 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Fri, 17 May 2024 09:56:14 +0800 Subject: [PATCH 21/33] refactor(decompress): Use 7zip to extract Zstd archive (#5973) --- CHANGELOG.md | 1 + lib/core.ps1 | 5 ++-- lib/decompress.ps1 | 40 ++----------------------- lib/depends.ps1 | 19 ++---------- test/Scoop-Decompress.Tests.ps1 | 37 +---------------------- test/Scoop-Depends.Tests.ps1 | 6 ---- test/bin/test.ps1 | 13 -------- test/fixtures/decompress/TestCases.zip | Bin 532697 -> 532327 bytes 8 files changed, 9 insertions(+), 112 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2788949974..c77dbacff7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ ### Code Refactoring +- **decompress:** Use 7zip to extract Zstd archive ([#5973](https://github.com/ScoopInstaller/Scoop/issues/5973)) - **install:** Separate archive extraction from downloader ([#5951](https://github.com/ScoopInstaller/Scoop/issues/5951)) - **install:** Replace 'run_(un)installer()' with 'Invoke-Installer()' ([#5968](https://github.com/ScoopInstaller/Scoop/issues/5968), [#5971](https://github.com/ScoopInstaller/Scoop/issues/5971)) diff --git a/lib/core.ps1 b/lib/core.ps1 index 22bac33920..e9311eb9cf 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -498,7 +498,7 @@ function Get-HelperPath { [OutputType([String])] param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] - [ValidateSet('Git', '7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2', 'Zstd')] + [ValidateSet('Git', '7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2')] [String] $Helper ) @@ -525,7 +525,6 @@ function Get-HelperPath { } } 'Aria2' { $HelperPath = Get-AppFilePath 'aria2' 'aria2c.exe' } - 'Zstd' { $HelperPath = Get-AppFilePath 'zstd' 'zstd.exe' } } return $HelperPath @@ -572,7 +571,7 @@ function Test-HelperInstalled { [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] - [ValidateSet('7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2', 'Zstd')] + [ValidateSet('7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2')] [String] $Helper ) diff --git a/lib/decompress.ps1 b/lib/decompress.ps1 index 3553621ced..968f6196f0 100644 --- a/lib/decompress.ps1 +++ b/lib/decompress.ps1 @@ -45,13 +45,7 @@ function Invoke-Extraction { } continue } - { Test-ZstdRequirement -Uri $_ } { - # Check Zstd first - $extractFn = 'Expand-ZstdArchive' - continue - } { Test-7zipRequirement -Uri $_ } { - # Then check 7zip $extractFn = 'Expand-7zipArchive' continue } @@ -164,37 +158,9 @@ function Expand-ZstdArchive { [Switch] $Removal ) - $ZstdPath = Get-HelperPath -Helper Zstd - $LogPath = Join-Path (Split-Path $Path) 'zstd.log' - $DestinationPath = $DestinationPath.TrimEnd('\') - ensure $DestinationPath | Out-Null - $ArgList = @('-d', $Path, '--output-dir-flat', $DestinationPath, '-f', '-v') - - if ($Switches) { - $ArgList += (-split $Switches) - } - if ($Removal) { - # Remove original archive file - $ArgList += '--rm' - } - $Status = Invoke-ExternalCommand $ZstdPath $ArgList -LogPath $LogPath - if (!$Status) { - abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $LogPath)`n$(new_issue_msg $app $bucket 'decompress error')" - } - $IsTar = (strip_ext $Path) -match '\.tar$' - if ($IsTar) { - # Check for tar - $TarFile = Join-Path $DestinationPath (strip_ext (fname $Path)) - Expand-7zipArchive -Path $TarFile -DestinationPath $DestinationPath -ExtractDir $ExtractDir -Removal - } - if (!$IsTar -and $ExtractDir) { - movedir (Join-Path $DestinationPath $ExtractDir) $DestinationPath | Out-Null - # Remove temporary directory - Remove-Item "$DestinationPath\$($ExtractDir -replace '[\\/].*')" -Recurse -Force -ErrorAction Ignore - } - if (Test-Path $LogPath) { - Remove-Item $LogPath -Force - } + # TODO: Remove this function after 2024/12/31 + Show-DeprecatedWarning $MyInvocation 'Expand-7zipArchive' + Expand-7zipArchive -Path $Path -DestinationPath $DestinationPath -ExtractDir $ExtractDir -Switches $Switches -Removal:$Removal } function Expand-MsiArchive { diff --git a/lib/depends.ps1 b/lib/depends.ps1 index bd4ed19cf2..3a38ca2b23 100644 --- a/lib/depends.ps1 +++ b/lib/depends.ps1 @@ -118,11 +118,8 @@ function Get-InstallationHelper { if ($script -like '*Expand-DarkArchive *') { $helper += 'dark' } - if ((Test-ZstdRequirement -Uri $url) -or ($script -like '*Expand-ZstdArchive *')) { - $helper += 'zstd' - } if (!$All) { - '7zip', 'lessmsi', 'innounp', 'dark', 'zstd' | ForEach-Object { + '7zip', 'lessmsi', 'innounp', 'dark' | ForEach-Object { if (Test-HelperInstalled -Helper $_) { $helper = $helper -ne $_ } @@ -144,22 +141,10 @@ function Test-7zipRequirement { $Uri ) return ($Uri | Where-Object { - $_ -match '\.((gz)|(tar)|(t[abgpx]z2?)|(lzma)|(bz2?)|(7z)|(001)|(rar)|(iso)|(xz)|(lzh)|(nupkg))(\.[^\d.]+)?$' + $_ -match '\.(001|7z|bz(ip)?2?|gz|img|iso|lzma|lzh|nupkg|rar|tar|t[abgpx]z2?|t?zst|xz)(\.[^\d.]+)?$' }).Count -gt 0 } -function Test-ZstdRequirement { - [CmdletBinding()] - [OutputType([Boolean])] - param ( - [Parameter(Mandatory = $true)] - [AllowNull()] - [String[]] - $Uri - ) - return ($Uri | Where-Object { $_ -match '\.zst$' }).Count -gt 0 -} - function Test-LessmsiRequirement { [CmdletBinding()] [OutputType([Boolean])] diff --git a/test/Scoop-Decompress.Tests.ps1 b/test/Scoop-Decompress.Tests.ps1 index 86220af5cb..4ec31a4ece 100644 --- a/test/Scoop-Decompress.Tests.ps1 +++ b/test/Scoop-Decompress.Tests.ps1 @@ -25,7 +25,7 @@ Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' { } It 'Test cases should exist and hash should match' { $testcases | Should -Exist - (Get-FileHash -Path $testcases -Algorithm SHA256).Hash.ToLower() | Should -Be '23a23a63e89ff95f5ef27f0cacf08055c2779cf41932266d8f509c2e200b8b63' + (Get-FileHash -Path $testcases -Algorithm SHA256).Hash.ToLower() | Should -Be 'afb86b0552187b8d630ce25d02835fb809af81c584f07e54cb049fb74ca134b6' } It 'Test cases should be extracted correctly' { { Microsoft.PowerShell.Archive\Expand-Archive -Path $testcases -DestinationPath $working_dir } | Should -Not -Throw @@ -152,41 +152,6 @@ Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' { } } - Context 'zstd extraction' { - - BeforeAll { - if ($env:CI) { - Mock Get-AppFilePath { $env:SCOOP_ZSTD_PATH } -ParameterFilter { $Helper -eq 'zstd' } - Mock Get-AppFilePath { '7z.exe' } -ParameterFilter { $Helper -eq '7zip' } - } elseif (!(installed zstd)) { - scoop install zstd - } - - $test1 = "$working_dir\ZstdTest.zst" - $test2 = "$working_dir\ZstdTest.tar.zst" - } - - It 'extract normal compressed file' { - $to = test_extract 'Expand-ZstdArchive' $test1 - $to | Should -Exist - "$to\ZstdTest" | Should -Exist - (Get-ChildItem $to).Count | Should -Be 1 - } - - It 'extract nested compressed file' { - $to = test_extract 'Expand-ZstdArchive' $test2 - $to | Should -Exist - "$to\ZstdTest" | Should -Exist - (Get-ChildItem $to).Count | Should -Be 1 - } - - It 'works with "-Removal" switch ($removal param)' { - $test1 | Should -Exist - test_extract 'Expand-ZstdArchive' $test1 $true - $test1 | Should -Not -Exist - } - } - Context 'msi extraction' { BeforeAll { diff --git a/test/Scoop-Depends.Tests.ps1 b/test/Scoop-Depends.Tests.ps1 index d80d7d2652..79b868ef90 100644 --- a/test/Scoop-Depends.Tests.ps1 +++ b/test/Scoop-Depends.Tests.ps1 @@ -14,11 +14,6 @@ Describe 'Package Dependencies' -Tag 'Scoop' { Test-7zipRequirement -Uri 'test.bin' | Should -BeFalse Test-7zipRequirement -Uri @('test.xz', 'test.bin') | Should -BeTrue } - It 'Test Zstd requirement' { - Test-ZstdRequirement -Uri 'test.zst' | Should -BeTrue - Test-ZstdRequirement -Uri 'test.bin' | Should -BeFalse - Test-ZstdRequirement -Uri @('test.zst', 'test.bin') | Should -BeTrue - } It 'Test lessmsi requirement' { Mock get_config { $true } Test-LessmsiRequirement -Uri 'test.msi' | Should -BeTrue @@ -27,7 +22,6 @@ Describe 'Package Dependencies' -Tag 'Scoop' { } It 'Allow $Uri be $null' { Test-7zipRequirement -Uri $null | Should -BeFalse - Test-ZstdRequirement -Uri $null | Should -BeFalse Test-LessmsiRequirement -Uri $null | Should -BeFalse } } diff --git a/test/bin/test.ps1 b/test/bin/test.ps1 index ffb35351a7..a25940ccb9 100644 --- a/test/bin/test.ps1 +++ b/test/bin/test.ps1 @@ -70,19 +70,6 @@ if ($env:CI -eq $true) { Invoke-WebRequest -Uri $source -OutFile $destination & 7z.exe x "$env:SCOOP_HELPERS_PATH\innounp.rar" -o"$env:SCOOP_HELPERS_PATH\innounp" -y | Out-Null } - - # Only download zstd for AppVeyor, GitHub Actions has zstd installed by default - if ($env:BHBuildSystem -eq 'AppVeyor') { - $env:SCOOP_ZSTD_PATH = "$env:SCOOP_HELPERS_PATH\zstd\zstd.exe" - if (!(Test-Path $env:SCOOP_ZSTD_PATH)) { - $source = 'https://github.com/facebook/zstd/releases/download/v1.5.1/zstd-v1.5.1-win32.zip' - $destination = "$env:SCOOP_HELPERS_PATH\zstd.zip" - Invoke-WebRequest -Uri $source -OutFile $destination - & 7z.exe x "$env:SCOOP_HELPERS_PATH\zstd.zip" -o"$env:SCOOP_HELPERS_PATH\zstd" -y | Out-Null - } - } else { - $env:SCOOP_ZSTD_PATH = (Get-Command zstd.exe).Path - } } } diff --git a/test/fixtures/decompress/TestCases.zip b/test/fixtures/decompress/TestCases.zip index d558faac39f56ea74413651db246885badf93995..d2cd98c0f2340427f32cdbc031b6fcfd91f073d9 100644 GIT binary patch delta 78 zcmcb4P~rJLg@zW!7N!>F7M2#)7Pc+yF|(%kD{x3{*O<+|v}Jm)0*9Jf#4L6OzhKYc ekksN5z0``-0B=?{1|Z-E!c0~Mh8L0?Kpp@lOBM+L delta 442 zcmaF9PvPc4g@zW!7N!>F7M2#)7Pc+yF|+Eq7(jq!$=u-fiaUD(fou>K0OF|Pl9Z6t z;u5`*#3H?_;u4Lm`hOWZT$vc2Fu|l43=9nn%uGxe3>XZJj13J9&CQL?jm%BV7!(W` zN{fLS84L^4aW_-zDel|p#k!>9dgSADYa)388i!sFh?ThCIhrc=_$O*I; zgn5wdhuW>cz{tP=(lcFFof~n9%%gjgH2J2 t%{LgYrA`-==P*#?n$6DO7wj1f4&~H})BtZ*HjsIOK&T01&645(@&J&&Z}$KI From c9048ad9781916d7f00d203417ed7177820479ec Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Mon, 20 May 2024 20:57:29 +0800 Subject: [PATCH 22/33] fix(sqlite): Fix generic class error in PS5 (#5981) --- CHANGELOG.md | 2 +- libexec/scoop-update.ps1 | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c77dbacff7..539f1f06aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Features -- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5949](https://github.com/ScoopInstaller/Scoop/issues/5949), [#5955](https://github.com/ScoopInstaller/Scoop/issues/5955), [#5966](https://github.com/ScoopInstaller/Scoop/issues/5966), [#5967](https://github.com/ScoopInstaller/Scoop/issues/5967)) +- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5949](https://github.com/ScoopInstaller/Scoop/issues/5949), [#5955](https://github.com/ScoopInstaller/Scoop/issues/5955), [#5966](https://github.com/ScoopInstaller/Scoop/issues/5966), [#5967](https://github.com/ScoopInstaller/Scoop/issues/5967), [#5981](https://github.com/ScoopInstaller/Scoop/issues/5981)) - **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929)) ### Bug Fixes diff --git a/libexec/scoop-update.ps1 b/libexec/scoop-update.ps1 index 507dd0583f..bd825731c3 100644 --- a/libexec/scoop-update.ps1 +++ b/libexec/scoop-update.ps1 @@ -73,7 +73,7 @@ function Sync-Scoop { # check for git if (!(Test-GitAvailable)) { abort "Scoop uses Git to update itself. Run 'scoop install git' and try again." } - Write-Host "Updating Scoop..." + Write-Host 'Updating Scoop...' $currentdir = versiondir 'scoop' 'current' if (!(Test-Path "$currentdir\.git")) { $newdir = "$currentdir\..\new" @@ -181,8 +181,8 @@ function Sync-Bucket { $buckets | Where-Object { !$_.valid } | ForEach-Object { Write-Host "'$($_.name)' is not a git repository. Skipped." } - $updatedFiles = [System.Collections.Generic.SynchronizedCollection[string]]::new() - $removedFiles = [System.Collections.Generic.SynchronizedCollection[psobject]]::new() + $updatedFiles = [System.Collections.ArrayList]::Synchronized([System.Collections.ArrayList]::new()) + $removedFiles = [System.Collections.ArrayList]::Synchronized([System.Collections.ArrayList]::new()) if ($PSVersionTable.PSVersion.Major -ge 7) { # Parallel parameter is available since PowerShell 7 $buckets | Where-Object { $_.valid } | ForEach-Object -ThrottleLimit 5 -Parallel { From 700a2f4f5e9c6f738a5da91c8d2fa311df615d19 Mon Sep 17 00:00:00 2001 From: insertokername <111150085+insertokername@users.noreply.github.com> Date: Thu, 23 May 2024 18:05:38 +0300 Subject: [PATCH 23/33] feat(install): Added the ability to specify @ version when installing from url/file location (#5988) --- CHANGELOG.md | 1 + lib/core.ps1 | 2 +- libexec/scoop-install.ps1 | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 539f1f06aa..001ac5798e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5949](https://github.com/ScoopInstaller/Scoop/issues/5949), [#5955](https://github.com/ScoopInstaller/Scoop/issues/5955), [#5966](https://github.com/ScoopInstaller/Scoop/issues/5966), [#5967](https://github.com/ScoopInstaller/Scoop/issues/5967), [#5981](https://github.com/ScoopInstaller/Scoop/issues/5981)) - **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929)) +- **install:** Added the ability to install specific version of app from URL/file link ([#5988](https://github.com/ScoopInstaller/Scoop/issues/5988)) ### Bug Fixes diff --git a/lib/core.ps1 b/lib/core.ps1 index e9311eb9cf..2ecde7ae5a 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -1192,7 +1192,7 @@ function applist($apps, $global) { } function parse_app([string]$app) { - if ($app -match '^(?:(?[a-zA-Z0-9-_.]+)/)?(?.*\.json$|[a-zA-Z0-9-_.]+)(?:@(?.*))?$') { + if ($app -match '^(?:(?[a-zA-Z0-9-_.]+)/)?(?.*\.json|[a-zA-Z0-9-_.]+)(?:@(?.*))?$') { return $Matches['app'], $Matches['bucket'], $Matches['version'] } else { return $app, $null, $null diff --git a/libexec/scoop-install.ps1 b/libexec/scoop-install.ps1 index 8d12a9fee9..173bcd868f 100644 --- a/libexec/scoop-install.ps1 +++ b/libexec/scoop-install.ps1 @@ -10,9 +10,15 @@ # To install an app from a manifest at a URL: # scoop install https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/runat.json # +# To install a different version of the app from a URL: +# scoop install https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/neovim.json@0.9.0 +# # To install an app from a manifest on your computer # scoop install \path\to\app.json # +# To install an app from a manifest on your computer +# scoop install \path\to\app.json@version +# # Options: # -g, --global Install the app globally # -i, --independent Don't install dependencies automatically From fa1b42bf285c2615699ca6f6c962d7eddb3833e5 Mon Sep 17 00:00:00 2001 From: Chawye Hsu Date: Sun, 26 May 2024 14:30:47 +0800 Subject: [PATCH 24/33] fix(checkver): Correct variable 'regex' to 'regexp' (#5993) --- CHANGELOG.md | 1 + bin/checkver.ps1 | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 001ac5798e..373b875059 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - **scoop-download|install|update:** Use consistent options ([#5956](https://github.com/ScoopInstaller/Scoop/issues/5956)) - **core:** Fix "Invoke-ExternalCommand" quoting rules ([#5945](https://github.com/ScoopInstaller/Scoop/issues/5945)) - **scoop-info:** Fix download size estimating ([#5958](https://github.com/ScoopInstaller/Scoop/issues/5958)) +- **checkver:** Correct variable 'regex' to 'regexp' ([#5993](https://github.com/ScoopInstaller/Scoop/issues/5993)) ### Code Refactoring diff --git a/bin/checkver.ps1 b/bin/checkver.ps1 index 07d954c671..48691c872c 100644 --- a/bin/checkver.ps1 +++ b/bin/checkver.ps1 @@ -275,7 +275,7 @@ while ($in_progress -gt 0) { $ver = $Version if (!$ver) { - if (!$regex -and $replace) { + if (!$regexp -and $replace) { next "'replace' requires 're' or 'regex'" continue } @@ -300,7 +300,7 @@ while ($in_progress -gt 0) { if ($jsonpath) { # Return only a single value if regex is absent - $noregex = [String]::IsNullOrEmpty($regex) + $noregex = [String]::IsNullOrEmpty($regexp) # If reverse is ON and regex is ON, # Then reverse would have no effect because regex handles reverse # on its own @@ -348,19 +348,19 @@ while ($in_progress -gt 0) { } if ($regexp) { - $regex = New-Object System.Text.RegularExpressions.Regex($regexp) + $re = New-Object System.Text.RegularExpressions.Regex($regexp) if ($reverse) { - $match = $regex.Matches($page) | Select-Object -Last 1 + $match = $re.Matches($page) | Select-Object -Last 1 } else { - $match = $regex.Matches($page) | Select-Object -First 1 + $match = $re.Matches($page) | Select-Object -First 1 } if ($match -and $match.Success) { $matchesHashtable = @{} - $regex.GetGroupNames() | ForEach-Object { $matchesHashtable.Add($_, $match.Groups[$_].Value) } + $re.GetGroupNames() | ForEach-Object { $matchesHashtable.Add($_, $match.Groups[$_].Value) } $ver = $matchesHashtable['1'] if ($replace) { - $ver = $regex.Replace($match.Value, $replace) + $ver = $re.Replace($match.Value, $replace) } if (!$ver) { $ver = $matchesHashtable['version'] From dec4232754e10b5c9e0126ed500377c13a5b6394 Mon Sep 17 00:00:00 2001 From: "L. Yeung" Date: Sun, 26 May 2024 15:36:06 +0800 Subject: [PATCH 25/33] fix(decompress): Match `extract_dir`/`extract_to` and archives (#5983) --- CHANGELOG.md | 1 + lib/decompress.ps1 | 16 +++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 373b875059..0262ceac1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - **scoop-download|install|update:** Use consistent options ([#5956](https://github.com/ScoopInstaller/Scoop/issues/5956)) - **core:** Fix "Invoke-ExternalCommand" quoting rules ([#5945](https://github.com/ScoopInstaller/Scoop/issues/5945)) - **scoop-info:** Fix download size estimating ([#5958](https://github.com/ScoopInstaller/Scoop/issues/5958)) +- **decompress:** Match `extract_dir`/`extract_to` and archives ([#5983](https://github.com/ScoopInstaller/Scoop/issues/5983)) - **checkver:** Correct variable 'regex' to 'regexp' ([#5993](https://github.com/ScoopInstaller/Scoop/issues/5993)) ### Code Refactoring diff --git a/lib/decompress.ps1 b/lib/decompress.ps1 index 968f6196f0..9c3f5c6813 100644 --- a/lib/decompress.ps1 +++ b/lib/decompress.ps1 @@ -13,20 +13,16 @@ function Invoke-Extraction { $ProcessorArchitecture ) - # 'url', 'extract_dir' and 'extract_to' are paired $uri = @(url $Manifest $ProcessorArchitecture) + # 'extract_dir' and 'extract_to' are paired $extractDir = @(extract_dir $Manifest $ProcessorArchitecture) $extractTo = @(extract_to $Manifest $ProcessorArchitecture) + $extracted = 0 for ($i = 0; $i -lt $Name.Length; $i++) { - $fnArgs = @{ - Path = Join-Path $Path $Name[$i] - DestinationPath = Join-Path $Path $extractTo[$i] - ExtractDir = $extractDir[$i] - } # work out extraction method, if applicable $extractFn = $null - switch -regex ($fnArgs.Path) { + switch -regex ($Name[$i]) { '\.zip$' { if ((Test-HelperInstalled -Helper 7zip) -or ((get_config 7ZIPEXTRACT_USE_EXTERNAL) -and (Test-CommandAvailable 7z))) { $extractFn = 'Expand-7zipArchive' @@ -51,11 +47,17 @@ function Invoke-Extraction { } } if ($extractFn) { + $fnArgs = @{ + Path = Join-Path $Path $Name[$i] + DestinationPath = Join-Path $Path $extractTo[$extracted] + ExtractDir = $extractDir[$extracted] + } Write-Host 'Extracting ' -NoNewline Write-Host $(url_remote_filename $uri[$i]) -ForegroundColor Cyan -NoNewline Write-Host ' ... ' -NoNewline & $extractFn @fnArgs -Removal Write-Host 'done.' -ForegroundColor Green + $extracted++ } } } From d20819e1479483d604c572ee53a4c134cea5040f Mon Sep 17 00:00:00 2001 From: Matej Kafka <6414091+MatejKafka@users.noreply.github.com> Date: Fri, 7 Jun 2024 12:45:52 +0200 Subject: [PATCH 26/33] fix(core): Search for Git executable instead of any cmdlet (#5998) --- CHANGELOG.md | 1 + lib/core.ps1 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0262ceac1c..add88204c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - **scoop-info:** Fix download size estimating ([#5958](https://github.com/ScoopInstaller/Scoop/issues/5958)) - **decompress:** Match `extract_dir`/`extract_to` and archives ([#5983](https://github.com/ScoopInstaller/Scoop/issues/5983)) - **checkver:** Correct variable 'regex' to 'regexp' ([#5993](https://github.com/ScoopInstaller/Scoop/issues/5993)) +- **core:** Search for Git executable instead of any cmdlet ([#5998](https://github.com/ScoopInstaller/Scoop/pull/5998)) ### Code Refactoring diff --git a/lib/core.ps1 b/lib/core.ps1 index 2ecde7ae5a..4865cc8e94 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -512,7 +512,7 @@ function Get-HelperPath { if ($internalgit) { $HelperPath = $internalgit } else { - $HelperPath = (Get-Command git -ErrorAction Ignore).Source + $HelperPath = (Get-Command git -CommandType Application -ErrorAction Ignore).Source } } '7zip' { $HelperPath = Get-AppFilePath '7zip' '7z.exe' } From a9ca75c0cda0ab9f2b683f972dc4a2c65d1b1cd0 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Tue, 11 Jun 2024 11:03:35 +0800 Subject: [PATCH 27/33] fix(core): Use correct path in 'bash' (#6006) --- CHANGELOG.md | 3 ++- lib/core.ps1 | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index add88204c8..d88bca272c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ - **scoop-info:** Fix download size estimating ([#5958](https://github.com/ScoopInstaller/Scoop/issues/5958)) - **decompress:** Match `extract_dir`/`extract_to` and archives ([#5983](https://github.com/ScoopInstaller/Scoop/issues/5983)) - **checkver:** Correct variable 'regex' to 'regexp' ([#5993](https://github.com/ScoopInstaller/Scoop/issues/5993)) -- **core:** Search for Git executable instead of any cmdlet ([#5998](https://github.com/ScoopInstaller/Scoop/pull/5998)) +- **core:** Search for Git executable instead of any cmdlet ([#5998](https://github.com/ScoopInstaller/Scoop/issues/5998)) +- **core:** Use correct path in 'bash' ([#6006](https://github.com/ScoopInstaller/Scoop/issues/6006)) ### Code Refactoring diff --git a/lib/core.ps1 b/lib/core.ps1 index 4865cc8e94..2983c62d49 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -1041,11 +1041,11 @@ function shim($path, $global, $name, $arg) { @( "#!/bin/sh", "# $resolved_path", - "if [ `$(echo `$WSL_DISTRO_NAME) ]", + "if [ `$WSL_INTEROP ]", 'then', " cd `$(wslpath -u '$(Split-Path $resolved_path -Parent)')", 'else', - " cd `"$((Split-Path $resolved_path -Parent).Replace('\', '/'))`"", + " cd `$(cygpath -u '$(Split-Path $resolved_path -Parent)')", 'fi', "java.exe -jar `"$resolved_path`" $arg `"$@`"" ) -join "`n" | Out-UTF8File $shim -NoNewLine @@ -1058,7 +1058,7 @@ function shim($path, $global, $name, $arg) { warn_on_overwrite $shim $path @( - "#!/bin/sh", + '#!/bin/sh', "# $resolved_path", "python.exe `"$resolved_path`" $arg `"$@`"" ) -join "`n" | Out-UTF8File $shim -NoNewLine @@ -1066,14 +1066,22 @@ function shim($path, $global, $name, $arg) { warn_on_overwrite "$shim.cmd" $path @( "@rem $resolved_path", - "@bash `"$resolved_path`" $arg %*" + "@bash `"`$(wslpath -u '$resolved_path')`" $arg %* 2>nul", + '@if %errorlevel% neq 0 (', + " @bash `"`$(cygpath -u '$resolved_path')`" $arg %* 2>nul", + ')' ) -join "`r`n" | Out-UTF8File "$shim.cmd" warn_on_overwrite $shim $path @( - "#!/bin/sh", + '#!/bin/sh', "# $resolved_path", - "`"$resolved_path`" $arg `"$@`"" + "if [ `$WSL_INTEROP ]", + 'then', + " `"`$(wslpath -u '$resolved_path')`" $arg `"$@`"", + 'else', + " `"`$(cygpath -u '$resolved_path')`" $arg `"$@`"", + 'fi' ) -join "`n" | Out-UTF8File $shim -NoNewLine } } From 3a39ba048f473bceb1f3f572d9f3c003f881732d Mon Sep 17 00:00:00 2001 From: qiuyk <155824362+qwertyhjklxyz@users.noreply.github.com> Date: Fri, 14 Jun 2024 00:18:14 +0800 Subject: [PATCH 28/33] fix(core): Limit the number of commands to get (#6013) --- CHANGELOG.md | 1 + lib/core.ps1 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d88bca272c..f1c02a3b49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - **checkver:** Correct variable 'regex' to 'regexp' ([#5993](https://github.com/ScoopInstaller/Scoop/issues/5993)) - **core:** Search for Git executable instead of any cmdlet ([#5998](https://github.com/ScoopInstaller/Scoop/issues/5998)) - **core:** Use correct path in 'bash' ([#6006](https://github.com/ScoopInstaller/Scoop/issues/6006)) +- **core:** Limit the number of commands to get when search for git executable ([#6013](https://github.com/ScoopInstaller/Scoop/pull/6013)) ### Code Refactoring diff --git a/lib/core.ps1 b/lib/core.ps1 index 2983c62d49..14b46abed6 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -512,7 +512,7 @@ function Get-HelperPath { if ($internalgit) { $HelperPath = $internalgit } else { - $HelperPath = (Get-Command git -CommandType Application -ErrorAction Ignore).Source + $HelperPath = (Get-Command git -CommandType Application -TotalCount 1 -ErrorAction Ignore).Source } } '7zip' { $HelperPath = Get-AppFilePath '7zip' '7z.exe' } From 9239c26bbd8c1a79414e85d73431d5e8a3436c3f Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Mon, 24 Jun 2024 21:20:29 +0800 Subject: [PATCH 29/33] feat(decompress): Use `innounp-unicode` as default Inno Setup Unpacker (#6028) --- CHANGELOG.md | 1 + lib/core.ps1 | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1c02a3b49..303d3614d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5949](https://github.com/ScoopInstaller/Scoop/issues/5949), [#5955](https://github.com/ScoopInstaller/Scoop/issues/5955), [#5966](https://github.com/ScoopInstaller/Scoop/issues/5966), [#5967](https://github.com/ScoopInstaller/Scoop/issues/5967), [#5981](https://github.com/ScoopInstaller/Scoop/issues/5981)) - **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929)) - **install:** Added the ability to install specific version of app from URL/file link ([#5988](https://github.com/ScoopInstaller/Scoop/issues/5988)) +- **decompress:** Use innounp-unicode as default Inno Setup Unpacker ([#6028](https://github.com/ScoopInstaller/Scoop/issues/6028)) ### Bug Fixes diff --git a/lib/core.ps1 b/lib/core.ps1 index 14b46abed6..1bf2ebf520 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -517,7 +517,12 @@ function Get-HelperPath { } '7zip' { $HelperPath = Get-AppFilePath '7zip' '7z.exe' } 'Lessmsi' { $HelperPath = Get-AppFilePath 'lessmsi' 'lessmsi.exe' } - 'Innounp' { $HelperPath = Get-AppFilePath 'innounp' 'innounp.exe' } + 'Innounp' { + $HelperPath = Get-AppFilePath 'innounp-unicode' 'innounp.exe' + if ([String]::IsNullOrEmpty($HelperPath)) { + $HelperPath = Get-AppFilePath 'innounp' 'innounp.exe' + } + } 'Dark' { $HelperPath = Get-AppFilePath 'wixtoolset' 'wix.exe' if ([String]::IsNullOrEmpty($HelperPath)) { From d8b3cc8a6cd88fa46995160deb6fd5f888cb27d8 Mon Sep 17 00:00:00 2001 From: "L. Yeung" Date: Tue, 25 Jun 2024 10:40:56 +0800 Subject: [PATCH 30/33] fix(checkver): Correct error messages (#6024) --- CHANGELOG.md | 1 + bin/checkver.ps1 | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 303d3614d6..6c71431a8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - **core:** Search for Git executable instead of any cmdlet ([#5998](https://github.com/ScoopInstaller/Scoop/issues/5998)) - **core:** Use correct path in 'bash' ([#6006](https://github.com/ScoopInstaller/Scoop/issues/6006)) - **core:** Limit the number of commands to get when search for git executable ([#6013](https://github.com/ScoopInstaller/Scoop/pull/6013)) +- **checkver:** Correct error messages ([#6024](https://github.com/ScoopInstaller/Scoop/issues/6024)) ### Code Refactoring diff --git a/bin/checkver.ps1 b/bin/checkver.ps1 index 48691c872c..57010c9992 100644 --- a/bin/checkver.ps1 +++ b/bin/checkver.ps1 @@ -294,8 +294,10 @@ while ($in_progress -gt 0) { } $page = (New-Object System.IO.StreamReader($ms, (Get-Encoding $wc))).ReadToEnd() } + $source = $url if ($script) { $page = Invoke-Command ([scriptblock]::Create($script -join "`r`n")) + $source = 'the output of script' } if ($jsonpath) { @@ -310,7 +312,7 @@ while ($in_progress -gt 0) { $ver = json_path_legacy $page $jsonpath } if (!$ver) { - next "couldn't find '$jsonpath' in $url" + next "couldn't find '$jsonpath' in $source" continue } } @@ -332,7 +334,7 @@ while ($in_progress -gt 0) { # Getting version from XML, using XPath $ver = $xml.SelectSingleNode($xpath, $nsmgr).'#text' if (!$ver) { - next "couldn't find '$($xpath -replace 'ns:', '')' in $url" + next "couldn't find '$($xpath -replace 'ns:', '')' in $source" continue } } @@ -366,13 +368,13 @@ while ($in_progress -gt 0) { $ver = $matchesHashtable['version'] } } else { - next "couldn't match '$regexp' in $url" + next "couldn't match '$regexp' in $source" continue } } if (!$ver) { - next "couldn't find new version in $url" + next "couldn't find new version in $source" continue } } From 93359a43a1122bce171b28a2542a1c47aad9ac4e Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Wed, 26 Jun 2024 18:34:38 +0800 Subject: [PATCH 31/33] fix(shim): Restore original path for JAR cmd (#6030) --- CHANGELOG.md | 1 + lib/core.ps1 | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c71431a8f..f357f06a11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - **core:** Use correct path in 'bash' ([#6006](https://github.com/ScoopInstaller/Scoop/issues/6006)) - **core:** Limit the number of commands to get when search for git executable ([#6013](https://github.com/ScoopInstaller/Scoop/pull/6013)) - **checkver:** Correct error messages ([#6024](https://github.com/ScoopInstaller/Scoop/issues/6024)) +- **shim:** Restore original path for JAR cmd ([#6030](https://github.com/ScoopInstaller/Scoop/issues/6030)) ### Code Refactoring diff --git a/lib/core.ps1 b/lib/core.ps1 index 1bf2ebf520..193a8c0020 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -1038,8 +1038,9 @@ function shim($path, $global, $name, $arg) { warn_on_overwrite "$shim.cmd" $path @( "@rem $resolved_path", - "@cd /d $(Split-Path $resolved_path -Parent)" - "@java -jar `"$resolved_path`" $arg %*" + "@pushd $(Split-Path $resolved_path -Parent)", + "@java -jar `"$resolved_path`" $arg %*", + "@popd" ) -join "`r`n" | Out-UTF8File "$shim.cmd" warn_on_overwrite $shim $path From 7e0a2a28c6a02b565af63c61da46f82c2cac8584 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Sun, 30 Jun 2024 18:09:59 +0800 Subject: [PATCH 32/33] Update chglog --- CHANGELOG.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8aec12db5a..743f9bea71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,23 +3,22 @@ ### Features - **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5949](https://github.com/ScoopInstaller/Scoop/issues/5949), [#5955](https://github.com/ScoopInstaller/Scoop/issues/5955), [#5966](https://github.com/ScoopInstaller/Scoop/issues/5966), [#5967](https://github.com/ScoopInstaller/Scoop/issues/5967), [#5981](https://github.com/ScoopInstaller/Scoop/issues/5981)) -- **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929)) -- **install:** Added the ability to install specific version of app from URL/file link ([#5988](https://github.com/ScoopInstaller/Scoop/issues/5988)) +- **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929), [#5944](https://github.com/ScoopInstaller/Scoop/issues/5944)) - **decompress:** Use innounp-unicode as default Inno Setup Unpacker ([#6028](https://github.com/ScoopInstaller/Scoop/issues/6028)) +- **install:** Added the ability to install specific version of app from URL/file link ([#5988](https://github.com/ScoopInstaller/Scoop/issues/5988)) ### Bug Fixes -- **json:** Serialize jsonpath return ([#5921](https://github.com/ScoopInstaller/Scoop/issues/5921)) -- **scoop-search:** Catch error of parsing invalid manifest ([#5930](https://github.com/ScoopInstaller/Scoop/issues/5930)) -- **scoop-cache:** Fix regression in 36026f18 ([#5944](https://github.com/ScoopInstaller/Scoop/issues/5944)) - **scoop-download|install|update:** Use consistent options ([#5956](https://github.com/ScoopInstaller/Scoop/issues/5956)) - **scoop-info:** Fix download size estimating ([#5958](https://github.com/ScoopInstaller/Scoop/issues/5958)) -- **decompress:** Match `extract_dir`/`extract_to` and archives ([#5983](https://github.com/ScoopInstaller/Scoop/issues/5983)) +- **scoop-search:** Catch error of parsing invalid manifest ([#5930](https://github.com/ScoopInstaller/Scoop/issues/5930)) - **checkver:** Correct variable 'regex' to 'regexp' ([#5993](https://github.com/ScoopInstaller/Scoop/issues/5993)) +- **checkver:** Correct error messages ([#6024](https://github.com/ScoopInstaller/Scoop/issues/6024)) - **core:** Search for Git executable instead of any cmdlet ([#5998](https://github.com/ScoopInstaller/Scoop/issues/5998)) - **core:** Use correct path in 'bash' ([#6006](https://github.com/ScoopInstaller/Scoop/issues/6006)) - **core:** Limit the number of commands to get when search for git executable ([#6013](https://github.com/ScoopInstaller/Scoop/pull/6013)) -- **checkver:** Correct error messages ([#6024](https://github.com/ScoopInstaller/Scoop/issues/6024)) +- **decompress:** Match `extract_dir`/`extract_to` and archives ([#5983](https://github.com/ScoopInstaller/Scoop/issues/5983)) +- **json:** Serialize jsonpath return ([#5921](https://github.com/ScoopInstaller/Scoop/issues/5921)) - **shim:** Restore original path for JAR cmd ([#6030](https://github.com/ScoopInstaller/Scoop/issues/6030)) ### Code Refactoring From ade7aa4a153b29b31c455b241729d6e5b27a6526 Mon Sep 17 00:00:00 2001 From: Hsiao-nan Cheung Date: Sun, 30 Jun 2024 18:19:19 +0800 Subject: [PATCH 33/33] (chore): Update changelog (#6036) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 743f9bea71..f1af1cac61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## [Unreleased](https://github.com/ScoopInstaller/Scoop/compare/master...develop) +## [v0.5.0](https://github.com/ScoopInstaller/Scoop/compare/v0.4.2...v0.5.0) - 2024-07-01 ### Features