From fa74f783b542dad95062fe1a35f880e85e8318b1 Mon Sep 17 00:00:00 2001 From: z-Fng <54583083+z-Fng@users.noreply.github.com> Date: Thu, 18 Dec 2025 09:50:18 +0100 Subject: [PATCH 1/6] feat(scoop-install|update): Update extraction tools ahead of installs/updates --- CHANGELOG.md | 1 + lib/core.ps1 | 2 + lib/depends.ps1 | 94 +++++++++++++++++++++++++ lib/install.ps1 | 2 +- lib/update.ps1 | 126 +++++++++++++++++++++++++++++++++ libexec/scoop-install.ps1 | 8 ++- libexec/scoop-update.ps1 | 144 ++++---------------------------------- 7 files changed, 244 insertions(+), 133 deletions(-) create mode 100644 lib/update.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 745b888b32..68f5b9f134 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - **install:** Add separator at the end of notes, highlight suggestions ([#6418](https://github.com/ScoopInstaller/Scoop/issues/6418)) - **download|scoop-download:** Add GitHub issue prompt when the default downloader fails ([#6539](https://github.com/ScoopInstaller/Scoop/issues/6539)) - **download|scoop-config:** Allow disabling automatic fallback to the default downloader when Aria2c download fails ([#6538](https://github.com/ScoopInstaller/Scoop/issues/6538)) +- **scoop-install|update:** Update extraction tools ahead of installs/updates ([#6572](https://github.com/ScoopInstaller/Scoop/issues/6572)) ### Bug Fixes diff --git a/lib/core.ps1 b/lib/core.ps1 index 71f025df54..8fe35323fc 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -474,12 +474,14 @@ function Get-HelperPath { '7zip' { $HelperPath = Get-AppFilePath '7zip' '7z.exe' } 'Lessmsi' { $HelperPath = Get-AppFilePath 'lessmsi' 'lessmsi.exe' } 'Innounp' { + # Changes to the extraction tool priority should be synced with the Get-OutdatedHelper function as well. $HelperPath = Get-AppFilePath 'innounp-unicode' 'innounp.exe' if ([String]::IsNullOrEmpty($HelperPath)) { $HelperPath = Get-AppFilePath 'innounp' 'innounp.exe' } } 'Dark' { + # Changes to the extraction tool priority should be synced with the Get-OutdatedHelper function as well $HelperPath = Get-AppFilePath 'wixtoolset' 'wix.exe' if ([String]::IsNullOrEmpty($HelperPath)) { $HelperPath = Get-AppFilePath 'dark' 'dark.exe' diff --git a/lib/depends.ps1 b/lib/depends.ps1 index 3a38ca2b23..623e714103 100644 --- a/lib/depends.ps1 +++ b/lib/depends.ps1 @@ -156,3 +156,97 @@ function Test-LessmsiRequirement { ) return ($Uri | Where-Object { $_ -match '\.msi$' }).Count -gt 0 } + +function Get-OutdatedHelper { + <# + .SYNOPSIS + Get outdated installation helpers + .PARAMETER App + App's name + .PARAMETER Global + Whether the app is globally installed + .OUTPUTS + [Object[]] + A list of concrete outdated helper apps, each represented as a PSCustomObject with 'App' and 'Global' properties + #> + [CmdletBinding()] + [OutputType([Object[]])] + param ( + [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] + [String] + $App, + [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] + [Boolean] + $Global + ) + begin { + $helpers = @() + } + + process { + $version = Select-CurrentVersion -AppName $App -Global:$Global + + $manifest = if ($version) { + installed_manifest $App $version $Global + } else { + Get-Manifest $App + } + $install = install_info $App $version $Global + $architecture = Format-ArchitectureString $install.architecture + + $helpers += Get-InstallationHelper $manifest $architecture -All | Where-Object { + (Test-HelperInstalled -Helper $_) -and ($helpers -notcontains $_) + } + } + + end { + $outdated = @() + + foreach ($helper in $helpers) { + # Get the concrete app name + $app = switch ($helper) { + '7zip' { '7zip' } + 'lessmsi' { 'lessmsi' } + 'innounp' { if (installed 'innounp-unicode') { 'innounp-unicode' } else { 'innounp' } } + 'dark' { if (installed 'wixtoolset') { 'wixtoolset' } else { 'dark' } } + default { $null } + } + + if (-not $app) { + continue + } + + $global = installed $app $true + $status = app_status $app $global + + if (-not ($status.installed -and $status.outdated)) { + continue + } + + info ("Outdated extraction tool '$app' detected: $($status.version) -> $($status.latest_version){0}." -f ('', ' (global)')[$global]) + + # Filter out outdated helpers that are held + if ($status.hold) { + warn "Skipping update of outdated extraction tool '$app' because it is held at version $($status.version)." + warn ("Outdated extraction tool may cause decompression errors. Please run 'scoop unhold $app{0}' to unhold it." -f ('', ' -g')[$global]) + continue + } + + # Filter out outdated helpers that are blocked by permission issues + if ((-not (is_admin)) -and $global) { + warn "Skipping update of outdated extraction tool '$app' because it is globally installed." + warn "Outdated extraction tool may cause decompression errors. Please run 'scoop update $app -g' to update it." + continue + } + + Write-Host ("$app`: $($status.version) -> $($status.latest_version){0}" -f ('', ' (global)')[$global]) -ForegroundColor Yellow + + $outdated += [PSCustomObject]@{ + App = $app + Global = $global + } + } + + return , $outdated + } +} diff --git a/lib/install.ps1 b/lib/install.ps1 index 78acdd252b..152ec69752 100644 --- a/lib/install.ps1 +++ b/lib/install.ps1 @@ -110,7 +110,7 @@ function Invoke-Installer { if ($installer.file -or $installer.args) { # Installer filename is either explicit defined ('installer.file') or file name in the first URL if (!$Name) { - $Name = url_filename @(url $manifest $architecture) + $Name = url_filename @(url $manifest $ProcessorArchitecture) } $progName = "$Path\$(coalesce $installer.file $Name[0])" if (!(is_in_dir $Path $progName)) { diff --git a/lib/update.ps1 b/lib/update.ps1 new file mode 100644 index 0000000000..5d8fe9937a --- /dev/null +++ b/lib/update.ps1 @@ -0,0 +1,126 @@ +function update($app, $global, $quiet = $false, $independent, $suggested, $use_cache = $true, $check_hash = $true) { + $old_version = Select-CurrentVersion -AppName $app -Global:$global + $old_manifest = installed_manifest $app $old_version $global + $install = install_info $app $old_version $global + + # re-use architecture, bucket and url from first install + $architecture = Format-ArchitectureString $install.architecture + $bucket = $install.bucket + if ($null -eq $bucket) { + $bucket = 'main' + } + $url = $install.url + + $manifest = manifest $app $bucket $url + $version = $manifest.version + $is_nightly = $version -eq 'nightly' + if ($is_nightly) { + $version = nightly_version $quiet + $check_hash = $false + } + + if (!$force -and ($old_version -eq $version)) { + if (!$quiet) { + warn "The latest version of '$app' ($version) is already installed." + } + return + } + if (!$version) { + # installed from a custom bucket/no longer supported + error "No manifest available for '$app'." + return + } + + Write-Host "Updating '$app' ($old_version -> $version)" + + #region Workaround for #2952 + if (test_running_process $app $global) { + Write-Host 'Running process detected, skip updating.' + return + } + #endregion Workaround for #2952 + + # 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' + if (Test-Aria2Enabled) { + Invoke-CachedAria2Download $app $version $manifest $architecture $cachedir $manifest.cookie $true $check_hash + } else { + $urls = script:url $manifest $architecture + + foreach ($url in $urls) { + Invoke-CachedDownload $app $version $url $null $manifest.cookie $true + + if ($check_hash) { + $manifest_hash = hash_for_url $manifest $url $architecture + $source = cache_path $app $version $url + $ok, $err = check_hash $source $manifest_hash $(show_app $app $bucket) + + if (!$ok) { + error $err + if (Test-Path $source) { + # rm cached file + 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') + } + } + } + } + # There is no need to check hash again while installing + $check_hash = $false + # endregion Workaround + + $dir = versiondir $app $old_version $global + $persist_dir = persistdir $app $global + + Invoke-HookScript -HookType 'pre_uninstall' -Manifest $old_manifest -Arch $architecture + + Write-Host "Uninstalling '$app' ($old_version)" + Invoke-Installer -Path $dir -Manifest $old_manifest -ProcessorArchitecture $architecture -Global:$global -Uninstall + rm_shims $app $old_manifest $global $architecture + + # If a junction was used during install, that will have been used + # as the reference directory. Otherwise it will just be the version + # directory. + $refdir = unlink_current $dir + uninstall_psmodule $old_manifest $refdir $global + env_rm_path $old_manifest $refdir $global $architecture + env_rm $old_manifest $global $architecture + + if ($force -and ($old_version -eq $version)) { + if (!(Test-Path "$dir/../_$version.old")) { + Move-Item "$dir" "$dir/../_$version.old" + } else { + $i = 1 + while (Test-Path "$dir/../_$version.old($i)") { + $i++ + } + Move-Item "$dir" "$dir/../_$version.old($i)" + } + } + + Invoke-HookScript -HookType 'post_uninstall' -Manifest $old_manifest -Arch $architecture + + if ($bucket) { + # add bucket name it was installed from + $app = "$bucket/$app" + } + if ($install.url) { + # use the url of the install json if the application was installed through url + $app = $install.url + } + + if ($independent) { + install_app $app $architecture $global $suggested $use_cache $check_hash + } else { + # Also add missing dependencies + $apps = @(Get-Dependency $app $architecture) -ne $app + ensure_none_failed $apps + $apps.Where({ !(installed $_) }) + $app | ForEach-Object { install_app $_ $architecture $global $suggested $use_cache $check_hash } + } +} diff --git a/libexec/scoop-install.ps1 b/libexec/scoop-install.ps1 index 6af199a8b8..da5a23c8cb 100644 --- a/libexec/scoop-install.ps1 +++ b/libexec/scoop-install.ps1 @@ -33,6 +33,7 @@ . "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest' 'Select-CurrentVersion' (indirectly) . "$PSScriptRoot\..\lib\system.ps1" . "$PSScriptRoot\..\lib\install.ps1" +. "$PSScriptRoot\..\lib\update.ps1" . "$PSScriptRoot\..\lib\download.ps1" . "$PSScriptRoot\..\lib\decompress.ps1" . "$PSScriptRoot\..\lib\shortcuts.ps1" @@ -65,7 +66,7 @@ if ($global -and !(is_admin)) { if (is_scoop_outdated) { if ($opt.u -or $opt.'no-update-scoop') { - warn "Scoop is out of date." + warn 'Scoop is out of date.' } else { & "$PSScriptRoot\scoop-update.ps1" } @@ -132,6 +133,11 @@ if ((Test-Aria2Enabled) -and (get_config 'aria2-warning-enabled' $true)) { warn "Should it cause issues, run 'scoop config aria2-enabled false' to disable it." warn "To disable this warning, run 'scoop config aria2-warning-enabled false'." } + +# Update extraction tools ahead of installs/updates +$outdated_helpers = $apps | ForEach-Object { [PSCustomObject]@{App = $_; Global = $global } } | Get-OutdatedHelper +$outdated_helpers | ForEach-Object { update $_.App $_.Global $false $independent $suggested $use_cache $check_hash } + $apps | ForEach-Object { install_app $_ $architecture $global $suggested $use_cache $check_hash } show_suggestions $suggested diff --git a/libexec/scoop-update.ps1 b/libexec/scoop-update.ps1 index bc590a13f6..ba5daf52c1 100644 --- a/libexec/scoop-update.ps1 +++ b/libexec/scoop-update.ps1 @@ -24,6 +24,7 @@ . "$PSScriptRoot\..\lib\versions.ps1" . "$PSScriptRoot\..\lib\depends.ps1" . "$PSScriptRoot\..\lib\install.ps1" +. "$PSScriptRoot\..\lib\update.ps1" . "$PSScriptRoot\..\lib\download.ps1" if (get_config USE_SQLITE_CACHE) { . "$PSScriptRoot\..\lib\database.ps1" @@ -63,7 +64,7 @@ $show_update_log = get_config SHOW_UPDATE_LOG $true function Sync-Scoop { [CmdletBinding()] - Param ( + param ( [Switch]$Log ) # Test if Scoop Core is hold @@ -153,7 +154,7 @@ function Sync-Scoop { } function Sync-Bucket { - Param ( + param ( [Switch]$Log ) Write-Host 'Updating Buckets...' @@ -258,133 +259,6 @@ function Sync-Bucket { } } -function update($app, $global, $quiet = $false, $independent, $suggested, $use_cache = $true, $check_hash = $true) { - $old_version = Select-CurrentVersion -AppName $app -Global:$global - $old_manifest = installed_manifest $app $old_version $global - $install = install_info $app $old_version $global - - # re-use architecture, bucket and url from first install - $architecture = Format-ArchitectureString $install.architecture - $bucket = $install.bucket - if ($null -eq $bucket) { - $bucket = 'main' - } - $url = $install.url - - $manifest = manifest $app $bucket $url - $version = $manifest.version - $is_nightly = $version -eq 'nightly' - if ($is_nightly) { - $version = nightly_version $quiet - $check_hash = $false - } - - if (!$force -and ($old_version -eq $version)) { - if (!$quiet) { - warn "The latest version of '$app' ($version) is already installed." - } - return - } - if (!$version) { - # installed from a custom bucket/no longer supported - error "No manifest available for '$app'." - return - } - - Write-Host "Updating '$app' ($old_version -> $version)" - - #region Workaround for #2952 - if (test_running_process $app $global) { - Write-Host 'Running process detected, skip updating.' - return - } - #endregion Workaround for #2952 - - # 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' - if (Test-Aria2Enabled) { - Invoke-CachedAria2Download $app $version $manifest $architecture $cachedir $manifest.cookie $true $check_hash - } else { - $urls = script:url $manifest $architecture - - foreach ($url in $urls) { - Invoke-CachedDownload $app $version $url $null $manifest.cookie $true - - if ($check_hash) { - $manifest_hash = hash_for_url $manifest $url $architecture - $source = cache_path $app $version $url - $ok, $err = check_hash $source $manifest_hash $(show_app $app $bucket) - - if (!$ok) { - error $err - if (Test-Path $source) { - # rm cached file - 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') - } - } - } - } - # There is no need to check hash again while installing - $check_hash = $false - # endregion Workaround - - $dir = versiondir $app $old_version $global - $persist_dir = persistdir $app $global - - Invoke-HookScript -HookType 'pre_uninstall' -Manifest $old_manifest -Arch $architecture - - Write-Host "Uninstalling '$app' ($old_version)" - Invoke-Installer -Path $dir -Manifest $old_manifest -ProcessorArchitecture $architecture -Global:$global -Uninstall - rm_shims $app $old_manifest $global $architecture - - # If a junction was used during install, that will have been used - # as the reference directory. Otherwise it will just be the version - # directory. - $refdir = unlink_current $dir - uninstall_psmodule $old_manifest $refdir $global - env_rm_path $old_manifest $refdir $global $architecture - env_rm $old_manifest $global $architecture - - if ($force -and ($old_version -eq $version)) { - if (!(Test-Path "$dir/../_$version.old")) { - Move-Item "$dir" "$dir/../_$version.old" - } else { - $i = 1 - While (Test-Path "$dir/../_$version.old($i)") { - $i++ - } - Move-Item "$dir" "$dir/../_$version.old($i)" - } - } - - Invoke-HookScript -HookType 'post_uninstall' -Manifest $old_manifest -Arch $architecture - - if ($bucket) { - # add bucket name it was installed from - $app = "$bucket/$app" - } - if ($install.url) { - # use the url of the install json if the application was installed through url - $app = $install.url - } - - if ($independent) { - install_app $app $architecture $global $suggested $use_cache $check_hash - } else { - # Also add missing dependencies - $apps = @(Get-Dependency $app $architecture) -ne $app - ensure_none_failed $apps - $apps.Where({ !(installed $_) }) + $app | ForEach-Object { install_app $_ $architecture $global $suggested $use_cache $check_hash } - } -} - if (-not ($apps -or $all)) { if ($global) { error 'scoop update: --global is invalid when is not specified.' @@ -431,7 +305,10 @@ if (-not ($apps -or $all)) { $status = app_status $app $global if ($status.installed -and ($force -or $status.outdated)) { if (!$status.hold) { - $outdated += applist $app $global + $outdated += [PSCustomObject]@{ + App = $app + Global = $global + } Write-Host -f yellow ("$app`: $($status.version) -> $($status.latest_version){0}" -f ('', ' (global)')[$global]) } else { warn "'$app' is held to version $($status.version)" @@ -446,6 +323,11 @@ if (-not ($apps -or $all)) { } } + # Update extraction tools ahead of installs/updates + $outdated_helpers = $outdated | Get-OutdatedHelper + $outdated = $outdated | Where-Object { $outdated.App -notin $outdated_helpers.App } + $outdated = $outdated_helpers + $outdated + if ($outdated -and ((Test-Aria2Enabled) -and (get_config 'aria2-warning-enabled' $true))) { warn "Scoop uses 'aria2c' for multi-connection downloads." warn "Should it cause issues, run 'scoop config aria2-enabled false' to disable it." @@ -462,7 +344,7 @@ if (-not ($apps -or $all)) { $suggested = @{} # $outdated is a list of ($app, $global) tuples - $outdated | ForEach-Object { update @_ $quiet $independent $suggested $use_cache $check_hash } + $outdated | ForEach-Object { update $_.App $_.Global $quiet $independent $suggested $use_cache $check_hash } } exit 0 From 4ffe9e6cb53555bcb8d3a2996bd0152e0bcc5f06 Mon Sep 17 00:00:00 2001 From: z-Fng <54583083+z-Fng@users.noreply.github.com> Date: Thu, 18 Dec 2025 10:22:45 +0100 Subject: [PATCH 2/6] Fix typos and add missing $force parameter --- lib/update.ps1 | 2 +- libexec/scoop-install.ps1 | 2 +- libexec/scoop-update.ps1 | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/update.ps1 b/lib/update.ps1 index 5d8fe9937a..3aa3f0e866 100644 --- a/lib/update.ps1 +++ b/lib/update.ps1 @@ -1,4 +1,4 @@ -function update($app, $global, $quiet = $false, $independent, $suggested, $use_cache = $true, $check_hash = $true) { +function update($app, $global, $force, $quiet = $false, $independent, $suggested, $use_cache = $true, $check_hash = $true) { $old_version = Select-CurrentVersion -AppName $app -Global:$global $old_manifest = installed_manifest $app $old_version $global $install = install_info $app $old_version $global diff --git a/libexec/scoop-install.ps1 b/libexec/scoop-install.ps1 index da5a23c8cb..e90332f8cd 100644 --- a/libexec/scoop-install.ps1 +++ b/libexec/scoop-install.ps1 @@ -136,7 +136,7 @@ if ((Test-Aria2Enabled) -and (get_config 'aria2-warning-enabled' $true)) { # Update extraction tools ahead of installs/updates $outdated_helpers = $apps | ForEach-Object { [PSCustomObject]@{App = $_; Global = $global } } | Get-OutdatedHelper -$outdated_helpers | ForEach-Object { update $_.App $_.Global $false $independent $suggested $use_cache $check_hash } +$outdated_helpers | ForEach-Object { update $_.App $_.Global $false $false $independent $suggested $use_cache $check_hash } $apps | ForEach-Object { install_app $_ $architecture $global $suggested $use_cache $check_hash } diff --git a/libexec/scoop-update.ps1 b/libexec/scoop-update.ps1 index ba5daf52c1..6329d984f4 100644 --- a/libexec/scoop-update.ps1 +++ b/libexec/scoop-update.ps1 @@ -325,7 +325,7 @@ if (-not ($apps -or $all)) { # Update extraction tools ahead of installs/updates $outdated_helpers = $outdated | Get-OutdatedHelper - $outdated = $outdated | Where-Object { $outdated.App -notin $outdated_helpers.App } + $outdated = $outdated | Where-Object { $_.App -notin $outdated_helpers.App } $outdated = $outdated_helpers + $outdated if ($outdated -and ((Test-Aria2Enabled) -and (get_config 'aria2-warning-enabled' $true))) { @@ -343,8 +343,7 @@ if (-not ($apps -or $all)) { } $suggested = @{} - # $outdated is a list of ($app, $global) tuples - $outdated | ForEach-Object { update $_.App $_.Global $quiet $independent $suggested $use_cache $check_hash } + $outdated | ForEach-Object { update $_.App $_.Global $force $quiet $independent $suggested $use_cache $check_hash } } exit 0 From 597ca423d8e0031f2a587e631a2538d9245ed2df Mon Sep 17 00:00:00 2001 From: z-Fng <54583083+z-Fng@users.noreply.github.com> Date: Sun, 21 Dec 2025 19:35:07 -0500 Subject: [PATCH 3/6] Fix incorrect parameter handling in Get-OutdatedHelper function --- lib/core.ps1 | 2 +- lib/depends.ps1 | 37 +++++++++++++------------------------ libexec/scoop-install.ps1 | 10 ++++++++-- libexec/scoop-update.ps1 | 16 ++++++++++++++-- 4 files changed, 36 insertions(+), 29 deletions(-) diff --git a/lib/core.ps1 b/lib/core.ps1 index 915a7f4fa4..995ba1995a 100644 --- a/lib/core.ps1 +++ b/lib/core.ps1 @@ -474,7 +474,7 @@ function Get-HelperPath { '7zip' { $HelperPath = Get-AppFilePath '7zip' '7z.exe' } 'Lessmsi' { $HelperPath = Get-AppFilePath 'lessmsi' 'lessmsi.exe' } 'Innounp' { - # Changes to the extraction tool priority should be synced with the Get-OutdatedHelper function as well. + # Changes to the extraction tool priority should be synced with the Get-OutdatedHelper function as well $HelperPath = Get-AppFilePath 'innounp-unicode' 'innounp.exe' if ([String]::IsNullOrEmpty($HelperPath)) { $HelperPath = Get-AppFilePath 'innounp' 'innounp.exe' diff --git a/lib/depends.ps1 b/lib/depends.ps1 index 623e714103..b267ef59d8 100644 --- a/lib/depends.ps1 +++ b/lib/depends.ps1 @@ -161,10 +161,10 @@ function Get-OutdatedHelper { <# .SYNOPSIS Get outdated installation helpers - .PARAMETER App - App's name + .PARAMETER Manifest + App's Manifest .PARAMETER Global - Whether the app is globally installed + Architecture of the app .OUTPUTS [Object[]] A list of concrete outdated helper apps, each represented as a PSCustomObject with 'App' and 'Global' properties @@ -172,29 +172,20 @@ function Get-OutdatedHelper { [CmdletBinding()] [OutputType([Object[]])] param ( - [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] + [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName)] + [PSObject] + $Manifest, + [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName)] [String] - $App, - [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] - [Boolean] - $Global + $Architecture ) + begin { $helpers = @() } process { - $version = Select-CurrentVersion -AppName $App -Global:$Global - - $manifest = if ($version) { - installed_manifest $App $version $Global - } else { - Get-Manifest $App - } - $install = install_info $App $version $Global - $architecture = Format-ArchitectureString $install.architecture - - $helpers += Get-InstallationHelper $manifest $architecture -All | Where-Object { + $helpers += Get-InstallationHelper -Manifest $Manifest -Architecture $Architecture -All | Where-Object { (Test-HelperInstalled -Helper $_) -and ($helpers -notcontains $_) } } @@ -223,24 +214,22 @@ function Get-OutdatedHelper { continue } - info ("Outdated extraction tool '$app' detected: $($status.version) -> $($status.latest_version){0}." -f ('', ' (global)')[$global]) + warn ("Outdated extraction tool '$app' detected: $($status.version) -> $($status.latest_version){0}." -f ('', ' (global)')[$global]) # Filter out outdated helpers that are held if ($status.hold) { - warn "Skipping update of outdated extraction tool '$app' because it is held at version $($status.version)." + warn "Skipping update of '$app' because it is held at version $($status.version)." warn ("Outdated extraction tool may cause decompression errors. Please run 'scoop unhold $app{0}' to unhold it." -f ('', ' -g')[$global]) continue } # Filter out outdated helpers that are blocked by permission issues if ((-not (is_admin)) -and $global) { - warn "Skipping update of outdated extraction tool '$app' because it is globally installed." + warn "Skipping update of '$app' because it is globally installed and requires admin rights to update." warn "Outdated extraction tool may cause decompression errors. Please run 'scoop update $app -g' to update it." continue } - Write-Host ("$app`: $($status.version) -> $($status.latest_version){0}" -f ('', ' (global)')[$global]) -ForegroundColor Yellow - $outdated += [PSCustomObject]@{ App = $app Global = $global diff --git a/libexec/scoop-install.ps1 b/libexec/scoop-install.ps1 index e90332f8cd..cceb3154b5 100644 --- a/libexec/scoop-install.ps1 +++ b/libexec/scoop-install.ps1 @@ -127,6 +127,13 @@ $skip | Where-Object { $explicit_apps -contains $_ } | ForEach-Object { warn "'$app' ($version) is already installed. Skipping." } +$outdated_helpers = $apps | ForEach-Object { + [PSCustomObject]@{ + Manifest = Get-Manifest $_ + Architecture = $architecture + } +} | Get-OutdatedHelper + $suggested = @{ }; if ((Test-Aria2Enabled) -and (get_config 'aria2-warning-enabled' $true)) { warn "Scoop uses 'aria2c' for multi-connection downloads." @@ -135,8 +142,7 @@ if ((Test-Aria2Enabled) -and (get_config 'aria2-warning-enabled' $true)) { } # Update extraction tools ahead of installs/updates -$outdated_helpers = $apps | ForEach-Object { [PSCustomObject]@{App = $_; Global = $global } } | Get-OutdatedHelper -$outdated_helpers | ForEach-Object { update $_.App $_.Global $false $false $independent $suggested $use_cache $check_hash } +$outdated_helpers | ForEach-Object { update $_.App $_.Global $false $false $independent $suggested $use_cache $check_hash} $apps | ForEach-Object { install_app $_ $architecture $global $suggested $use_cache $check_hash } diff --git a/libexec/scoop-update.ps1 b/libexec/scoop-update.ps1 index 6329d984f4..1c72b412be 100644 --- a/libexec/scoop-update.ps1 +++ b/libexec/scoop-update.ps1 @@ -309,7 +309,6 @@ if (-not ($apps -or $all)) { App = $app Global = $global } - Write-Host -f yellow ("$app`: $($status.version) -> $($status.latest_version){0}" -f ('', ' (global)')[$global]) } else { warn "'$app' is held to version $($status.version)" } @@ -324,10 +323,23 @@ if (-not ($apps -or $all)) { } # Update extraction tools ahead of installs/updates - $outdated_helpers = $outdated | Get-OutdatedHelper + $outdated_helpers = $outdated | ForEach-Object { + $version = Select-CurrentVersion -AppName $_.App -Global:$($_.Global) + $install = install_info $_.App $version $_.Global + + [PSCustomObject]@{ + Manifest = manifest $_.App $install.bucket $install.url + Architecture = Format-ArchitectureString -Architecture $install.architecture + } + } | Get-OutdatedHelper $outdated = $outdated | Where-Object { $_.App -notin $outdated_helpers.App } $outdated = $outdated_helpers + $outdated + $outdated | ForEach-Object { + $status = app_status $_.App $_.Global + Write-Host -f yellow ("$($_.App)`: $($status.version) -> $($status.latest_version){0}" -f ('', ' (global)')[$_.Global]) + } + if ($outdated -and ((Test-Aria2Enabled) -and (get_config 'aria2-warning-enabled' $true))) { warn "Scoop uses 'aria2c' for multi-connection downloads." warn "Should it cause issues, run 'scoop config aria2-enabled false' to disable it." From bad8870d68cd0aaf69ca5fa36dc9e736d87376d1 Mon Sep 17 00:00:00 2001 From: z-Fng <54583083+z-Fng@users.noreply.github.com> Date: Mon, 22 Dec 2025 08:44:13 +0800 Subject: [PATCH 4/6] Fix typos, simplify Parameter attributes --- lib/depends.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/depends.ps1 b/lib/depends.ps1 index b267ef59d8..c5034bbfd9 100644 --- a/lib/depends.ps1 +++ b/lib/depends.ps1 @@ -163,7 +163,7 @@ function Get-OutdatedHelper { Get outdated installation helpers .PARAMETER Manifest App's Manifest - .PARAMETER Global + .PARAMETER Architecture Architecture of the app .OUTPUTS [Object[]] @@ -172,10 +172,10 @@ function Get-OutdatedHelper { [CmdletBinding()] [OutputType([Object[]])] param ( - [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName)] + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [PSObject] $Manifest, - [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName)] + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [String] $Architecture ) From 932fba6a6ce630771f5bdde2ccaff4632676e4ed Mon Sep 17 00:00:00 2001 From: z-Fng <54583083+z-Fng@users.noreply.github.com> Date: Tue, 23 Dec 2025 01:26:48 -0500 Subject: [PATCH 5/6] Properly handle 'Get-Manifest' return value --- lib/depends.ps1 | 6 ++++++ libexec/scoop-install.ps1 | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/depends.ps1 b/lib/depends.ps1 index c5034bbfd9..970ccb1a1d 100644 --- a/lib/depends.ps1 +++ b/lib/depends.ps1 @@ -168,6 +168,12 @@ function Get-OutdatedHelper { .OUTPUTS [Object[]] A list of concrete outdated helper apps, each represented as a PSCustomObject with 'App' and 'Global' properties + .NOTES + helper | concrete helper name + 7zip | 7zip + lessmsi | lessmsi + innounp | innounp-unicode/innounp + dark | wixtoolset/dark #> [CmdletBinding()] [OutputType([Object[]])] diff --git a/libexec/scoop-install.ps1 b/libexec/scoop-install.ps1 index cceb3154b5..09174b8959 100644 --- a/libexec/scoop-install.ps1 +++ b/libexec/scoop-install.ps1 @@ -128,8 +128,9 @@ $skip | Where-Object { $explicit_apps -contains $_ } | ForEach-Object { } $outdated_helpers = $apps | ForEach-Object { + $null, $manifest, $null, $null = Get-Manifest $_ [PSCustomObject]@{ - Manifest = Get-Manifest $_ + Manifest = $manifest Architecture = $architecture } } | Get-OutdatedHelper @@ -142,7 +143,7 @@ if ((Test-Aria2Enabled) -and (get_config 'aria2-warning-enabled' $true)) { } # Update extraction tools ahead of installs/updates -$outdated_helpers | ForEach-Object { update $_.App $_.Global $false $false $independent $suggested $use_cache $check_hash} +$outdated_helpers | ForEach-Object { update $_.App $_.Global $false $false $independent $suggested $use_cache $check_hash } $apps | ForEach-Object { install_app $_ $architecture $global $suggested $use_cache $check_hash } From efb9adee616f04c685e52360d6a3978f0b1870c1 Mon Sep 17 00:00:00 2001 From: z-Fng <54583083+z-Fng@users.noreply.github.com> Date: Tue, 21 Apr 2026 10:20:36 +0000 Subject: [PATCH 6/6] Adjust the priority of the WIX extraction tool --- lib/depends.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/depends.ps1 b/lib/depends.ps1 index 970ccb1a1d..9ddee13b99 100644 --- a/lib/depends.ps1 +++ b/lib/depends.ps1 @@ -173,7 +173,7 @@ function Get-OutdatedHelper { 7zip | 7zip lessmsi | lessmsi innounp | innounp-unicode/innounp - dark | wixtoolset/dark + dark | dark/wixtoolset #> [CmdletBinding()] [OutputType([Object[]])] @@ -205,7 +205,7 @@ function Get-OutdatedHelper { '7zip' { '7zip' } 'lessmsi' { 'lessmsi' } 'innounp' { if (installed 'innounp-unicode') { 'innounp-unicode' } else { 'innounp' } } - 'dark' { if (installed 'wixtoolset') { 'wixtoolset' } else { 'dark' } } + 'dark' { if (installed 'dark') { 'dark' } else { 'wixtoolset' } } default { $null } }