Skip to content

Commit d3d3bef

Browse files
committed
feat(scoop-install|update): Update extraction tools ahead of installs/updates
1 parent cbd7c64 commit d3d3bef

8 files changed

Lines changed: 285 additions & 139 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- **download|scoop-download:** Add GitHub issue prompt when the default downloader fails ([#6539](https://github.com/ScoopInstaller/Scoop/issues/6539))
99
- **download|scoop-config:** Allow disabling automatic fallback to the default downloader when Aria2c download fails ([#6538](https://github.com/ScoopInstaller/Scoop/issues/6538))
1010
- **checkver:** Set GitHub mode default jsonpath and regex ([#6653](https://github.com/ScoopInstaller/Scoop/issues/6653))
11+
- **scoop-install|update:** Update extraction tools ahead of installs/updates ([#6572](https://github.com/ScoopInstaller/Scoop/issues/6572))
1112

1213
### Bug Fixes
1314

lib/core.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,12 +474,14 @@ function Get-HelperPath {
474474
'7zip' { $HelperPath = Get-AppFilePath '7zip' '7z.exe' }
475475
'Lessmsi' { $HelperPath = Get-AppFilePath 'lessmsi' 'lessmsi.exe' }
476476
'Innounp' {
477+
# Changes to the extraction tool priority should be synced with the Get-OutdatedHelper function as well
477478
$HelperPath = Get-AppFilePath 'innounp-unicode' 'innounp.exe'
478479
if ([String]::IsNullOrEmpty($HelperPath)) {
479480
$HelperPath = Get-AppFilePath 'innounp' 'innounp.exe'
480481
}
481482
}
482483
'Dark' {
484+
# Changes to the extraction tool priority should be synced with the Get-OutdatedHelper function as well
483485
$HelperPath = Get-AppFilePath 'dark' 'dark.exe'
484486
if ([String]::IsNullOrEmpty($HelperPath)) {
485487
$HelperPath = Get-AppFilePath 'wixtoolset' 'wix.exe'

lib/depends.ps1

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ function Get-InstallationHelper {
7979
App's manifest
8080
.PARAMETER Architecture
8181
Architecture of the app
82-
.PARAMETER All
83-
If true, return all helpers, otherwise return only helpers that are not already installed
82+
.PARAMETER IncludeInstalled
83+
Include helpers that are already installed. By default, only missing helpers are returned.
8484
.OUTPUTS
8585
[Object[]]
8686
List of helpers
@@ -95,7 +95,7 @@ function Get-InstallationHelper {
9595
[String]
9696
$Architecture,
9797
[Switch]
98-
$All
98+
$IncludeInstalled
9999
)
100100
begin {
101101
$helper = @()
@@ -118,7 +118,7 @@ function Get-InstallationHelper {
118118
if ($script -like '*Expand-DarkArchive *') {
119119
$helper += 'dark'
120120
}
121-
if (!$All) {
121+
if (!$IncludeInstalled) {
122122
'7zip', 'lessmsi', 'innounp', 'dark' | ForEach-Object {
123123
if (Test-HelperInstalled -Helper $_) {
124124
$helper = $helper -ne $_
@@ -156,3 +156,92 @@ function Test-LessmsiRequirement {
156156
)
157157
return ($Uri | Where-Object { $_ -match '\.msi$' }).Count -gt 0
158158
}
159+
160+
function Get-OutdatedHelper {
161+
<#
162+
.SYNOPSIS
163+
Get outdated installation helpers
164+
.PARAMETER Manifest
165+
App's Manifest
166+
.PARAMETER Architecture
167+
Architecture of the app
168+
.OUTPUTS
169+
[Object[]]
170+
A list of concrete outdated helper apps, each represented as a PSCustomObject with 'App' and 'Global' properties
171+
.NOTES
172+
helper | concrete helper name
173+
7zip | 7zip
174+
lessmsi | lessmsi
175+
innounp | innounp-unicode/innounp
176+
dark | dark/wixtoolset
177+
#>
178+
[CmdletBinding()]
179+
[OutputType([Object[]])]
180+
param (
181+
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
182+
[PSObject]
183+
$Manifest,
184+
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
185+
[String]
186+
$Architecture
187+
)
188+
189+
begin {
190+
$helpers = @()
191+
}
192+
193+
process {
194+
$helpers += Get-InstallationHelper -Manifest $Manifest -Architecture $Architecture -IncludeInstalled | Where-Object {
195+
(Test-HelperInstalled -Helper $_) -and ($helpers -notcontains $_)
196+
}
197+
}
198+
199+
end {
200+
$outdated = @()
201+
202+
foreach ($helper in $helpers) {
203+
# Get the concrete app name
204+
$app = switch ($helper) {
205+
'7zip' { '7zip' }
206+
'lessmsi' { 'lessmsi' }
207+
'innounp' { if (installed 'innounp-unicode') { 'innounp-unicode' } else { 'innounp' } }
208+
'dark' { if (installed 'dark') { 'dark' } else { 'wixtoolset' } }
209+
default { $null }
210+
}
211+
212+
if (-not $app) {
213+
continue
214+
}
215+
216+
$global = installed $app $true
217+
$status = app_status $app $global
218+
219+
if (-not ($status.installed -and $status.outdated)) {
220+
continue
221+
}
222+
223+
warn ("Outdated extraction tool '$app' detected: $($status.version) -> $($status.latest_version){0}." -f ('', ' (global)')[$global])
224+
225+
# Filter out outdated helpers that are held
226+
if ($status.hold) {
227+
warn "Skipping update of '$app' because it is held at version $($status.version)."
228+
warn ("Outdated extraction tool may cause decompression errors. Please run 'scoop unhold $app{0}' to unhold it." -f ('', ' -g')[$global])
229+
continue
230+
}
231+
232+
# Filter out outdated helpers that are blocked by permission issues
233+
if ((-not (is_admin)) -and $global) {
234+
warn "Skipping update of '$app' because it is globally installed and requires admin rights to update."
235+
warn "Outdated extraction tool may cause decompression errors. Please run 'scoop update $app -g' to update it."
236+
continue
237+
}
238+
239+
$outdated += [PSCustomObject]@{
240+
App = $app
241+
Global = $global
242+
}
243+
}
244+
245+
return , $outdated
246+
}
247+
}

lib/install.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ function Invoke-Installer {
110110
if ($installer.file -or $installer.args) {
111111
# Installer filename is either explicit defined ('installer.file') or file name in the first URL
112112
if (!$Name) {
113-
$Name = url_filename @(url $manifest $architecture)
113+
$Name = url_filename @(url $manifest $ProcessorArchitecture)
114114
}
115115
$progName = "$Path\$(coalesce $installer.file $Name[0])"
116116
if (!(is_in_dir $Path $progName)) {

lib/update.ps1

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
function update($app, $global, $force, $quiet = $false, $independent, $suggested, $use_cache = $true, $check_hash = $true) {
2+
$old_version = Select-CurrentVersion -AppName $app -Global:$global
3+
$old_manifest = installed_manifest $app $old_version $global
4+
$install = install_info $app $old_version $global
5+
6+
# re-use architecture, bucket and url from first install
7+
$architecture = Format-ArchitectureString $install.architecture
8+
$bucket = $install.bucket
9+
if ($null -eq $bucket) {
10+
$bucket = 'main'
11+
}
12+
$url = $install.url
13+
14+
$manifest = manifest $app $bucket $url
15+
$version = $manifest.version
16+
$is_nightly = $version -eq 'nightly'
17+
if ($is_nightly) {
18+
$version = nightly_version $quiet
19+
$check_hash = $false
20+
}
21+
22+
if (!$force -and ($old_version -eq $version)) {
23+
if (!$quiet) {
24+
warn "The latest version of '$app' ($version) is already installed."
25+
}
26+
return
27+
}
28+
if (!$version) {
29+
# installed from a custom bucket/no longer supported
30+
error "No manifest available for '$app'."
31+
return
32+
}
33+
34+
Write-Host "Updating '$app' ($old_version -> $version)"
35+
36+
#region Workaround for #2952
37+
if (test_running_process $app $global) {
38+
Write-Host 'Running process detected, skip updating.'
39+
return
40+
}
41+
#endregion Workaround for #2952
42+
43+
# region Workaround
44+
# Workaround for https://github.com/ScoopInstaller/Scoop/issues/2220 until install is refactored
45+
# Remove and replace whole region after proper fix
46+
Write-Host 'Downloading new version'
47+
if (Test-Aria2Enabled) {
48+
Invoke-CachedAria2Download $app $version $manifest $architecture $cachedir $manifest.cookie $true $check_hash
49+
} else {
50+
$urls = script:url $manifest $architecture
51+
52+
foreach ($url in $urls) {
53+
Invoke-CachedDownload $app $version $url $null $manifest.cookie $true
54+
55+
if ($check_hash) {
56+
$manifest_hash = hash_for_url $manifest $url $architecture
57+
$source = cache_path $app $version $url
58+
$ok, $err = check_hash $source $manifest_hash $(show_app $app $bucket)
59+
60+
if (!$ok) {
61+
error $err
62+
if (Test-Path $source) {
63+
# rm cached file
64+
Remove-Item -Force $source
65+
}
66+
if ($url.Contains('sourceforge.net')) {
67+
Write-Host -f yellow 'SourceForge.net is known for causing hash validation fails. Please try again before opening a ticket.'
68+
}
69+
abort $(new_issue_msg $app $bucket 'hash check failed')
70+
}
71+
}
72+
}
73+
}
74+
# There is no need to check hash again while installing
75+
$check_hash = $false
76+
# endregion Workaround
77+
78+
$dir = versiondir $app $old_version $global
79+
$persist_dir = persistdir $app $global
80+
81+
Invoke-HookScript -HookType 'pre_uninstall' -Manifest $old_manifest -Arch $architecture
82+
83+
Write-Host "Uninstalling '$app' ($old_version)"
84+
Invoke-Installer -Path $dir -Manifest $old_manifest -ProcessorArchitecture $architecture -Global:$global -Uninstall
85+
rm_shims $app $old_manifest $global $architecture
86+
87+
# If a junction was used during install, that will have been used
88+
# as the reference directory. Otherwise it will just be the version
89+
# directory.
90+
$refdir = unlink_current $dir
91+
uninstall_psmodule $old_manifest $refdir $global
92+
env_rm_path $old_manifest $refdir $global $architecture
93+
env_rm $old_manifest $global $architecture
94+
95+
if ($force -and ($old_version -eq $version)) {
96+
if (!(Test-Path "$dir/../_$version.old")) {
97+
Move-Item "$dir" "$dir/../_$version.old"
98+
} else {
99+
$i = 1
100+
while (Test-Path "$dir/../_$version.old($i)") {
101+
$i++
102+
}
103+
Move-Item "$dir" "$dir/../_$version.old($i)"
104+
}
105+
}
106+
107+
Invoke-HookScript -HookType 'post_uninstall' -Manifest $old_manifest -Arch $architecture
108+
109+
if ($bucket) {
110+
# add bucket name it was installed from
111+
$app = "$bucket/$app"
112+
}
113+
if ($install.url) {
114+
# use the url of the install json if the application was installed through url
115+
$app = $install.url
116+
}
117+
118+
if ($independent) {
119+
install_app $app $architecture $global $suggested $use_cache $check_hash
120+
} else {
121+
# Also add missing dependencies
122+
$apps = @(Get-Dependency $app $architecture) -ne $app
123+
ensure_none_failed $apps
124+
$apps.Where({ !(installed $_) }) + $app | ForEach-Object { install_app $_ $architecture $global $suggested $use_cache $check_hash }
125+
}
126+
}

libexec/scoop-install.ps1

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
. "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
3434
. "$PSScriptRoot\..\lib\system.ps1"
3535
. "$PSScriptRoot\..\lib\install.ps1"
36+
. "$PSScriptRoot\..\lib\update.ps1"
3637
. "$PSScriptRoot\..\lib\download.ps1"
3738
. "$PSScriptRoot\..\lib\decompress.ps1"
3839
. "$PSScriptRoot\..\lib\shortcuts.ps1"
@@ -65,7 +66,7 @@ if ($global -and !(is_admin)) {
6566

6667
if (is_scoop_outdated) {
6768
if ($opt.u -or $opt.'no-update-scoop') {
68-
warn "Scoop is out of date."
69+
warn 'Scoop is out of date.'
6970
} else {
7071
& "$PSScriptRoot\scoop-update.ps1"
7172
}
@@ -126,12 +127,29 @@ $skip | Where-Object { $explicit_apps -contains $_ } | ForEach-Object {
126127
warn "'$app' ($version) is already installed. Skipping."
127128
}
128129

130+
$outdated_helpers = $apps | ForEach-Object {
131+
$null, $manifest, $null, $null = Get-Manifest $_
132+
133+
if (-not $manifest) {
134+
return
135+
}
136+
137+
[PSCustomObject]@{
138+
Manifest = $manifest
139+
Architecture = $architecture
140+
}
141+
} | Get-OutdatedHelper
142+
129143
$suggested = @{ };
130144
if ((Test-Aria2Enabled) -and (get_config 'aria2-warning-enabled' $true)) {
131145
warn "Scoop uses 'aria2c' for multi-connection downloads."
132146
warn "Should it cause issues, run 'scoop config aria2-enabled false' to disable it."
133147
warn "To disable this warning, run 'scoop config aria2-warning-enabled false'."
134148
}
149+
150+
# Update extraction tools ahead of installs/updates
151+
$outdated_helpers | ForEach-Object { update $_.App $_.Global $false $false $independent $suggested $use_cache $check_hash }
152+
135153
$apps | ForEach-Object { install_app $_ $architecture $global $suggested $use_cache $check_hash }
136154

137155
show_suggestions $suggested

0 commit comments

Comments
 (0)