Skip to content

Commit f13fcd0

Browse files
committed
(chore): switch to local checkver.ps1 to bypass upstream limitations
- Avoids potential breaking changes and environment restrictions from the upstream - See ScoopInstaller/Scoop#6652
1 parent 0d01f0e commit f13fcd0

1 file changed

Lines changed: 388 additions & 4 deletions

File tree

bin/checkver.ps1

Lines changed: 388 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,388 @@
1-
if (!$env:SCOOP_HOME) { $env:SCOOP_HOME = Convert-Path (scoop prefix scoop) }
2-
$checkver = "$env:SCOOP_HOME/bin/checkver.ps1"
3-
$dir = "$PSScriptRoot/../bucket" # checks the parent dir
4-
& $checkver -Dir $dir @Args
1+
<#
2+
.SYNOPSIS
3+
Check manifest for a newer version.
4+
.DESCRIPTION
5+
Checks websites for newer versions using an (optional) regular expression defined in the manifest.
6+
.PARAMETER App
7+
Manifest name to search.
8+
Placeholders are supported.
9+
.PARAMETER Dir
10+
Where to search for manifest(s).
11+
.PARAMETER Update
12+
Update given manifest
13+
.PARAMETER ForceUpdate
14+
Update given manifest(s) even when there is no new version.
15+
Useful for hash updates.
16+
.PARAMETER SkipUpdated
17+
Updated manifests will not be shown.
18+
.PARAMETER Version
19+
Update manifest to specific version.
20+
.PARAMETER ThrowError
21+
Throw error as exception instead of just printing it.
22+
.EXAMPLE
23+
PS BUCKETROOT > .\bin\checkver.ps1
24+
Check all manifest inside default directory.
25+
.EXAMPLE
26+
PS BUCKETROOT > .\bin\checkver.ps1 -SkipUpdated
27+
Check all manifest inside default directory (list only outdated manifests).
28+
.EXAMPLE
29+
PS BUCKETROOT > .\bin\checkver.ps1 -Update
30+
Check all manifests and update All outdated manifests.
31+
.EXAMPLE
32+
PS BUCKETROOT > .\bin\checkver.ps1 APP
33+
Check manifest APP.json inside default directory.
34+
.EXAMPLE
35+
PS BUCKETROOT > .\bin\checkver.ps1 APP -Update
36+
Check manifest APP.json and update, if there is newer version.
37+
.EXAMPLE
38+
PS BUCKETROOT > .\bin\checkver.ps1 APP -ForceUpdate
39+
Check manifest APP.json and update, even if there is no new version.
40+
.EXAMPLE
41+
PS BUCKETROOT > .\bin\checkver.ps1 APP -Update -Version VER
42+
Check manifest APP.json and update, using version VER
43+
.EXAMPLE
44+
PS BUCKETROOT > .\bin\checkver.ps1 APP DIR
45+
Check manifest APP.json inside ./DIR directory.
46+
.EXAMPLE
47+
PS BUCKETROOT > .\bin\checkver.ps1 -Dir DIR
48+
Check all manifests inside ./DIR directory.
49+
.EXAMPLE
50+
PS BUCKETROOT > .\bin\checkver.ps1 APP DIR -Update
51+
Check manifest APP.json inside ./DIR directory and update if there is newer version.
52+
#>
53+
param(
54+
[String] $App = '*',
55+
[String] $Dir = "$PSScriptRoot/../bucket",
56+
[String] $Segment,
57+
[Switch] $Update,
58+
[Switch] $ForceUpdate,
59+
[Switch] $SkipUpdated,
60+
[String] $Version = '',
61+
[Switch] $ThrowError
62+
)
63+
64+
if (-not $env:SCOOP_HOME) { $env:SCOOP_HOME = Convert-Path (scoop prefix scoop) }
65+
66+
. "$env:SCOOP_HOME\lib\core.ps1"
67+
. "$env:SCOOP_HOME\lib\autoupdate.ps1"
68+
. "$env:SCOOP_HOME\lib\manifest.ps1"
69+
. "$env:SCOOP_HOME\lib\buckets.ps1"
70+
. "$env:SCOOP_HOME\lib\json.ps1"
71+
. "$env:SCOOP_HOME\lib\versions.ps1"
72+
. "$env:SCOOP_HOME\lib\download.ps1"
73+
74+
if ($App -ne '*' -and (Test-Path $App -PathType Leaf)) {
75+
$Dir = Split-Path $App
76+
$files = Get-ChildItem $Dir -Filter (Split-Path $App -Leaf)
77+
}
78+
elseif ($Dir) {
79+
$Dir = Convert-Path $Dir
80+
$files = Get-ChildItem $Dir -Filter "$App.json" -Recurse
81+
}
82+
else {
83+
throw "'-Dir' parameter required if '-App' is not a filepath!"
84+
}
85+
86+
if ($Segment) {
87+
$start, $end = $Segment.Split('-')
88+
$files = $files.Where({
89+
$letter = $_.BaseName.ToLower()[0]
90+
$letter -ge $start -and $letter -le $end
91+
})
92+
}
93+
94+
$GitHubToken = Get-GitHubToken
95+
96+
# don't use $Version with $App = '*'
97+
if ($App -eq '*' -and $Version -ne '') {
98+
throw "Don't use '-Version' with '-App *'!"
99+
}
100+
101+
# get apps to check
102+
$Queue = @()
103+
$json = ''
104+
$files | ForEach-Object {
105+
$file = $_.FullName
106+
$json = parse_json $file
107+
if ($json.checkver) {
108+
$Queue += , @($_.BaseName, $json, $file)
109+
}
110+
}
111+
112+
# clear any existing events
113+
Get-Event | Remove-Event
114+
Get-EventSubscriber | Unregister-Event
115+
116+
# start all downloads
117+
$in_progress = 0
118+
$Queue | ForEach-Object {
119+
$name, $json, $file = $_
120+
121+
$substitutions = Get-VersionSubstitution $json.version # 'autoupdate.ps1'
122+
123+
$wc = [System.Net.WebClient]::new()
124+
if ($json.checkver.useragent) {
125+
$wc.Headers.Add('User-Agent', (substitute $json.checkver.useragent $substitutions))
126+
}
127+
else {
128+
$wc.Headers.Add('User-Agent', (Get-UserAgent))
129+
}
130+
131+
# Not Specified
132+
$regex = ''
133+
$jsonpath = ''
134+
$xpath = ''
135+
$replace = ''
136+
137+
if ($json.checkver.url) {
138+
$url = $json.checkver.url
139+
}
140+
else {
141+
# It is meaningless to check the homepage in abyss bucket.
142+
# So, we use a stable URL as a placeholder to avoid access errors.
143+
$url = 'https://github.com/'
144+
}
145+
146+
$regex = $json.checkver.regex
147+
148+
$jsonpath = $json.checkver.jsonpath
149+
150+
if ($json.checkver.xpath) {
151+
$xpath = $json.checkver.xpath
152+
}
153+
154+
if ($json.checkver.replace -is [string]) {
155+
# If `checkver` is [string], it has a method called `Replace`
156+
$replace = $json.checkver.replace
157+
}
158+
159+
$reverse = $json.checkver.reverse -eq 'true'
160+
161+
$url = substitute $url $substitutions
162+
163+
$state = [psobject]@{
164+
app = $name
165+
file = $file
166+
url = $url
167+
regex = $regex
168+
json = $json
169+
jsonpath = $jsonpath
170+
xpath = $xpath
171+
reverse = $reverse
172+
replace = $replace
173+
}
174+
175+
# get_config PRIVATE_HOSTS | Where-Object { $_ -ne $null -and $url -match $_.match } | ForEach-Object {
176+
# (ConvertFrom-StringData -StringData $_.Headers).GetEnumerator() | ForEach-Object {
177+
# $wc.Headers[$_.Key] = $_.Value
178+
# }
179+
# }
180+
181+
$wc.Headers.Add('Referer', (strip_filename $url))
182+
Register-ObjectEvent $wc downloadDataCompleted -ErrorAction Stop | Out-Null
183+
$wc.DownloadDataAsync($url, $state)
184+
$in_progress++
185+
}
186+
187+
function next($er) {
188+
Write-Host
189+
Write-Host "$App`: " -NoNewline
190+
Write-Host $er -ForegroundColor DarkRed
191+
}
192+
193+
# wait for all to complete
194+
while ($in_progress -gt 0) {
195+
$ev = Wait-Event
196+
Remove-Event $ev.SourceIdentifier
197+
$in_progress--
198+
199+
$state = $ev.SourceEventArgs.UserState
200+
$result = $ev.SourceEventArgs.Result
201+
$app = $state.app
202+
$file = $state.file
203+
$json = $state.json
204+
$url = $state.url
205+
$regexp = $state.regex
206+
$jsonpath = $state.jsonpath
207+
$xpath = $state.xpath
208+
$script = $json.checkver.script
209+
$reverse = $state.reverse
210+
$replace = $state.replace
211+
$expected_ver = $json.version
212+
$ver = $Version
213+
214+
$matchesHashtable = @{}
215+
216+
if (!$ver) {
217+
if (!$regexp -and $replace) {
218+
next "'replace' requires 're' or 'regex'"
219+
continue
220+
}
221+
$err = $ev.SourceEventArgs.Error
222+
if ($err) {
223+
if (!$script) {
224+
next "$($err.message)`r`nURL $url is not valid"
225+
continue
226+
}
227+
else {
228+
# Run script despite URL download failure
229+
Write-Host "$($err.message)`r`nURL $url is not valid. Falling back to checkver.script ..."
230+
}
231+
}
232+
233+
if ($json.version -in 'pending', 'renamed', 'deprecated', 'virtual') {
234+
if (!$SkipUpdated) {
235+
Write-Host
236+
Write-Host "$app`: " -NoNewline
237+
Write-Host $json.version -ForegroundColor DarkGreen
238+
}
239+
continue
240+
}
241+
242+
$page = $null
243+
$source = $url
244+
245+
if ($url -and !$err) {
246+
$ms = New-Object System.IO.MemoryStream
247+
$ms.Write($result, 0, $result.Length)
248+
$ms.Seek(0, 0) | Out-Null
249+
if ($result[0] -eq 0x1F -and $result[1] -eq 0x8B) {
250+
$ms = New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Decompress)
251+
}
252+
$page = (New-Object System.IO.StreamReader($ms, (Get-Encoding $wc))).ReadToEnd()
253+
}
254+
255+
if ($script) {
256+
$page = Invoke-Command ([scriptblock]::Create($script -join "`r`n"))
257+
$source = 'the output of script'
258+
}
259+
260+
if ($null -eq $page) {
261+
next "couldn't retrieve content from $source"
262+
continue
263+
}
264+
265+
if ($jsonpath) {
266+
# Return only a single value if regex is absent
267+
$noregex = [String]::IsNullOrEmpty($regexp)
268+
# If reverse is ON and regex is ON,
269+
# Then reverse would have no effect because regex handles reverse
270+
# on its own
271+
# So in this case we have to disable reverse
272+
$ver = json_path $page $jsonpath $null ($reverse -and $noregex) $noregex
273+
if (!$ver) {
274+
$ver = json_path_legacy $page $jsonpath
275+
}
276+
if (!$ver) {
277+
next "couldn't find '$jsonpath' in $source"
278+
continue
279+
}
280+
}
281+
282+
if ($xpath) {
283+
$xml = [xml]$page
284+
# Find all `significant namespace declarations` from the XML file
285+
$nsList = $xml.SelectNodes('//namespace::*[not(. = ../../namespace::*)]')
286+
# Then add them into the NamespaceManager
287+
$nsmgr = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
288+
$nsList | ForEach-Object {
289+
if ($_.LocalName -eq 'xmlns') {
290+
$nsmgr.AddNamespace('ns', $_.Value)
291+
$xpath = $xpath -replace '/([^:/]+)((?=/)|(?=$))', '/ns:$1'
292+
}
293+
else {
294+
$nsmgr.AddNamespace($_.LocalName, $_.Value)
295+
}
296+
}
297+
# Getting version from XML, using XPath
298+
$ver = $xml.SelectSingleNode($xpath, $nsmgr).'#text'
299+
if (!$ver) {
300+
next "couldn't find '$($xpath -replace 'ns:', '')' in $source"
301+
continue
302+
}
303+
}
304+
305+
if ($jsonpath -and $regexp) {
306+
$page = $ver
307+
$ver = ''
308+
}
309+
310+
if ($xpath -and $regexp) {
311+
$page = $ver
312+
$ver = ''
313+
}
314+
315+
if ($regexp) {
316+
$re = New-Object System.Text.RegularExpressions.Regex($regexp)
317+
if ($reverse) {
318+
$match = $re.Matches($page) | Select-Object -Last 1
319+
}
320+
else {
321+
$match = $re.Matches($page) | Select-Object -First 1
322+
}
323+
324+
if ($match -and $match.Success) {
325+
$re.GetGroupNames() | ForEach-Object { $matchesHashtable.Add($_, $match.Groups[$_].Value) }
326+
$ver = $matchesHashtable['1']
327+
if ($replace) {
328+
$ver = $re.Replace($match.Value, $replace)
329+
}
330+
if (!$ver) {
331+
$ver = $matchesHashtable['version']
332+
}
333+
}
334+
else {
335+
next "couldn't match '$regexp' in $source"
336+
continue
337+
}
338+
}
339+
340+
if (!$ver) {
341+
next "couldn't find new version in $source"
342+
continue
343+
}
344+
}
345+
346+
# Skip actual only if versions are same and there is no -f
347+
if (($ver -eq $expected_ver) -and !$ForceUpdate -and $SkipUpdated) { continue }
348+
349+
Write-Host
350+
Write-Host "$app`: " -NoNewline
351+
352+
# version hasn't changed (step over if forced update)
353+
if ($ver -eq $expected_ver -and !$ForceUpdate) {
354+
Write-Host $ver -ForegroundColor DarkGreen
355+
continue
356+
}
357+
358+
Write-Host $ver -ForegroundColor DarkRed -NoNewline
359+
Write-Host " (scoop version is $expected_ver)" -NoNewline
360+
$update_available = (Compare-Version -ReferenceVersion $ver -DifferenceVersion $expected_ver) -ne 0
361+
362+
if ($json.autoupdate -and $update_available) {
363+
Write-Host ' autoupdate available' -ForegroundColor Cyan
364+
}
365+
else {
366+
Write-Host ''
367+
}
368+
369+
# forcing an update implies updating, right?
370+
if ($ForceUpdate) { $Update = $true }
371+
372+
if ($Update -and $json.autoupdate) {
373+
if ($ForceUpdate) {
374+
Write-Host 'Forcing autoupdate!' -ForegroundColor DarkMagenta
375+
}
376+
try {
377+
Invoke-AutoUpdate $app $file $json $ver $matchesHashtable # 'autoupdate.ps1'
378+
}
379+
catch {
380+
if ($ThrowError) {
381+
throw $_
382+
}
383+
else {
384+
error $_.Exception.Message
385+
}
386+
}
387+
}
388+
}

0 commit comments

Comments
 (0)