mirror of
https://github.com/ScoopInstaller/Scoop.git
synced 2025-12-04 07:05:51 +00:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93db5f47f1 | ||
|
|
5987e499b9 | ||
|
|
666d474ee1 | ||
|
|
e7c0faa29a | ||
|
|
9e6c758c1f | ||
|
|
0b38c91f1a | ||
|
|
6e25e440af | ||
|
|
d63b7d6f01 | ||
|
|
ecb8f02d4e | ||
|
|
7e6be8f3f5 | ||
|
|
574bea4975 | ||
|
|
9e70dcad79 | ||
|
|
64364b40b4 | ||
|
|
387835753d | ||
|
|
bfb5c8d04a | ||
|
|
3a1186ea1b | ||
|
|
ccd067b2b1 | ||
|
|
78c1bc45b4 | ||
|
|
dd0f51426b | ||
|
|
d6c6ddcbb3 | ||
|
|
2e52888b63 | ||
|
|
0f6d012d26 | ||
|
|
896ea6cdbd | ||
|
|
d056d542db | ||
|
|
ad04dc9e6f | ||
|
|
8140a2052c | ||
|
|
ac2fb38722 | ||
|
|
47d7f76f7c | ||
|
|
bb5392b486 | ||
|
|
f49f976618 | ||
|
|
5d58703484 | ||
|
|
b130e606cf | ||
|
|
c6fc2de306 | ||
|
|
a2600b1203 |
46
CHANGELOG.md
46
CHANGELOG.md
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
112
lib/core.ps1
112
lib/core.ps1
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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..."
|
||||
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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] }
|
||||
}
|
||||
|
||||
|
||||
@@ -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 '[\\/]') {
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
12
schema.json
12
schema.json
@@ -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": {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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!'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
test/fixtures/decompress/TestCases.zip
vendored
BIN
test/fixtures/decompress/TestCases.zip
vendored
Binary file not shown.
Reference in New Issue
Block a user