34 Commits

Author SHA1 Message Date
Hsiao-nan Cheung
93db5f47f1 chore(release): Bump to version 0.2.2 2022-06-21 13:48:57 +08:00
Hsiao-nan Cheung
5987e499b9 docs(changelog): Update CHANGELOG for v0.2.2 (#5000)
A celebratable 5000
2022-06-21 00:13:33 +08:00
Chawye Hsu
666d474ee1 feat(scoop-update): Support scoop update scoop (#4992) 2022-06-20 14:43:28 +08:00
L. Yeung
e7c0faa29a feat(scoop-hold,scoop-unhold): Support -g/--global flag (#4991)
* feat(scoop-hold,scoop-unhold): Support `-g`/`--global` flag

* Update CHANGELOG.md
2022-06-17 10:53:18 +05:30
ClassicDarkChocolate
9e6c758c1f feat(scoop-virustotal): Migrate to VirusTotal API v3 (#4983) 2022-06-15 23:39:45 +08:00
Hsiao-nan Cheung
0b38c91f1a fix(manifest): Fix bugs in 'Get-Manifest()' (#4986) 2022-06-14 14:25:53 +08:00
yi_Xu
6e25e440af feat(core): Add 'Get-Encoding()' function to fix missing webClient encoding (#4956)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-06-13 23:21:57 +08:00
Hsiao-nan Cheung
d63b7d6f01 chore(release): Bump to version 0.2.1 2022-06-10 23:22:07 +08:00
Hsiao-nan Cheung
ecb8f02d4e Merge branch 'master' into develop 2022-06-10 23:17:38 +08:00
Hsiao-nan Cheung
7e6be8f3f5 Revert "chore(release): Bump to version 0.2.1 (#4960)"
This reverts commit 574bea4975.
2022-06-10 23:13:38 +08:00
Hsiao-nan Cheung
574bea4975 chore(release): Bump to version 0.2.1 (#4960)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
Co-authored-by: Chawye Hsu <chawyehsu@hotmail.com>
Co-authored-by: Issac Lin <issaclin32@gmail.com>
Co-authored-by: Garcha Sprgchma <904364+sprgchma@users.noreply.github.com>
Co-authored-by: L. Yeung <lewis_yeung-ly@outlook.com>
Co-authored-by: Lewin Chan <quotidian-ennui@users.noreply.github.com>
Co-authored-by: Xuesong <amorphobia@users.noreply.github.com>
Co-authored-by: ISHIKAWA Takayuki <topstone@users.noreply.github.com>
Co-authored-by: Rosen Penev <rosenp@gmail.com>
Co-authored-by: rayinfinite <rayinfinite@hotmail.com>
Co-authored-by: beerpsi <92439990+beerpiss@users.noreply.github.com>
Co-authored-by: HUMORCE <humorce@outlook.com>
Co-authored-by: Daniel Villarreal <7376487+danx12@users.noreply.github.com>
2022-06-10 23:02:16 +08:00
Hsiao-nan Cheung
9e70dcad79 fix(get-manfest): Add back '$appPath' (#4981) 2022-06-10 22:31:43 +08:00
Daniel Villarreal
64364b40b4 fix(buckets): Make sure list_buckets return array (#4979) 2022-06-10 18:06:00 +08:00
Chawye Hsu
387835753d docs(changelog): Fix CHANGELOG (#4977) 2022-06-10 10:17:23 +08:00
HUMORCE
bfb5c8d04a fix(scoop-download): Use correct Args when calling Get-Manifest (#4970) 2022-06-10 09:37:39 +08:00
Hsiao-nan Cheung
3a1186ea1b docs(changelog): Update CHANGELOG (#4969) 2022-06-07 09:45:28 +08:00
Hsiao-nan Cheung
ccd067b2b1 refactor(manifest): Rename 'Find-Manifest()' to 'Get-Manifest()' (#4966) 2022-06-07 09:31:30 +08:00
beerpsi
78c1bc45b4 fix(scoop-uninstall): run pre_uninstall before testing running processes (#4962) 2022-06-03 12:17:55 +08:00
Chawye Hsu
dd0f51426b feat(core): Add pre_uninstall and post_uninstall hooks (#4957) 2022-06-02 00:34:57 +08:00
rayinfinite
d6c6ddcbb3 fix(update): Prevent uninstall when update (#4949)
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-06-02 00:27:48 +08:00
Rosen Penev
2e52888b63 chore(core): Deprecate tls1 and tls1.1 (#4950) 2022-05-28 22:38:29 +08:00
Rashil Gandhi
0f6d012d26 fix(shim): Add 'Get-CommandPath()' to find git (#4913)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-05-27 10:18:41 +08:00
Issac Lin
896ea6cdbd chore: Update Nonportable bucket URL (#4955) 2022-05-26 22:39:45 +08:00
Hsiao-nan Cheung
d056d542db fix(core): Use Invoke-Command instead of Invoke-Expression (#4941) 2022-05-26 10:54:34 +08:00
ISHIKAWA Takayuki
ad04dc9e6f fix(core): Allow to use '_' and '.' in bucket name (#4952) 2022-05-25 20:07:21 +08:00
Xuesong
8140a2052c fix(scoop-search): Require files in 'bucket' dir for remote known buckets (#4944) 2022-05-24 18:49:28 +08:00
L. Yeung
ac2fb38722 fix(scoop): Pass CLI arguments as string objects (#4931)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-05-18 00:06:08 +08:00
Lewin Chan
47d7f76f7c fix(scoop-info): Fix error message when manifest is not found (#4935) 2022-05-17 23:31:41 +08:00
L. Yeung
bb5392b486 fix(shim): Remove character replacement in .cmd -> .ps1 shims (#4914) 2022-05-17 23:18:10 +08:00
Garcha Sprgchma
f49f976618 fix(config): Load config file before initialization (#4932) 2022-05-17 23:02:56 +08:00
Chawye Hsu
5d58703484 docs(readme): Update license badge [skip ci] (#4929) 2022-05-17 17:12:26 +08:00
Issac Lin
b130e606cf fix(depends): Avoid digits in archive file extension (#4915)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-05-16 22:32:26 +08:00
Chawye Hsu
c6fc2de306 fix(buckets): Don't write message OK before bucket is cloned (#4925) 2022-05-15 17:10:08 +08:00
Hsiao-nan Cheung
a2600b1203 fix(buckets): Don't check remote URL of non-git buckets (#4923) 2022-05-15 15:49:38 +08:00
36 changed files with 700 additions and 382 deletions

View File

@@ -1,3 +1,49 @@
## [v0.2.2](https://github.com/ScoopInstaller/Scoop/compare/v0.2.1...v0.2.2) - 2022-06-21
### Features
- **core:** Add `Get-Encoding` function to fix missing webclient encoding ([#4956](https://github.com/ScoopInstaller/Scoop/issues/4956))
- **scoop-(un)hold:** Add `-g`/`--global` flag ([#4991](https://github.com/ScoopInstaller/Scoop/issues/4991))
- **scoop-update:** Support `scoop update scoop` ([#4992](https://github.com/ScoopInstaller/Scoop/issues/4992))
- **scoop-virustotal:** Migrate to VirusTotal API v3 ([#4983](https://github.com/ScoopInstaller/Scoop/issues/4983))
### Bug Fixes
- **manifest:** Fix bugs in 'Get-Manifest()' ([#4986](https://github.com/ScoopInstaller/Scoop/issues/4986))
## [v0.2.1](https://github.com/ScoopInstaller/Scoop/compare/v0.2.0...v0.2.1) - 2022-06-10
### Features
- **core:** Add pre_uninstall and post_uninstall hooks ([#4957](https://github.com/ScoopInstaller/Scoop/issues/4957), [#4962](https://github.com/ScoopInstaller/Scoop/issues/4962))
### Bug Fixes
- **bucket:** Make sure `list_buckets` return array ([#4979](https://github.com/ScoopInstaller/Scoop/issues/4979))
- **chore:** Deprecate tls1 and tls1.1 ([#4950](https://github.com/ScoopInstaller/Scoop/issues/4950))
- **chore:** Update Nonportable bucket URL ([#4955](https://github.com/ScoopInstaller/Scoop/issues/4955))
- **core:** Using `Invoke-Command` instead of `Invoke-Expression` ([#4941](https://github.com/ScoopInstaller/Scoop/issues/4941))
- **core:** Load config file before initialization ([#4932](https://github.com/ScoopInstaller/Scoop/issues/4932))
- **core:** Allow to use '_' and '.' in bucket name ([#4952](https://github.com/ScoopInstaller/Scoop/issues/4952))
- **depends:** Avoid digits in archive file extension (except for .7z and .001) ([#4915](https://github.com/ScoopInstaller/Scoop/issues/4915))
- **bucket:** Don't check remote URL of non-git buckets ([#4923](https://github.com/ScoopInstaller/Scoop/issues/4923))
- **bucket:** Don't write message OK before bucket is cloned ([#4925](https://github.com/ScoopInstaller/Scoop/issues/4925))
- **shim:** Add 'Get-CommandPath()' to find git ([#4913](https://github.com/ScoopInstaller/Scoop/issues/4913))
- **shim:** Remove character replacement in .cmd -> .ps1 shims ([#4914](https://github.com/ScoopInstaller/Scoop/issues/4914))
- **scoop:** Pass CLI arguments as string objects ([#4931](https://github.com/ScoopInstaller/Scoop/issues/4931))
- **scoop-info:** Fix error message when manifest is not found ([#4935](https://github.com/ScoopInstaller/Scoop/issues/4935))
- **scoop-search:** Require files in 'bucket' dir for remote known buckets ([#4944](https://github.com/ScoopInstaller/Scoop/issues/4944))
- **update:** Prevent uninstall when update ([#4949](https://github.com/ScoopInstaller/Scoop/issues/4949))
- **scoop-download:** Use correct Args when calling `Get-Manifest` ([#4970](https://github.com/ScoopInstaller/Scoop/issues/4970))
### Code Refactoring
- **manifest:** Rename 'Find-Manifest()' to 'Get-Manifest() ([#4966](https://github.com/ScoopInstaller/Scoop/issues/4966), [#4981](https://github.com/ScoopInstaller/Scoop/issues/4981))
### Documentation
- **readme:** Update license badge ([#4929](https://github.com/ScoopInstaller/Scoop/issues/4929))
## [v0.2.0](https://github.com/ScoopInstaller/Scoop/compare/v0.1.0...v0.2.0) - 2022-05-10
### Features

View File

@@ -27,8 +27,8 @@
<a href="https://gitter.im/lukesampson/scoop">
<img src="https://badges.gitter.im/lukesampson/scoop.png" alt="Gitter Chat" />
</a>
<a href="https://github.com/ScoopInstaller/Scoop/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/ScoopInstaller/Scoop.svg" alt="License" />
<a href="./LICENSE">
<img src="https://img.shields.io/badge/license-UNLICENSE%20or%20MIT-blue" alt="License" />
</a>
</p>
@@ -121,7 +121,7 @@ The following buckets are known to scoop:
- [nerd-fonts](https://github.com/matthewjberger/scoop-nerd-fonts) - Nerd Fonts
- [nirsoft](https://github.com/kodybrown/scoop-nirsoft) - Almost all of the [250+](https://rasa.github.io/scoop-directory/by-apps#kodybrown_scoop-nirsoft) apps from [Nirsoft](https://nirsoft.net)
- [java](https://github.com/ScoopInstaller/Java) - A collection of Java development kits (JDKs), Java runtime engines (JREs), Java's virtual machine debugging tools and Java based runtime engines.
- [nonportable](https://github.com/TheRandomLabs/scoop-nonportable) - Non-portable apps (may require UAC)
- [nonportable](https://github.com/ScoopInstaller/Nonportable) - Non-portable apps (may require UAC)
- [php](https://github.com/ScoopInstaller/PHP) - Installers for most versions of PHP
- [versions](https://github.com/ScoopInstaller/Versions) - Alternative versions of apps found in other buckets

View File

@@ -96,7 +96,7 @@ if (is_unix) {
function execute($cmd) {
Write-Host $cmd -ForegroundColor Green
$output = Invoke-Expression $cmd
$output = Invoke-Command ([scriptblock]::Create($cmd))
if ($LASTEXITCODE -gt 0) {
abort "^^^ Error! See above ^^^ (last command: $cmd)"

View File

@@ -113,7 +113,7 @@ $Queue | ForEach-Object {
} else {
$wc.Headers.Add('User-Agent', (Get-UserAgent))
}
Register-ObjectEvent $wc downloadstringcompleted -ErrorAction Stop | Out-Null
Register-ObjectEvent $wc downloadDataCompleted -ErrorAction Stop | Out-Null
$githubRegex = '\/releases\/tag\/(?:v|V)?([\d.]+)'
@@ -190,7 +190,7 @@ $Queue | ForEach-Object {
}
$wc.Headers.Add('Referer', (strip_filename $url))
$wc.DownloadStringAsync($url, $state)
$wc.DownloadDataAsync($url, $state)
}
function next($er) {
@@ -218,10 +218,10 @@ while ($in_progress -gt 0) {
$ver = $Version
if (!$ver) {
$page = $ev.SourceEventArgs.Result
$page = (Get-Encoding($wc)).GetString($ev.SourceEventArgs.Result)
$err = $ev.SourceEventArgs.Error
if ($json.checkver.script) {
$page = $json.checkver.script -join "`r`n" | Invoke-Expression
$page = Invoke-Command ([scriptblock]::Create($json.checkver.script -join "`r`n"))
}
if ($err) {

View File

@@ -44,7 +44,8 @@ $Queue | ForEach-Object {
try {
$wc = New-Object Net.Webclient
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$home_html = $wc.DownloadString($manifest.homepage)
$homepage = $wc.DownloadData($manifest.homepage)
$home_html = (Get-Encoding($wc)).GetString($homepage)
} catch {
Write-Host "`n$($_.Exception.Message)" -ForegroundColor Red
return

View File

@@ -1,6 +1,4 @@
#Requires -Version 5
param($SubCommand)
Set-StrictMode -Off
. "$PSScriptRoot\..\lib\core.ps1"
@@ -8,24 +6,22 @@ Set-StrictMode -Off
. "$PSScriptRoot\..\lib\commands.ps1"
. "$PSScriptRoot\..\lib\help.ps1"
$subCommand = $Args[0]
# for aliases where there's a local function, re-alias so the function takes precedence
$aliases = Get-Alias | Where-Object { $_.Options -notmatch 'ReadOnly|AllScope' } | ForEach-Object { $_.Name }
Get-ChildItem Function: | Where-Object -Property Name -In -Value $aliases | ForEach-Object {
Set-Alias -Name $_.Name -Value Local:$($_.Name) -Scope Script
}
switch ($SubCommand) {
({ $SubCommand -in @($null, '--help', '/?') }) {
if (!$SubCommand -and $Args -eq '-v') {
$SubCommand = '--version'
} else {
exec 'help'
}
switch ($subCommand) {
({ $subCommand -in @($null, '-h', '--help', '/?') }) {
exec 'help'
}
({ $SubCommand -eq '--version' }) {
({ $subCommand -in @('-v', '--version') }) {
Write-Host 'Current Scoop version:'
if ((Test-CommandAvailable git) -and (Test-Path "$PSScriptRoot\..\.git") -and (get_config SCOOP_BRANCH 'master') -ne 'master') {
Invoke-Expression "git -C '$PSScriptRoot\..' --no-pager log --oneline HEAD -n 1"
git -C "$PSScriptRoot\.." --no-pager log --oneline HEAD -n 1
} else {
$version = Select-String -Pattern '^## \[(v[\d.]+)\].*?([\d-]+)$' -Path "$PSScriptRoot\..\CHANGELOG.md"
Write-Host $version.Matches.Groups[1].Value -ForegroundColor Cyan -NoNewline
@@ -35,22 +31,23 @@ switch ($SubCommand) {
Get-LocalBucket | ForEach-Object {
$bucketLoc = Find-BucketDirectory $_ -Root
if ((Test-Path (Join-Path $bucketLoc '.git')) -and (Test-CommandAvailable git)) {
if ((Test-Path "$bucketLoc\.git") -and (Test-CommandAvailable git)) {
Write-Host "'$_' bucket:"
Invoke-Expression "git -C '$bucketLoc' --no-pager log --oneline HEAD -n 1"
git -C "$bucketLoc" --no-pager log --oneline HEAD -n 1
Write-Host ''
}
}
}
({ $SubCommand -in (commands) }) {
if ($Args -in @('-h', '--help', '/?')) {
exec 'help' @($SubCommand)
({ $subCommand -in (commands) }) {
[string[]]$arguments = $Args | Select-Object -Skip 1
if ($null -ne $arguments -and $arguments[0] -in @('-h', '--help', '/?')) {
exec 'help' @($subCommand)
} else {
exec $SubCommand $Args
exec $subCommand $arguments
}
}
default {
"scoop: '$SubCommand' isn't a scoop command. See 'scoop help'."
"scoop: '$subCommand' isn't a scoop command. See 'scoop help'."
exit 1
}
}

View File

@@ -5,7 +5,7 @@
"nirsoft": "https://github.com/kodybrown/scoop-nirsoft",
"php": "https://github.com/ScoopInstaller/PHP",
"nerd-fonts": "https://github.com/matthewjberger/scoop-nerd-fonts",
"nonportable": "https://github.com/TheRandomLabs/scoop-nonportable",
"nonportable": "https://github.com/ScoopInstaller/Nonportable",
"java": "https://github.com/ScoopInstaller/Java",
"games": "https://github.com/Calinou/scoop-games"
}

View File

@@ -1,12 +1,13 @@
# Must included with 'json.ps1'
function find_hash_in_rdf([String] $url, [String] $basename) {
$data = $null
$xml = $null
try {
# Download and parse RDF XML file
$wc = New-Object Net.Webclient
$wc.Headers.Add('Referer', (strip_filename $url))
$wc.Headers.Add('User-Agent', (Get-UserAgent))
[xml]$data = $wc.downloadstring($url)
$data = $wc.DownloadData($url)
[xml]$xml = (Get-Encoding($wc)).GetString($data)
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"
@@ -14,7 +15,7 @@ function find_hash_in_rdf([String] $url, [String] $basename) {
}
# Find file content
$digest = $data.RDF.Content | Where-Object { [String]$_.about -eq $basename }
$digest = $xml.RDF.Content | Where-Object { [String]$_.about -eq $basename }
return format_hash $digest.sha256
}
@@ -35,7 +36,8 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
$wc = New-Object Net.Webclient
$wc.Headers.Add('Referer', (strip_filename $url))
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$hashfile = $wc.downloadstring($url)
$data = $wc.DownloadData($url)
$hashfile = (Get-Encoding($wc)).GetString($data)
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"
@@ -88,7 +90,8 @@ function find_hash_in_json([String] $url, [Hashtable] $substitutions, [String] $
$wc = New-Object Net.Webclient
$wc.Headers.Add('Referer', (strip_filename $url))
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$json = $wc.downloadstring($url)
$data = $wc.DownloadData($url)
$json = (Get-Encoding($wc)).GetString($data)
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"
@@ -108,7 +111,8 @@ function find_hash_in_xml([String] $url, [Hashtable] $substitutions, [String] $x
$wc = New-Object Net.Webclient
$wc.Headers.Add('Referer', (strip_filename $url))
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$xml = [xml]$wc.downloadstring($url)
$data = $wc.DownloadData($url)
$xml = [xml]((Get-Encoding($wc)).GetString($data))
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"

View File

@@ -72,43 +72,11 @@ function buckets {
return Get-LocalBucket
}
function Find-Manifest($app, $bucket) {
$manifest, $url = $null, $null
# check if app is a URL or UNC path
if ($app -match '^(ht|f)tps?://|\\\\') {
$url = $app
$app = appname_from_url $url
$manifest = url_manifest $url
} else {
if ($bucket) {
$manifest = manifest $app $bucket
} else {
foreach ($bucket in Get-LocalBucket) {
$manifest = manifest $app $bucket
if ($manifest) { break }
}
}
if (!$manifest) {
# couldn't find app in buckets: check if it's a local path
$path = $app
if (!$path.endswith('.json')) { $path += '.json' }
if (Test-Path $path) {
$url = "$(Resolve-Path $path)"
$app = appname_from_url $url
$manifest, $bucket = url_manifest $url
}
}
}
return $app, $manifest, $bucket, $url
}
function Convert-RepositoryUri {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0, ValueFromPipeline = $true)]
[AllowEmptyString()]
[String] $Uri
)
@@ -141,7 +109,7 @@ function list_buckets {
Measure-Object | Select-Object -ExpandProperty Count
$buckets += [PSCustomObject]$bucket
}
$buckets
,$buckets
}
function add_bucket($name, $repo) {
@@ -161,10 +129,12 @@ function add_bucket($name, $repo) {
return 1
}
foreach ($bucket in Get-LocalBucket) {
$remote = git -C "$bucketsdir\$bucket" config --get remote.origin.url
if ((Convert-RepositoryUri -Uri $remote) -eq $uni_repo) {
warn "Bucket $bucket already exists for $repo"
return 2
if (Test-Path -Path "$bucketsdir\$bucket\.git") {
$remote = git -C "$bucketsdir\$bucket" config --get remote.origin.url
if ((Convert-RepositoryUri -Uri $remote) -eq $uni_repo) {
warn "Bucket $bucket already exists for $repo"
return 2
}
}
}
@@ -174,11 +144,10 @@ function add_bucket($name, $repo) {
error "'$repo' doesn't look like a valid git repository`n`nError given:`n$out"
return 1
}
Write-Host 'OK'
ensure $bucketsdir | Out-Null
$dir = ensure $dir
git_cmd clone "$repo" "`"$dir`"" -q
Write-Host 'OK'
success "The $name bucket was added successfully."
return 0
}
@@ -195,12 +164,12 @@ function rm_bucket($name) {
}
function new_issue_msg($app, $bucket, $title, $body) {
$app, $manifest, $bucket, $url = Find-Manifest $app $bucket
$app, $manifest, $bucket, $url = Get-Manifest "$bucket/$app"
$url = known_bucket_repo $bucket
$bucket_path = "$bucketsdir\$bucket"
if (Test-Path $bucket_path) {
$remote = Invoke-Expression "git -C '$bucket_path' config --get remote.origin.url"
$remote = git -C "$bucket_path" config --get remote.origin.url
# Support ssh and http syntax
# git@PROVIDER:USER/REPO.git
# https://PROVIDER/USER/REPO.git

View File

@@ -20,7 +20,7 @@ function command_path($cmd) {
$shim_path = "$scoopdir\shims\scoop-$cmd.ps1"
$line = ((Get-Content $shim_path) | Where-Object { $_.startswith('$path') })
if($line) {
Invoke-Expression -command "$line"
Invoke-Command ([scriptblock]::Create($line)) -NoNewScope
$cmd_path = $path
}
else { $cmd_path = $shim_path }

View File

@@ -9,9 +9,17 @@ function Optimize-SecurityProtocol {
# If not, change it to support TLS 1.2
if (!($isNewerNetFramework -and $isSystemDefault)) {
# Set to TLS 1.2 (3072), then TLS 1.1 (768), and TLS 1.0 (192). Ssl3 has been superseded,
# https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype?view=netframework-4.5
[System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192
# Set to TLS 1.2 (3072). Ssl3, TLS 1.0, and 1.1 have been deprecated,
# https://datatracker.ietf.org/doc/html/rfc8996
[System.Net.ServicePointManager]::SecurityProtocol = 3072
}
}
function Get-Encoding($wc) {
if ($null -ne $wc.ResponseHeaders -and $wc.ResponseHeaders['Content-Type'] -match 'charset=([^;]*)') {
return [System.Text.Encoding]::GetEncoding($Matches[1])
} else {
return [System.Text.Encoding]::GetEncoding('utf-8')
}
}
@@ -318,6 +326,39 @@ function Get-HelperPath {
}
}
function Get-CommandPath {
[CmdletBinding()]
[OutputType([String])]
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[String]
$Command
)
begin {
$userShims = Convert-Path (shimdir $false)
$globalShims = fullpath (shimdir $true) # don't resolve: may not exist
}
process {
try {
$comm = Get-Command $Command -ErrorAction Stop
} catch {
return $null
}
$commandPath = if ($comm.Path -like "$userShims*" -or $comm.Path -like "$globalShims*") {
Get-ShimTarget ($comm.Path -replace '\.exe$', '.shim')
} elseif ($comm.CommandType -eq 'Application') {
$comm.Source
} elseif ($comm.CommandType -eq 'Alias') {
Get-CommandPath $comm.ResolvedCommandName
} else {
$null
}
return $commandPath
}
}
function Test-HelperInstalled {
[CmdletBinding()]
param(
@@ -599,6 +640,20 @@ function get_app_name_from_shim($shim) {
return get_app_name $content
}
function Get-ShimTarget($ShimPath) {
if ($ShimPath) {
$shimTarget = if ($ShimPath.EndsWith('.shim')) {
(Get-Content -Path $ShimPath | Select-Object -First 1).Replace('path = ', '').Replace('"', '')
} else {
((Select-String -Path $ShimPath -Pattern '^(?:@rem|#)\s*(.*)$').Matches.Groups | Select-Object -Index 1).Value
}
if (!$shimTarget) {
$shimTarget = ((Select-String -Path $ShimPath -Pattern '[''"]([^@&]*?)[''"]' -AllMatches).Matches.Groups | Select-Object -Last 1).Value
}
$shimTarget | Convert-Path
}
}
function warn_on_overwrite($shim, $path) {
if (!(Test-Path $shim)) {
return
@@ -679,19 +734,11 @@ function shim($path, $global, $name, $arg) {
@(
"@rem $resolved_path",
"@echo off",
"setlocal enabledelayedexpansion",
"set args=%*",
":: replace problem characters in arguments",
"set args=%args:`"='%",
"set args=%args:(=``(%",
"set args=%args:)=``)%",
"set invalid=`"='",
"if !args! == !invalid! ( set args= )",
"where /q pwsh.exe",
"if %errorlevel% equ 0 (",
" pwsh -noprofile -ex unrestricted -file `"$resolved_path`" $arg %args%",
" pwsh -noprofile -ex unrestricted -file `"$resolved_path`" $arg %*",
") else (",
" powershell -noprofile -ex unrestricted -file `"$resolved_path`" $arg %args%",
" powershell -noprofile -ex unrestricted -file `"$resolved_path`" $arg %*",
")"
) -join "`r`n" | Out-UTF8File "$shim.cmd"
@@ -734,7 +781,7 @@ function shim($path, $global, $name, $arg) {
} else {
warn_on_overwrite "$shim.cmd" $path
# find path to Git's bash so that batch scripts can run bash scripts
$gitdir = (Get-Item (Get-Command git -CommandType:Application -ErrorAction:Stop).Source -ErrorAction:Stop).Directory.Parent
$gitdir = (Get-Item (Get-CommandPath git) -ErrorAction:Stop).Directory.Parent
if ($gitdir.FullName -imatch 'mingw') {
$gitdir = $gitdir.Parent
}
@@ -808,7 +855,7 @@ function Confirm-InstallationStatus {
$Global
)
$Installed = @()
$Apps | Select-Object -Unique | Where-Object { $_.Name -ne 'scoop' } | ForEach-Object {
$Apps | Select-Object -Unique | Where-Object { $_ -ne 'scoop' } | ForEach-Object {
$App, $null, $null = parse_app $_
if ($Global) {
if (Test-Path (appdir $App $true)) {
@@ -902,11 +949,12 @@ function applist($apps, $global) {
return ,@($apps | ForEach-Object { ,@($_, $global) })
}
function parse_app([string] $app) {
if($app -match '(?:(?<bucket>[a-zA-Z0-9-]+)\/)?(?<app>.*\.json$|[a-zA-Z0-9-_.]+)(?:@(?<version>.*))?') {
return $matches['app'], $matches['bucket'], $matches['version']
function parse_app([string]$app) {
if ($app -match '^(?:(?<bucket>[a-zA-Z0-9-_.]+)/)?(?<app>.*\.json$|[a-zA-Z0-9-_.]+)(?:@(?<version>.*))?$') {
return $Matches['app'], $Matches['bucket'], $Matches['version']
} else {
return $app, $null, $null
}
return $app, $null, $null
}
function show_app($app, $bucket, $version) {
@@ -1109,19 +1157,6 @@ function Out-UTF8File {
# for all communication with api.github.com
Optimize-SecurityProtocol
# Scoop root directory
$scoopdir = $env:SCOOP, (get_config 'rootPath'), "$env:USERPROFILE\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
# Scoop global apps directory
$globaldir = $env:SCOOP_GLOBAL, (get_config 'globalPath'), "$env:ProgramData\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -first 1
# Scoop cache directory
# Note: Setting the SCOOP_CACHE environment variable to use a shared directory
# is experimental and untested. There may be concurrency issues when
# multiple users write and access cached files at the same time.
# Use at your own risk.
$cachedir = $env:SCOOP_CACHE, (get_config 'cachePath'), "$scoopdir\cache" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -first 1
# Scoop config file migration
$configHome = $env:XDG_CONFIG_HOME, "$env:USERPROFILE\.config" | Select-Object -First 1
$configFile = "$configHome\scoop\config.json"
@@ -1135,5 +1170,18 @@ if ((Test-Path "$env:USERPROFILE\.scoop") -and !(Test-Path $configFile)) {
# Load Scoop config
$scoopConfig = load_cfg $configFile
# Scoop root directory
$scoopdir = $env:SCOOP, (get_config 'rootPath'), "$env:USERPROFILE\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
# Scoop global apps directory
$globaldir = $env:SCOOP_GLOBAL, (get_config 'globalPath'), "$env:ProgramData\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -first 1
# Scoop cache directory
# Note: Setting the SCOOP_CACHE environment variable to use a shared directory
# is experimental and untested. There may be concurrency issues when
# multiple users write and access cached files at the same time.
# Use at your own risk.
$cachedir = $env:SCOOP_CACHE, (get_config 'cachePath'), "$scoopdir\cache" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -first 1
# Setup proxy globally
setup_proxy

View File

@@ -64,7 +64,12 @@ function Expand-7zipArchive {
}
if ($Removal) {
# Remove original archive file
Remove-Item $Path -Force
if (($Path -replace '.*\.([^\.]*)$', '$1') -eq '001') {
# Remove splited 7-zip archive parts
Get-ChildItem "$($Path -replace '\.[^\.]*$', '').???" | Remove-Item -Force
} else {
Remove-Item $Path -Force
}
}
}

View File

@@ -32,9 +32,8 @@ function Get-Dependency {
$Unresolved = @()
)
process {
$AppName, $bucket, $null = parse_app $AppName
$AppName, $manifest, $bucket, $url = Get-Manifest $AppName
$Unresolved += $AppName
$null, $manifest, $null, $null = Find-Manifest $AppName $bucket
if (!$manifest) {
if (((Get-LocalBucket) -notcontains $bucket) -and $bucket) {
@@ -58,7 +57,11 @@ function Get-Dependency {
if ($bucket) {
$Resolved += "$bucket/$AppName"
} else {
$Resolved += $AppName
if ($url) {
$Resolved += $url
} else {
$Resolved += $AppName
}
}
if ($Unresolved.Length -eq 0) {
return $Resolved
@@ -141,7 +144,7 @@ function Test-7zipRequirement {
$Uri
)
return ($Uri | Where-Object {
$_ -match '\.((gz)|(tar)|(t[abgpx]z2?)|(lzma)|(bz2?)|(7z)|(rar)|(iso)|(xz)|(lzh)|(nupkg))(\.[^.]+)?$'
$_ -match '\.((gz)|(tar)|(t[abgpx]z2?)|(lzma)|(bz2?)|(7z)|(001)|(rar)|(iso)|(xz)|(lzh)|(nupkg))(\.[^\d.]+)?$'
}).Count -gt 0
}

View File

@@ -18,7 +18,8 @@ function find_description($url, $html, $redir = $false) {
if($refresh -and !$redir) {
$wc = New-Object Net.Webclient
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$html = $wc.downloadstring($refresh)
$data = $wc.DownloadData($refresh)
$html = (Get-Encoding($wc)).GetString($data)
return find_description $refresh $html $true
}

View File

@@ -7,8 +7,7 @@ function nightly_version($date, $quiet = $false) {
}
function install_app($app, $architecture, $global, $suggested, $use_cache = $true, $check_hash = $true) {
$app, $bucket, $null = parse_app $app
$app, $manifest, $bucket, $url = Find-Manifest $app $bucket
$app, $manifest, $bucket, $url = Get-Manifest $app
if(!$manifest) {
abort "Couldn't find manifest for '$app'$(if($url) { " at the URL $url" })."
@@ -51,7 +50,8 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
$persist_dir = persistdir $app $global
$fname = dl_urls $app $version $manifest $bucket $architecture $dir $use_cache $check_hash
pre_install $manifest $architecture
Invoke-HookScript -HookType 'pre_install' -Manifest $manifest -Arch $architecture
run_installer $fname $manifest $architecture $dir $global
ensure_install_dir_not_in_path $dir $global
$dir = link_current $dir
@@ -65,7 +65,7 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
persist_data $manifest $original_dir $persist_dir
persist_permission $manifest $global
post_install $manifest $architecture
Invoke-HookScript -HookType 'post_install' -Manifest $manifest -Arch $architecture
# save info for uninstall
save_installed_manifest $app $bucket $dir $url
@@ -269,7 +269,7 @@ function dl_with_cache_aria2($app, $version, $manifest, $architecture, $dir, $co
$oriConsoleEncoding = [Console]::OutputEncoding
[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding
Invoke-Expression $aria2 | ForEach-Object {
Invoke-Command ([scriptblock]::Create($aria2)) | ForEach-Object {
# Skip blank lines
if ([String]::IsNullOrWhiteSpace($_)) { return }
@@ -718,7 +718,7 @@ function run_installer($fname, $manifest, $architecture, $dir, $global) {
$installer = installer $manifest $architecture
if($installer.script) {
write-output "Running installer script..."
Invoke-Expression (@($installer.script) -join "`r`n")
Invoke-Command ([scriptblock]::Create($installer.script -join "`r`n"))
return
}
@@ -798,7 +798,7 @@ function run_uninstaller($manifest, $architecture, $dir) {
$version = $manifest.version
if($uninstaller.script) {
write-output "Running uninstaller script..."
Invoke-Expression (@($uninstaller.script) -join "`r`n")
Invoke-Command ([scriptblock]::Create($uninstaller.script -join "`r`n"))
return
}
@@ -1031,19 +1031,25 @@ function env_rm($manifest, $global, $arch) {
}
}
function pre_install($manifest, $arch) {
$pre_install = arch_specific 'pre_install' $manifest $arch
if($pre_install) {
write-output "Running pre-install script..."
Invoke-Expression (@($pre_install) -join "`r`n")
}
}
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')]
[String] $Arch
)
function post_install($manifest, $arch) {
$post_install = arch_specific 'post_install' $manifest $arch
if($post_install) {
write-output "Running post-install script..."
Invoke-Expression (@($post_install) -join "`r`n")
$script = arch_specific $HookType $Manifest $Arch
if ($script) {
Write-Output "Running $HookType script..."
Invoke-Command ([scriptblock]::Create($script -join "`r`n"))
}
}

View File

@@ -12,7 +12,8 @@ function url_manifest($url) {
try {
$wc = New-Object Net.Webclient
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$str = $wc.downloadstring($url)
$data = $wc.DownloadData($url)
$str = (Get-Encoding($wc)).GetString($data)
} catch [system.management.automation.methodinvocationexception] {
warn "error: $($_.exception.innerexception.message)"
} catch {
@@ -22,16 +23,54 @@ function url_manifest($url) {
$str | convertfrom-json
}
function Get-Manifest($app) {
$bucket, $manifest, $url = $null
$app = $app.TrimStart('/')
# check if app is a URL or UNC path
if ($app -match '^(ht|f)tps?://|\\\\') {
$url = $app
$app = appname_from_url $url
$manifest = url_manifest $url
} else {
$app, $bucket, $version = parse_app $app
if ($bucket) {
$manifest = manifest $app $bucket
} else {
foreach ($bucket in Get-LocalBucket) {
$manifest = manifest $app $bucket
if ($manifest) {
break
}
}
}
if (!$manifest) {
# couldn't find app in buckets: check if it's a local path
$appPath = $app
$bucket = $null
if (!$appPath.EndsWith('.json')) {
$appPath += '.json'
}
if (Test-Path $appPath) {
$url = Convert-Path $appPath
$app = appname_from_url $url
$manifest = url_manifest $url
}
}
}
return $app, $manifest, $bucket, $url
}
function manifest($app, $bucket, $url) {
if($url) { return url_manifest $url }
if ($url) { return url_manifest $url }
parse_json (manifest_path $app $bucket)
}
function save_installed_manifest($app, $bucket, $dir, $url) {
if($url) {
if ($url) {
$wc = New-Object Net.Webclient
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$wc.downloadstring($url) > "$dir\manifest.json"
$data = $wc.DownloadData($url)
(Get-Encoding($wc)).GetString($data) | Out-UTF8File "$dir\manifest.json"
} else {
Copy-Item (manifest_path $app $bucket) "$dir\manifest.json"
}
@@ -51,7 +90,7 @@ function save_install_info($info, $dir) {
function install_info($app, $version, $global) {
$path = "$(versiondir $app $version $global)\install.json"
if(!(test-path $path)) { return $null }
if (!(Test-Path $path)) { return $null }
parse_json $path
}
@@ -73,20 +112,21 @@ function default_architecture {
}
function arch_specific($prop, $manifest, $architecture) {
if($manifest.architecture) {
if ($manifest.architecture) {
$val = $manifest.architecture.$architecture.$prop
if($val) { return $val } # else fallback to generic prop
if ($val) { return $val } # else fallback to generic prop
}
if($manifest.$prop) { return $manifest.$prop }
if ($manifest.$prop) { return $manifest.$prop }
}
function supports_architecture($manifest, $architecture) {
return -not [String]::IsNullOrEmpty((arch_specific 'url' $manifest $architecture))
}
function generate_user_manifest($app, $bucket, $version) { # 'autoupdate.ps1' 'buckets.ps1' 'manifest.ps1'
$null, $manifest, $bucket, $null = Find-Manifest $app $bucket
function generate_user_manifest($app, $bucket, $version) {
# 'autoupdate.ps1' 'buckets.ps1' 'manifest.ps1'
$app, $manifest, $bucket, $null = Get-Manifest "$bucket/$app"
if ("$($manifest.version)" -eq "$version") {
return manifest_path $app $bucket
}

View File

@@ -4,12 +4,11 @@
param($app)
. "$PSScriptRoot\..\lib\json.ps1" # 'ConvertToPrettyJson'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Find-Manifest' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
if (!$app) { error '<app> missing'; my_usage; exit 1 }
$app, $bucket, $null = parse_app $app
$app, $manifest, $bucket, $url = Find-Manifest $app $bucket
$null, $manifest, $bucket, $url = Get-Manifest $app
if ($manifest) {
$style = get_config cat_style

View File

@@ -18,7 +18,7 @@
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' (indirectly)
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture' 'generate_user_manifest' 'Find-Manifest' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture' 'generate_user_manifest' 'Get-Manifest'
. "$PSScriptRoot\..\lib\install.ps1"
$opt, $apps, $err = getopt $args 'fhua:' 'force', 'no-hash-check', 'no-update-scoop', 'arch='
@@ -51,7 +51,7 @@ foreach ($curr_app in $apps) {
$bucket = $version = $app = $manifest = $url = $null
$app, $bucket, $version = parse_app $curr_app
$app, $manifest, $bucket, $url = Find-Manifest $app $bucket
$app, $manifest, $bucket, $url = Get-Manifest "$bucket/$app"
info "Starting download for $app..."

View File

@@ -1,23 +1,43 @@
# Usage: scoop hold <apps>
# Summary: Hold an app to disable updates
# Help: To hold a user-scoped app:
# scoop hold <app>
#
# To hold a global app:
# scoop hold -g <app>
#
# Options:
# -g, --global Hold globally installed apps
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1" # 'save_install_info' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'install_info' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
$apps = $args
$opt, $apps, $err = getopt $args 'g' 'global'
if ($err) { "scoop hold: $err"; exit 1 }
if(!$apps) {
$global = $opt.g -or $opt.global
if (!$apps) {
my_usage
exit 1
}
if ($global -and !(is_admin)) {
error 'You need admin rights to hold a global app.'
exit 1
}
$apps | ForEach-Object {
$app = $_
$global = installed $app $true
if (!(installed $app)) {
error "'$app' is not installed."
if (!(installed $app $global)) {
if ($global) {
error "'$app' is not installed globally."
} else {
error "'$app' is not installed."
}
return
}
@@ -29,7 +49,7 @@ $apps | ForEach-Object {
$dir = versiondir $app $version $global
$json = install_info $app $version $global
$install = @{}
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name))}
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }
$install.hold = $true
save_install_info $install $dir
success "$app is now held and can not be updated anymore."

View File

@@ -2,10 +2,10 @@
# Summary: Opens the app homepage
param($app)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Find-Manifest' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
if ($app) {
$null, $manifest, $bucket, $null = Find-Manifest $app
$null, $manifest, $bucket, $null = Get-Manifest $app
if ($manifest) {
if ($manifest.homepage) {
Start-Process $manifest.homepage

View File

@@ -4,7 +4,7 @@
# -v, --verbose Show full paths and URLs
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Find-Manifest' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
. "$PSScriptRoot\..\lib\versions.ps1" # 'Get-InstalledVersion'
$opt, $app, $err = getopt $args 'v' 'verbose'
@@ -13,31 +13,21 @@ $verbose = $opt.v -or $opt.verbose
if (!$app) { my_usage; exit 1 }
if ($app -match '^(ht|f)tps?://|\\\\') {
# check if $app is a URL or UNC path
$url = $app
$app = appname_from_url $url
$global = installed $app $true
$status = app_status $app $global
$manifest = url_manifest $url
$manifest_file = $url
} else {
# else $app is a normal app name
$global = installed $app $true
$app, $bucket, $null = parse_app $app
$status = app_status $app $global
$app, $manifest, $bucket, $url = Find-Manifest $app $bucket
}
$app, $manifest, $bucket, $url = Get-Manifest $app
if (!$manifest) {
abort "Could not find manifest for '$(show_app $app $bucket)'."
abort "Could not find manifest for '$(show_app $app)' in local buckets."
}
$global = installed $app $true
$status = app_status $app $global
$install = install_info $app $status.version $global
$status.installed = $bucket -and $install.bucket -eq $bucket
$version_output = $manifest.version
if (!$manifest_file) {
$manifest_file = if ($bucket) { manifest_path $app $bucket } else { $url }
$manifest_file = if ($bucket) {
manifest_path $app $bucket
} else {
$url
}
if ($verbose) {

View File

@@ -20,7 +20,7 @@
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' 'manifest.ps1' (indirectly)
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture' 'generate_user_manifest' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture' 'generate_user_manifest' 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\decompress.ps1"
. "$PSScriptRoot\..\lib\shortcuts.ps1"

View File

@@ -63,7 +63,7 @@ function search_remote($bucket, $query) {
$repo_name = $Matches[2]
$api_link = "https://api.github.com/repos/$user/$repo_name/git/trees/HEAD?recursive=1"
$result = download_json $api_link | Select-Object -ExpandProperty tree |
Where-Object -Value "^(?:bucket/)?(.*$query.*)\.json$" -Property Path -Match |
Where-Object -Value "^bucket/(.*$query.*)\.json$" -Property Path -Match |
ForEach-Object { $Matches[1] }
}

View File

@@ -108,20 +108,6 @@ function Get-ShimPath($ShimName, $Global) {
}
}
function Get-ShimTarget($ShimPath) {
if ($ShimPath) {
$shimTarget = if ($ShimPath.EndsWith('.shim')) {
(Get-Content -Path $ShimPath | Select-Object -First 1).Replace('path = ', '').Replace('"', '')
} else {
((Select-String -Path $ShimPath -Pattern '^(?:@rem|#)\s*(.*)$').Matches.Groups | Select-Object -Index 1).Value
}
if (!$shimTarget) {
$shimTarget = ((Select-String -Path $ShimPath -Pattern '[''"]([^@&]*?)[''"]' -AllMatches).Matches.Groups | Select-Object -Last 1).Value
}
$shimTarget | Convert-Path
}
}
switch ($SubCommand) {
'add' {
if ($commandPath -notmatch '[\\/]') {

View File

@@ -1,23 +1,43 @@
# Usage: scoop unhold <app>
# Summary: Unhold an app to enable updates
# Help: To unhold a user-scoped app:
# scoop unhold <app>
#
# To unhold a global app:
# scoop unhold -g <app>
#
# Options:
# -g, --global Unhold globally installed apps
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1" # 'save_install_info' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'install_info' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
$apps = $args
$opt, $apps, $err = getopt $args 'g' 'global'
if ($err) { "scoop unhold: $err"; exit 1 }
if(!$apps) {
$global = $opt.g -or $opt.global
if (!$apps) {
my_usage
exit 1
}
if ($global -and !(is_admin)) {
error 'You need admin rights to unhold a global app.'
exit 1
}
$apps | ForEach-Object {
$app = $_
$global = installed $app $true
if (!(installed $app)) {
error "'$app' is not installed."
if (!(installed $app $global)) {
if ($global) {
error "'$app' is not installed globally."
} else {
error "'$app' is not installed."
}
return
}
@@ -29,7 +49,7 @@ $apps | ForEach-Object {
$dir = versiondir $app $version $global
$json = install_info $app $version $global
$install = @{}
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name))}
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }
$install.hold = $null
save_install_info $install $dir
success "$app is no longer held and can be updated again."

View File

@@ -7,7 +7,7 @@
# -p, --purge Remove all persistent data
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\shortcuts.ps1"
. "$PSScriptRoot\..\lib\psmodules.ps1"
@@ -54,6 +54,12 @@ if (!$apps) { exit 0 }
$dir = versiondir $app $version $global
$persist_dir = persistdir $app $global
$manifest = installed_manifest $app $version $global
$install = install_info $app $version $global
$architecture = $install.architecture
Invoke-HookScript -HookType 'pre_uninstall' -Manifest $manifest -Arch $architecture
#region Workaround for #2952
if (test_running_process $app $global) {
continue
@@ -67,10 +73,6 @@ if (!$apps) { exit 0 }
continue
}
$manifest = installed_manifest $app $version $global
$install = install_info $app $version $global
$architecture = $install.architecture
run_uninstaller $manifest $architecture $dir
rm_shims $app $manifest $global $architecture
rm_startmenu_shortcuts $manifest $global $architecture
@@ -95,6 +97,8 @@ if (!$apps) { exit 0 }
continue
}
}
Invoke-HookScript -HookType 'post_uninstall' -Manifest $manifest -Arch $architecture
}
# remove older versions
$oldVersions = @(Get-ChildItem $appDir -Name -Exclude 'current')

View File

@@ -66,49 +66,60 @@ function update_scoop() {
$show_update_log = get_config 'show_update_log' $true
$currentdir = fullpath $(versiondir 'scoop' 'current')
if (!(Test-Path "$currentdir\.git")) {
$newdir = fullpath $(versiondir 'scoop' 'new')
$newdir = "$currentdir\..\new"
$olddir = "$currentdir\..\old"
# get git scoop
git_cmd clone -q $configRepo --branch $configBranch --single-branch "`"$newdir`""
# check if scoop was successful downloaded
if (!(Test-Path "$newdir")) {
abort 'Scoop update failed.'
if (!(Test-Path "$newdir\bin\scoop.ps1")) {
Remove-Item $newdir -Force -Recurse
abort "Scoop download failed. If this appears several times, try removing SCOOP_REPO by 'scoop config rm SCOOP_REPO'"
} else {
# replace non-git scoop with the git version
try {
Rename-Item $currentdir 'old' -ErrorAction Stop
Rename-Item $newdir 'current' -ErrorAction Stop
} catch {
Write-Warning $_
abort "Scoop update failed. Folder in use. Paste $newdir into $currentdir."
}
}
} else {
if (Test-Path "$currentdir\..\old") {
Remove-Item "$currentdir\..\old" -Recurse -Force -ErrorAction SilentlyContinue
}
# replace non-git scoop with the git version
Remove-Item -r -force $currentdir -ea stop
Move-Item $newdir $currentdir
} else {
$previousCommit = Invoke-Expression "git -C '$currentdir' rev-parse HEAD"
$currentRepo = Invoke-Expression "git -C '$currentdir' config remote.origin.url"
$currentBranch = Invoke-Expression "git -C '$currentdir' branch"
$previousCommit = git -C "$currentdir" rev-parse HEAD
$currentRepo = git -C "$currentdir" config remote.origin.url
$currentBranch = git -C "$currentdir" branch
$isRepoChanged = !($currentRepo -match $configRepo)
$isBranchChanged = !($currentBranch -match "\*\s+$configBranch")
# Change remote url if the repo is changed
if ($isRepoChanged) {
Invoke-Expression "git -C '$currentdir' config remote.origin.url '$configRepo'"
git -C "$currentdir" config remote.origin.url "$configRepo"
}
# Fetch and reset local repo if the repo or the branch is changed
if ($isRepoChanged -or $isBranchChanged) {
# Reset git fetch refs, so that it can fetch all branches (GH-3368)
Invoke-Expression "git -C '$currentdir' config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'"
git -C "$currentdir" config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
# fetch remote branch
git_cmd -C "`"$currentdir`"" fetch --force origin "refs/heads/`"$configBranch`":refs/remotes/origin/$configBranch" -q
# checkout and track the branch
git_cmd -C "`"$currentdir`"" checkout -B $configBranch -t origin/$configBranch -q
# reset branch HEAD
Invoke-Expression "git -C '$currentdir' reset --hard origin/$configBranch -q"
git -C "$currentdir" reset --hard origin/$configBranch -q
} else {
git_cmd -C "`"$currentdir`"" pull -q
}
$res = $lastexitcode
if ($show_update_log) {
Invoke-Expression "git -C '$currentdir' --no-pager log --no-decorate --grep='^(chore)' --invert-grep --format='tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset' '$previousCommit..HEAD'"
git -C "$currentdir" --no-pager log --no-decorate --grep='^(chore)' --invert-grep --format='tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset' "$previousCommit..HEAD"
}
if ($res -ne 0) {
@@ -133,19 +144,25 @@ function update_scoop() {
if (!(Test-Path (Join-Path $bucketLoc '.git'))) {
if ($bucket -eq 'main') {
# Make sure main bucket, which was downloaded as zip, will be properly "converted" into git
Write-Host " Converting 'main' bucket to git..."
rm_bucket 'main'
add_bucket 'main'
Write-Host " Converting 'main' bucket to git repo..."
$status = rm_bucket 'main'
if ($status -ne 0) {
abort "Failed to remove local 'main' bucket."
}
$status = add_bucket 'main' (known_bucket_repo 'main')
if ($status -ne 0) {
abort "Failed to add remote 'main' bucket."
}
} else {
Write-Host "'$bucket' is not a git repository. Skipped."
}
continue
}
$previousCommit = (Invoke-Expression "git -C '$bucketLoc' rev-parse HEAD")
$previousCommit = git -C "$bucketLoc" rev-parse HEAD
git_cmd -C "`"$bucketLoc`"" pull -q
if ($show_update_log) {
Invoke-Expression "git -C '$bucketLoc' --no-pager log --no-decorate --grep='^(chore)' --invert-grep --format='tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset' '$previousCommit..HEAD'"
git -C "$bucketLoc" --no-pager log --no-decorate --grep='^(chore)' --invert-grep --format='tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset' "$previousCommit..HEAD"
}
}
@@ -289,11 +306,14 @@ if (-not ($apps -or $all)) {
'ERROR: You need admin rights to update global apps.'; exit 1
}
if (is_scoop_outdated) {
$outdated = @()
$updateScoop = $null -ne ($apps | Where-Object { $_ -eq 'scoop' }) -or (is_scoop_outdated)
$apps = $apps | Where-Object { $_ -ne 'scoop' }
$apps_param = $apps
if ($updateScoop) {
update_scoop
}
$outdated = @()
$apps_param = $apps
if ($apps_param -eq '*' -or $all) {
$apps = applist (installed_apps $false) $false
@@ -301,7 +321,9 @@ if (-not ($apps -or $all)) {
$apps += applist (installed_apps $true) $true
}
} else {
$apps = Confirm-InstallationStatus $apps_param -Global:$global
if ($apps_param) {
$apps = Confirm-InstallationStatus $apps_param -Global:$global
}
}
if ($apps) {
$apps | ForEach-Object {

View File

@@ -1,28 +1,21 @@
# Usage: scoop virustotal [* | app1 app2 ...] [options]
# Summary: Look for app's hash on virustotal.com
# Help: Look for app's hash (MD5, SHA1 or SHA256) on virustotal.com
# Summary: Look for app's hash or url on virustotal.com
# Help: Look for app's hash or url on virustotal.com
#
# Use a single '*' for app to check all installed apps.
#
# The download's hash is also a key to access VirusTotal's scan results.
# This allows to check the safety of the files without even downloading
# them in many cases. If the hash is unknown to VirusTotal, the
# download link is printed to submit it to VirusTotal.
#
# If you have signed up to VirusTotal's community, you have an API key
# that this script can use to submit unknown packages for inspection
# if you use the `--scan' flag. Tell scoop about your API key with:
# To use this command, you have to sign up to VirusTotal's community,
# and get an API key. Then, tell scoop about your API key with:
#
# scoop config virustotal_api_key <your API key: 64 lower case hex digits>
#
# Exit codes:
# 0 -> success
# 1 -> problem parsing arguments
# 2 -> at least one package was marked unsafe by VirusTotal
# 4 -> at least one exception was raised while looking for info
# 8 -> at least one package couldn't be queried because its hash type
# isn't supported by VirusTotal, the manifest couldn't be found
# or didn't contain a hash
# 0 -> success
# 1 -> problem parsing arguments
# 2 -> at least one package was marked unsafe by VirusTotal
# 4 -> at least one exception was raised while looking for info
# 8 -> at least one package couldn't be queried because the manifest couldn't be found
# 16 -> VirusTotal API key is not configured
# Note: the exit codes (2, 4 & 8) may be combined, e.g. 6 -> exit codes
# 2 & 4 combined
#
@@ -33,14 +26,15 @@
# your virustotal_api_key.
# -n, --no-depends By default, all dependencies are checked too. This flag avoids it.
# -u, --no-update-scoop Don't update Scoop before checking if it's outdated
# -p, --passthru Return reports as objects
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Find-Manifest' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
. "$PSScriptRoot\..\lib\json.ps1" # 'json_path'
. "$PSScriptRoot\..\lib\install.ps1" # 'hash_for_url'
. "$PSScriptRoot\..\lib\depends.ps1" # 'Get-Dependency'
$opt, $apps, $err = getopt $args 'a:snu' @('arch=', 'scan', 'no-depends', 'no-update-scoop')
$opt, $apps, $err = getopt $args 'a:snup' @('arch=', 'scan', 'no-depends', 'no-update-scoop', 'passthru')
if ($err) { "scoop virustotal: $err"; exit 1 }
if (!$apps) { my_usage; exit 1 }
$architecture = ensure_architecture ($opt.a + $opt.arch)
@@ -67,11 +61,17 @@ if (!$opt.n -and !$opt.'no-depends') {
$_ERR_UNSAFE = 2
$_ERR_EXCEPTION = 4
$_ERR_NO_INFO = 8
$_ERR_NO_API_KEY = 16
$exit_code = 0
# Global flag to warn only once about missing API key:
$warned_no_api_key = $False
# Global API key:
$api_key = get_config virustotal_api_key
if (!$api_key) {
abort ("VirusTotal API key is not configured`n" +
" You could get one from https://www.virustotal.com/gui/my-apikey and set with`n" +
" scoop config virustotal_api_key <API key>") $_ERR_NO_API_KEY
}
# Global flag to explain only once about sleep between requests
$explained_rate_limit_sleeping = $False
@@ -80,65 +80,132 @@ $explained_rate_limit_sleeping = $False
# script execution progresses
$requests = 0
Function Get-VirusTotalResult($hash, $app) {
$hash = $hash.ToLower()
$url = "https://www.virustotal.com/ui/files/$hash"
$wc = New-Object Net.Webclient
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$result = $wc.downloadstring($url)
$stats = json_path $result '$.data.attributes.last_analysis_stats'
$malicious = json_path $stats '$.malicious'
$suspicious = json_path $stats '$.suspicious'
$undetected = json_path $stats '$.undetected'
$unsafe = [int]$malicious + [int]$suspicious
$see_url = "see https://www.virustotal.com/#/file/$hash/detection"
switch ($unsafe) {
0 { if ($undetected -eq 0) { $fg = 'Yellow' } else { $fg = 'DarkGreen' } }
1 { $fg = 'DarkYellow' }
2 { $fg = 'Yellow' }
default { $fg = 'Red' }
}
Write-Host -f $fg "$app`: $unsafe/$undetected, $see_url"
if ($unsafe -gt 0) {
return $_ERR_UNSAFE
}
return 0
Function ConvertTo-VirusTotalUrlId ($url) {
$url_id = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($url))
$url_id = $url_id -replace '\+', '-'
$url_id = $url_id -replace '/', '_'
$url_id = $url_id -replace '=', ''
$url_id
}
Function Search-VirusTotal ($hash, $app) {
if ($hash -match '(?<algo>[^:]+):(?<hash>.*)') {
$hash = $matches['hash']
if ($matches['algo'] -match '(md5|sha1|sha256)') {
return Get-VirusTotalResult $hash $app
} else {
warn "$app`: Unsupported hash $($matches['algo']). VirusTotal needs md5, sha1 or sha256."
return $_ERR_NO_INFO
Function Get-RemoteFileSize ($url) {
$response = Invoke-WebRequest -Uri $url -Method HEAD -UseBasicParsing
$response.Headers.'Content-Length' | ForEach-Object { [System.Convert]::ToInt32($_) }
}
Function Get-VirusTotalResultByHash ($hash, $url, $app) {
$hash = $hash.ToLower()
$api_url = "https://www.virustotal.com/api/v3/files/$hash"
$headers = @{}
$headers.Add('Accept', 'application/json')
$headers.Add('x-apikey', $api_key)
$response = Invoke-WebRequest -Uri $api_url -Method GET -Headers $headers -UseBasicParsing
$result = $response.Content
$stats = json_path $result '$.data.attributes.last_analysis_stats'
[int]$malicious = json_path $stats '$.malicious'
[int]$suspicious = json_path $stats '$.suspicious'
[int]$timeout = json_path $stats '$.timeout'
[int]$undetected = json_path $stats '$.undetected'
[int]$unsafe = $malicious + $suspicious
[int]$total = $unsafe + $undetected
[int]$fileSize = json_path $result '$.data.attributes.size'
$report_hash = json_path $result '$.data.attributes.sha256'
$report_url = "https://www.virustotal.com/gui/file/$report_hash"
if ($total -eq 0) {
info "$app`: Analysis in progress."
[PSCustomObject] @{
'App.Name' = $app
'App.Url' = $url
'App.Hash' = $hash
'App.HashType' = $null
'App.Size' = filesize $fileSize
'FileReport.Url' = $report_url
'FileReport.Hash' = $report_hash
'UrlReport.Url' = $null
}
} else {
$vendorResults = (ConvertFrom-Json((json_path $result '$.data.attributes.last_analysis_results'))).PSObject.Properties.Value
switch ($unsafe) {
0 {
success "$app`: $unsafe/$total, see $report_url"
}
1 {
warn "$app`: $unsafe/$total, see $report_url"
}
2 {
warn "$app`: $unsafe/$total, see $report_url"
}
Default {
warn "`e[31m$app`: $unsafe/$total, see $report_url`e[0m"
}
}
$maliciousResults = $vendorResults |
Where-Object -Property category -EQ 'malicious' |
Select-Object -ExpandProperty engine_name
$suspiciousResults = $vendorResults |
Where-Object -Property category -EQ 'suspicious' |
Select-Object -ExpandProperty engine_name
[PSCustomObject] @{
'App.Name' = $app
'App.Url' = $url
'App.Hash' = $hash
'App.HashType' = $null
'App.Size' = filesize $fileSize
'FileReport.Url' = $report_url
'FileReport.Hash' = $report_hash
'FileReport.Malicious' = if ($maliciousResults) { $maliciousResults } else { 0 }
'FileReport.Suspicious' = if ($suspiciousResults) { $suspiciousResults } else { 0 }
'FileReport.Timeout' = $timeout
'FileReport.Undetected' = $undetected
'UrlReport.Url' = $null
}
}
return Get-VirusTotalResult $hash $app
if ($unsafe -gt 0) {
$Script:exit_code = $exit_code -bor $_ERR_UNSAFE
}
}
Function Submit-RedirectedUrl {
# Follow up to one level of HTTP redirection
#
# Copied from http://www.powershellmagazine.com/2013/01/29/pstip-retrieve-a-redirected-url/
# Adapted according to Roy's response (January 23, 2014 at 11:59 am)
# Adapted to always return an URL
Param (
[Parameter(Mandatory = $true)]
[String]$URL
)
$request = [System.Net.WebRequest]::Create($url)
$request.AllowAutoRedirect = $false
$response = $request.GetResponse()
if (([int]$response.StatusCode -ge 300) -and ([int]$response.StatusCode -lt 400)) {
$redir = $response.GetResponseHeader('Location')
Function Get-VirusTotalResultByUrl ($url, $app) {
$id = ConvertTo-VirusTotalUrlId $url
$api_url = "https://www.virustotal.com/api/v3/urls/$id"
$headers = @{}
$headers.Add('Accept', 'application/json')
$headers.Add('x-apikey', $api_key)
$response = Invoke-WebRequest -Uri $api_url -Method GET -Headers $headers -UseBasicParsing
$result = $response.Content
$id = json_path $result '$.data.id'
$hash = json_path $result '$.data.attributes.last_http_response_content_sha256' 6>$null
$last_analysis_date = json_path $result '$.data.attributes.last_analysis_date' 6>$null
$url_report_url = "https://www.virustotal.com/gui/url/$id"
info "$app`: Url report found."
if (!$hash) {
if (!$last_analysis_date) {
info "$app`: Analysis in progress."
} else {
info "$app`: Related file report not found."
warn "$app`: Manual file upload is required (instead of url submission)."
}
[PSCustomObject] @{
'App.Name' = $app
'App.Url' = $url
'App.Hash' = $null
'App.HashType' = $null
'FileReport.Url' = $null
'UrlReport.Url' = $url_report_url
'UrlReport.Hash' = $null
}
} else {
$redir = $URL
info "$app`: Related file report found."
[PSCustomObject] @{
'App.Name' = $app
'App.Url' = $url
'App.Hash' = $null
'App.HashType' = $null
'FileReport.Url' = $null
'UrlReport.Url' = $url_report_url
'UrlReport.Hash' = $hash
}
}
$response.Close()
return $redir
}
# Submit-ToVirusTotal
@@ -152,32 +219,38 @@ Function Submit-RedirectedUrl {
# exceeded, without risking an infinite loop (as stack
# overflow) if the submission keeps failing.
Function Submit-ToVirusTotal ($url, $app, $do_scan, $retrying = $False) {
$api_key = get_config virustotal_api_key
if ($do_scan -and !$api_key -and !$warned_no_api_key) {
$warned_no_api_key = $true
info "Submitting unknown apps needs a VirusTotal API key. " +
"Set it up with`n`tscoop config virustotal_api_key <API key>"
}
if (!$do_scan -or !$api_key) {
warn "$app`: not found`: manually submit $url"
if (!$do_scan) {
warn "$app`: not found`: you can manually submit $url"
return
}
try {
# Follow redirections (for e.g. sourceforge URLs) because
# VirusTotal analyzes only "direct" download links
$url = $url.Split("#").GetValue(0)
$new_redir = $url
do {
$orig_redir = $new_redir
$new_redir = Submit-RedirectedUrl $orig_redir
} while ($orig_redir -ne $new_redir)
$requests += 1
$result = Invoke-WebRequest -Uri "https://www.virustotal.com/vtapi/v2/url/scan" -Body @{apikey=$api_key;url=$new_redir} -Method Post -UseBasicParsing
$submitted = $result.StatusCode -eq 200
if ($submitted) {
warn "$app`: not found`: submitted $url"
$encoded_url = [System.Web.HttpUtility]::UrlEncode($url)
$api_url = 'https://www.virustotal.com/api/v3/urls'
$content_type = 'application/x-www-form-urlencoded'
$headers = @{}
$headers.Add('Accept', 'application/json')
$headers.Add('x-apikey', $api_key)
$headers.Add('Content-Type', $content_type)
$body = "url=$encoded_url"
$result = Invoke-WebRequest -Uri $api_url -Method POST -Headers $headers -ContentType $content_type -Body $body -UseBasicParsing
if ($result.StatusCode -eq 200) {
$id = ((json_path $result '$.data.id') -split '-')[1]
$url_report_url = "https://www.virustotal.com/gui/url/$id"
$fileSize = Get-RemoteFileSize $url
if ($fileSize -gt 80000000) {
info "$app`: Remote file size: $(filesize $fileSize). Large files might require manual file upload instead of url submission."
}
info "$app`: Analysis in progress."
[PSCustomObject] @{
'App.Name' = $app
'App.Url' = $url
'App.Size' = filesize $fileSize
'FileReport.Url' = $null
'UrlReport.Url' = $url_report_url
}
return
}
@@ -188,10 +261,10 @@ Function Submit-ToVirusTotal ($url, $app, $do_scan, $retrying = $False) {
info "Sleeping 60+ seconds between requests due to VirusTotal's 4/min limit"
}
Start-Sleep -s (60 + $requests)
Submit-ToVirusTotal $new_redir $app $do_scan $True
Submit-ToVirusTotal $url $app $do_scan $True
} else {
warn "$app`: VirusTotal submission of $url failed`:`n" +
"`tAPI returned $($result.StatusCode) after retrying"
"`tAPI returned $($result.StatusCode) after retrying"
}
} catch [Exception] {
warn "$app`: VirusTotal submission failed`: $($_.Exception.Message)"
@@ -199,39 +272,120 @@ Function Submit-ToVirusTotal ($url, $app, $do_scan, $retrying = $False) {
}
}
$apps | ForEach-Object {
$reports = $apps | ForEach-Object {
$app = $_
# write-host $app
$null, $manifest, $bucket, $null = Find-Manifest $app
if(!$manifest) {
$null, $manifest, $bucket, $null = Get-Manifest $app
if (!$manifest) {
$exit_code = $exit_code -bor $_ERR_NO_INFO
warn "$app`: manifest not found"
return
}
[int]$index = 0
$urls = script:url $manifest $architecture
$urls | ForEach-Object {
$url = $_
$index++
if ($urls.GetType().IsArray) {
info "$app`: url $index"
}
$hash = hash_for_url $manifest $url $architecture
try {
if($hash) {
$exit_code = $exit_code -bor (Search-VirusTotal $hash $app)
} else {
warn "$app`: Can't find hash for $url"
$isHashUnsupported = $false
if ($hash -match '(?<algo>[^:]+):(?<hash>.*)') {
$algo = $matches.algo
$hash = $matches.hash
if ($matches.algo -inotin 'md5', 'sha1', 'sha256') {
$hash = $null
$isHashUnsupported = $true
warn "$app`: Unsupported hash $($matches.algo). Will search by url instead."
}
} elseif ($hash) {
$algo = 'sha256'
}
if ($hash) {
$file_report = Get-VirusTotalResultByHash $hash $url $app
$file_report.'App.HashType' = $algo
$file_report
return
} elseif (!$isHashUnsupported) {
warn "$app`: Hash not found. Will search by url instead."
}
} catch [Exception] {
$exit_code = $exit_code -bor $_ERR_EXCEPTION
if ($_.Exception.Message -like "*(404)*") {
Submit-ToVirusTotal $url $app ($opt.scan -or $opt.s)
if ($_.Exception.Response.StatusCode -eq 404) {
$file_report_not_found = $true
warn "$app`: File report not found. Will search by url instead."
} else {
if ($_.Exception.Message -match "\(204|429\)") {
abort "$app`: VirusTotal request failed`: $($_.Exception.Message)", $exit_code
if ($_.Exception.Response.StatusCode -in 204, 429) {
abort "$app`: VirusTotal request failed`: $($_.Exception.Message)" $exit_code
}
warn "$app`: VirusTotal request failed`: $($_.Exception.Message)"
return
}
}
try {
$url_report = Get-VirusTotalResultByUrl $url $app
$url_report.'App.Hash' = $hash
$url_report.'App.HashType' = $algo
if ($url_report.'UrlReport.Hash' -and ($file_report_not_found -eq $true) -and $hash) {
if ($algo -eq 'sha256') {
if ($url_report.'UrlReport.Hash' -eq $hash) {
warn "$app`: Manual file upload is required (instead of url submission) for $url"
} else {
error "$app`: Hash not matched for $url"
}
} else {
error "$app`: Hash not matched or manual file upload is required (instead of url submission) for $url"
}
$url_report
return
}
if (!$url_report.'UrlReport.Hash') {
$url_report
return
}
} catch [Exception] {
$exit_code = $exit_code -bor $_ERR_EXCEPTION
if ($_.Exception.Response.StatusCode -eq 404) {
warn "$app`: Url report not found. Will submit $url"
Submit-ToVirusTotal $url $app ($opt.scan -or $opt.s)
return
} else {
if ($_.Exception.Response.StatusCode -in 204, 429) {
abort "$app`: VirusTotal request failed`: $($_.Exception.Message)" $exit_code
}
warn "$app`: VirusTotal request failed`: $($_.Exception.Message)"
return
}
}
try {
$file_report = Get-VirusTotalResultByHash $url_report.'UrlReport.Hash' $url $app
$file_report.'App.Hash' = $hash
$file_report.'App.HashType' = $algo
$file_report.'UrlReport.Url' = $url_report.'UrlReport.Url'
$file_report
warn "$app`: Unable to check hash match for $url"
} catch [Exception] {
$exit_code = $exit_code -bor $_ERR_EXCEPTION
if ($_.Exception.Response.StatusCode -eq 404) {
warn "$app`: File report not found for unknown reason. Manual file upload is required (instead of url submission)."
$url_report
} else {
if ($_.Exception.Response.StatusCode -in 204, 429) {
abort "$app`: VirusTotal request failed`: $($_.Exception.Message)" $exit_code
}
warn "$app`: VirusTotal request failed`: $($_.Exception.Message)"
return
}
}
}
}
if ($opt.p -or $opt.'passthru') {
$reports
}
exit $exit_code

View File

@@ -9,40 +9,12 @@ if (!$command) {
exit 1
}
try {
$gcm = Get-Command "$command" -ErrorAction Stop
} catch {
abort "'$command' not found" 3
}
$path = Get-CommandPath $command
$path = $gcm.Path
$usershims = Convert-Path (shimdir $false)
$globalshims = fullpath (shimdir $true) # don't resolve: may not exist
if ($path -like "$usershims*" -or $path -like "$globalshims*") {
$exepath = if ($path.EndsWith('.exe') -or $path.EndsWith('.shim')) {
(Get-Content ($path -replace '\.exe$', '.shim') | Select-Object -First 1).Replace('path = ', '').Replace('"', '')
} else {
((Select-String -Path $path -Pattern '^(?:@rem|#)\s*(.*)$').Matches.Groups | Select-Object -Index 1).Value
}
if (!$exepath) {
$exepath = ((Select-String -Path $path -Pattern '[''"]([^@&]*?)[''"]' -AllMatches).Matches.Groups | Select-Object -Last 1).Value
}
if (![System.IO.Path]::IsPathRooted($exepath)) {
# Expand relative path
$exepath = Convert-Path $exepath
}
friendly_path $exepath
} elseif ($gcm.CommandType -eq 'Application') {
$gcm.Source
} elseif ($gcm.CommandType -eq 'Alias') {
scoop which $gcm.ResolvedCommandName
} else {
Write-Host 'Not a scoop shim.'
$path
if ($null -eq $path) {
Write-Host "'$command' not found / not a scoop shim."
exit 2
} else {
friendly_path $path
exit 0
}
exit 0

View File

@@ -134,9 +134,15 @@
"post_install": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"post_uninstall": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"pre_install": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"pre_uninstall": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"shortcuts": {
"$ref": "#/definitions/shortcutsArray"
},
@@ -567,9 +573,15 @@
"post_install": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"post_uninstall": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"pre_install": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"pre_uninstall": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"psmodule": {
"additionalProperties": false,
"properties": {

View File

@@ -1,10 +1,11 @@
Param([Switch]$Fast)
Push-Location $PSScriptRoot
. "$PSScriptRoot\..\..\lib\core.ps1"
. "$PSScriptRoot\..\..\lib\install.ps1"
if (!$Fast) {
Write-Host "Install dependencies ..."
Invoke-Expression "$PSScriptRoot\install.ps1"
& "$PSScriptRoot\install.ps1"
}
$output = "$PSScriptRoot\bin"

View File

@@ -1,10 +1,11 @@
Param([Switch]$Fast)
Push-Location $PSScriptRoot
. "$PSScriptRoot\..\..\lib\core.ps1"
. "$PSScriptRoot\..\..\lib\install.ps1"
if (!$Fast) {
Write-Host "Install dependencies ..."
Invoke-Expression "$PSScriptRoot\install.ps1"
& "$PSScriptRoot\install.ps1"
}
$output = "$PSScriptRoot\bin"

View File

@@ -17,7 +17,7 @@ Describe 'add_alias' -Tag 'Scoop' {
$alias_file | Should -Not -Exist
add_alias 'rm' '"hello, world!"'
Invoke-Expression $alias_file | Should -Be 'hello, world!'
& $alias_file | Should -Be 'hello, world!'
}
}

View File

@@ -22,7 +22,7 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
It 'Decompression test cases should exist' {
$testcases = "$working_dir\TestCases.zip"
$testcases | Should -Exist
compute_hash $testcases 'sha256' | Should -Be '3a442e85b466833eeafbd08c57d8f51bf7ff041867ee0bdb7db1f12480b3624a'
compute_hash $testcases 'sha256' | Should -Be '16507166814dbd02be80c14b737eb6b0245c47439ca3ed308b5625d64babecc8'
if (!$isUnix) {
Microsoft.PowerShell.Archive\Expand-Archive $testcases $working_dir
}
@@ -41,6 +41,9 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
$test2 = "$working_dir\7ZipTest2.tgz"
$test3 = "$working_dir\7ZipTest3.tar.bz2"
$test4 = "$working_dir\7ZipTest4.tar.gz"
$test5_1 = "$working_dir\7ZipTest5.7z.001"
$test5_2 = "$working_dir\7ZipTest5.7z.002"
$test5_3 = "$working_dir\7ZipTest5.7z.003"
}
It 'extract normal compressed file' -Skip:$isUnix {
@@ -71,10 +74,24 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
(Get-ChildItem $to).Count | Should -Be 1
}
It 'extract splited 7z archives (.001, .002, ...)' -Skip:$isUnix {
$to = test_extract 'Expand-7zipArchive' $test5_1
$to | Should -Exist
"$to\empty" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix {
$test1 | Should -Exist
test_extract 'Expand-7zipArchive' $test1 $true
$test1 | Should -Not -Exist
$test5_1 | Should -Exist
$test5_2 | Should -Exist
$test5_3 | Should -Exist
test_extract 'Expand-7zipArchive' $test5_1 $true
$test5_1 | Should -Not -Exist
$test5_2 | Should -Not -Exist
$test5_3 | Should -Not -Exist
}
}

View File

@@ -69,30 +69,30 @@ Describe 'Package Dependencies' -Tag 'Scoop' {
BeforeAll {
Mock Test-HelperInstalled { $false }
Mock get_config { $true } -ParameterFilter { $name -eq 'MSIEXTRACT_USE_LESSMSI' }
Mock Find-Manifest { $null, @{}, $null, $null } -ParameterFilter { $AppName -eq 'lessmsi' }
Mock Find-Manifest { $null, @{ url = 'test.msi' }, $null, $null } -ParameterFilter { $AppName -eq '7zip' }
Mock Find-Manifest { $null, @{}, $null, $null } -ParameterFilter { $AppName -eq 'innounp' }
Mock Get-Manifest { 'lessmsi', @{}, $null, $null } -ParameterFilter { $app -eq 'lessmsi' }
Mock Get-Manifest { '7zip', @{ url = 'test.msi' }, $null, $null } -ParameterFilter { $app -eq '7zip' }
Mock Get-Manifest { 'innounp', @{}, $null, $null } -ParameterFilter { $app -eq 'innounp' }
}
It 'Resolve install dependencies' {
Mock Find-Manifest { $null, @{ url = 'test.7z' }, $null, $null }
Mock Get-Manifest { 'test', @{ url = 'test.7z' }, $null, $null }
Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('lessmsi', '7zip', 'test')
Mock Find-Manifest { $null, @{ innosetup = $true }, $null, $null }
Mock Get-Manifest { 'test', @{ innosetup = $true }, $null, $null }
Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('innounp', 'test')
}
It 'Resolve script dependencies' {
Mock Find-Manifest { $null, @{ pre_install = 'Expand-7zipArchive ' }, $null, $null }
Mock Get-Manifest { 'test', @{ pre_install = 'Expand-7zipArchive ' }, $null, $null }
Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('lessmsi', '7zip', 'test')
}
It 'Resolve runtime dependencies' {
Mock Find-Manifest { $null, @{}, $null, $null } -ParameterFilter { $AppName -eq 'depends' }
Mock Find-Manifest { $null, @{ depends = 'depends' }, $null, $null }
Mock Get-Manifest { 'depends', @{}, $null, $null } -ParameterFilter { $app -eq 'depends' }
Mock Get-Manifest { 'test', @{ depends = 'depends' }, $null, $null }
Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('depends', 'test')
}
It 'Keep bucket name of app' {
Mock Find-Manifest { $null, @{}, $null, $null } -ParameterFilter { $AppName -eq 'bucket/depends' }
Mock Find-Manifest { $null, @{ depends = 'bucket/depends' }, $null, $null }
Get-Dependency -AppName 'anotherbucket/test' -Architecture '32bit' | Should -Be @('bucket/depends', 'anotherbucket/test')
Mock Get-Manifest { 'depends', @{}, 'anotherbucket', $null } -ParameterFilter { $app -eq 'anotherbucket/depends' }
Mock Get-Manifest { 'test', @{ depends = 'anotherbucket/depends' }, 'bucket', $null }
Get-Dependency -AppName 'bucket/test' -Architecture '32bit' | Should -Be @('anotherbucket/depends', 'bucket/test')
}
}
}

Binary file not shown.