52 Commits

Author SHA1 Message Date
Hsiao-nan Cheung
859d1db51b chore(release): Bump to version 0.5.2 (#6080)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
Co-authored-by: Matt Cargile <74641867+mattcargile@users.noreply.github.com>
Co-authored-by: ccyykkcyk <91893497+ccyykkcyk@users.noreply.github.com>
2024-07-30 15:34:41 +08:00
Hsiao-nan Cheung
c7ec5c82b2 docs(chglog): Update CHANGELOG for v0.5.2 (#6081) 2024-07-25 20:29:02 +08:00
Hsiao-nan Cheung
46624b00c9 fix(core): Use 'Join-Path' to construct cache file path (#6079) 2024-07-25 13:54:26 +08:00
Hsiao-nan Cheung
49ee8ad6ec fix(scoop-hold): Use 'foreach' loop to allow 'continue' statement (#6078) 2024-07-25 13:00:36 +08:00
Hsiao-nan Cheung
5dc5dd22f8 fix(json): Fix JSON items counting function (#6073) 2024-07-23 19:55:40 +08:00
Hsiao-nan Cheung
5971f1dda8 builds(supporting): Update Json.Schema to 4.0.1 (#6072) 2024-07-23 19:13:00 +08:00
ccyykkcyk
0cb479abf0 fix(json): Don't serialize jsonpath return if only one result (#6066)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-07-22 19:02:06 +08:00
Matt Cargile
429ba7af5b fix(scoop-alias): Fix 'Option --verbose not recognized.' (#6062) 2024-07-17 22:12:29 +08:00
Hsiao-nan Cheung
be56faf290 chore(release): Bump to version 0.5.1 (#6057) 2024-07-17 10:38:53 +08:00
Hsiao-nan Cheung
69edc6dc54 chore(chglog): Update CHANGELOG for v0.5.1 (#6058) 2024-07-15 13:17:19 +08:00
L. Yeung
0138dc4266 fix(scoop-alias): Pass options correctly (#6003)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-07-15 13:06:22 +08:00
Bill ZHANG
1c271f5b90 fix(scoop-virustotal): Adjust json_path parameters to retrieve correct analysis stats (#6044) 2024-07-09 13:45:09 +08:00
qiuyk
a76884af19 fix(install): Fix parsing error when installing multiple apps w/ specific version (#6039)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-07-09 13:22:06 +08:00
Hsiao-nan Cheung
7cc4faea1d fix(bucket): Implement error handling for failed bucket addition (#6051) 2024-07-09 01:16:23 +08:00
Hsiao-nan Cheung
83f25a4673 fix(install): Expand env_set items (#6050) 2024-07-09 00:24:42 +08:00
Hsiao-nan Cheung
42af27d16c fix(sqlite): Fix compatibility with Windows PowerShell (#6045) 2024-07-08 13:43:22 +08:00
Hsiao-nan Cheung
716b6db3ae chore(release): Bump to version 0.5.0 (#6035) 2024-07-05 11:14:42 +08:00
Hsiao-nan Cheung
ade7aa4a15 (chore): Update changelog (#6036) 2024-06-30 18:19:19 +08:00
Hsiao-nan Cheung
7e0a2a28c6 Update chglog 2024-06-30 18:09:59 +08:00
Hsiao-nan Cheung
2d02483fb8 Merge branch 'master' into develop 2024-06-30 18:07:36 +08:00
Hsiao-nan Cheung
93359a43a1 fix(shim): Restore original path for JAR cmd (#6030) 2024-06-26 18:34:38 +08:00
L. Yeung
d8b3cc8a6c fix(checkver): Correct error messages (#6024) 2024-06-25 10:40:56 +08:00
Hsiao-nan Cheung
9239c26bbd feat(decompress): Use innounp-unicode as default Inno Setup Unpacker (#6028) 2024-06-24 21:20:29 +08:00
qiuyk
3a39ba048f fix(core): Limit the number of commands to get (#6013) 2024-06-14 00:18:14 +08:00
Hsiao-nan Cheung
a9ca75c0cd fix(core): Use correct path in 'bash' (#6006) 2024-06-11 11:03:35 +08:00
Matej Kafka
d20819e147 fix(core): Search for Git executable instead of any cmdlet (#5998) 2024-06-07 18:45:52 +08:00
L. Yeung
dec4232754 fix(decompress): Match extract_dir/extract_to and archives (#5983) 2024-05-26 15:36:06 +08:00
Chawye Hsu
fa1b42bf28 fix(checkver): Correct variable 'regex' to 'regexp' (#5993) 2024-05-26 14:30:47 +08:00
insertokername
700a2f4f5e feat(install): Added the ability to specify @ version when installing from url/file location (#5988) 2024-05-23 23:05:38 +08:00
Hsiao-nan Cheung
c9048ad978 fix(sqlite): Fix generic class error in PS5 (#5981) 2024-05-20 20:57:29 +08:00
Hsiao-nan Cheung
5c896e901f refactor(decompress): Use 7zip to extract Zstd archive (#5973) 2024-05-17 09:56:14 +08:00
Hsiao-nan Cheung
8ea37387ae fix(install): Add back arg $Name in Invoke-Installer() (#5971) 2024-05-15 19:25:35 +08:00
Hsiao-nan Cheung
2544745695 refactor(install): Replace 'run_(un)installer()' with 'Invoke-Installer()' (#5968) 2024-05-15 19:03:54 +08:00
Hsiao-nan Cheung
5ce70c4139 fix(sqlite): Update cache after removing bucket or manifests (#5967) 2024-05-15 17:07:37 +08:00
L. Yeung
2d50a02a32 fix(scoop-download|install|update): Use consistent options (#5956) 2024-05-14 22:43:53 +08:00
Hsiao-nan Cheung
f6f46f6cf4 fix(sqlite): Dispose all command to release database file handle (#5966) 2024-05-14 16:38:05 +08:00
Hsiao-nan Cheung
4dd2cfdc5f fix(core): Add 'PSObject.Copy()' to 'substitute()' (#5962) 2024-05-13 13:46:59 +08:00
Hsiao-nan Cheung
a5bd2297c6 refactor(install): Separate archive extraction from downloader (#5951) 2024-05-12 18:47:16 +08:00
Hsiao-nan Cheung
b710ff6c0a fix(scoop-info): Fix download size estimating (#5958) 2024-05-11 18:22:31 +08:00
Hsiao-nan Cheung
1dd479f0be fix(database): Use 'Find-BucketDirectory()' to locate bucket dir (#5955) 2024-05-11 14:50:20 +08:00
Hsiao-nan Cheung
1752050614 fix(core): Fix "Invoke-ExternalCommand" quoting rules (#5945) 2024-05-11 14:49:49 +08:00
Hsiao-nan Cheung
edaae8d39f fix(database): Skip caching 'deprecated' dir (#5949) 2024-05-09 20:15:57 +08:00
Hsiao-nan Cheung
776135e0ab fix(database): Update cache when adding bucket (#5946) 2024-05-08 18:10:13 +08:00
Chawye Hsu
cc863353e2 fix(scoop-cache): Fix regression in 36026f18 (#5944)
Signed-off-by: Chawye Hsu <su+git@chawyehsu.com>
2024-05-08 11:24:41 +08:00
Inseo Lee
5b86c302e5 fix(system): Fix argument passing to Split-PathLikeEnvVar() in deprecated strip_path() (#5937)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-05-06 10:04:40 +08:00
Xuesong
b8580aa931 fix(autoupdate): Copy PSCustomObject-type properties within autoupdate to prevent reference changes (#5934) 2024-05-01 23:02:13 +08:00
Chawye Hsu
23d55ced72 fix(scoop-search): Catch error of parsing invalid manifest (#5930) 2024-05-01 00:52:49 +08:00
Chawye Hsu
36026f1804 feat(core): New cache filename format (#5929)
Signed-off-by: Chawye Hsu <su+git@chawyehsu.com>
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-04-29 18:36:19 +08:00
Chawye Hsu
3b34497eb4 fix(json): Serialize jsonpath return (#5921) 2024-04-26 18:12:34 +08:00
Hsiao-nan Cheung
bb88dfb2d4 Sync 'master' branch 2024-04-25 22:09:16 +08:00
Hsiao-nan Cheung
eac58400db fix(sqlite): Skip use_sqlite_cache config on ARM64 platform (#5918) 2024-04-25 21:32:41 +08:00
Hsiao-nan Cheung
2dd91d5ba3 feat(sqlite): Use SQLite for caching apps to speed up local search (#5851) 2024-04-19 14:02:50 +08:00
38 changed files with 1072 additions and 508 deletions

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@ test/installer/tmp/*
test/tmp/*
*~
TestResults.xml
supporting/sqlite/*

View File

@@ -1,3 +1,56 @@
## [v0.5.2](https://github.com/ScoopInstaller/Scoop/compare/v0.5.1...v0.5.2) - 2024-07-26
### Bug Fixes
- **scoop-alias:** Fix 'Option --verbose not recognized.' ([#6062](https://github.com/ScoopInstaller/Scoop/issues/6062))
- **scoop-hold:** Use 'foreach' loop to allow 'continue' statement ([#6078](https://github.com/ScoopInstaller/Scoop/issues/6078))
- **core:** Use 'Join-Path' to construct cache file path ([#6079](https://github.com/ScoopInstaller/Scoop/issues/6079))
- **json:** Don't serialize jsonpath return if only one result ([#6066](https://github.com/ScoopInstaller/Scoop/issues/6066), [#6073](https://github.com/ScoopInstaller/Scoop/issues/6073))
### Builds
- **supporting:** Update Json.Schema to 4.0.1 ([#6072](https://github.com/ScoopInstaller/Scoop/issues/6072))
## [v0.5.1](https://github.com/ScoopInstaller/Scoop/compare/v0.5.0...v0.5.1) - 2024-07-16
### Bug Fixes
- **scoop-alias:** Pass options correctly ([#6003](https://github.com/ScoopInstaller/Scoop/issues/6003))
- **scoop-virustotal:** Adjust `json_path` parameters to retrieve correct analysis stats ([#6044](https://github.com/ScoopInstaller/Scoop/issues/6044))
- **bucket:** Implement error handling for failed bucket addition ([#6051](https://github.com/ScoopInstaller/Scoop/issues/6051))
- **database:** Fix compatibility with Windows PowerShell ([#6045](https://github.com/ScoopInstaller/Scoop/issues/6045))
- **install:** Expand `env_set` items before setting Environment Variables ([#6050](https://github.com/ScoopInstaller/Scoop/issues/6050))
- **install:** Fix parsing error when installing multiple apps w/ specific version ([#6039](https://github.com/ScoopInstaller/Scoop/issues/6039))
## [v0.5.0](https://github.com/ScoopInstaller/Scoop/compare/v0.4.2...v0.5.0) - 2024-07-01
### Features
- **scoop-search:** Use SQLite for caching apps to speed up local search ([#5851](https://github.com/ScoopInstaller/Scoop/issues/5851), [#5918](https://github.com/ScoopInstaller/Scoop/issues/5918), [#5946](https://github.com/ScoopInstaller/Scoop/issues/5946), [#5949](https://github.com/ScoopInstaller/Scoop/issues/5949), [#5955](https://github.com/ScoopInstaller/Scoop/issues/5955), [#5966](https://github.com/ScoopInstaller/Scoop/issues/5966), [#5967](https://github.com/ScoopInstaller/Scoop/issues/5967), [#5981](https://github.com/ScoopInstaller/Scoop/issues/5981))
- **core:** New cache filename format ([#5929](https://github.com/ScoopInstaller/Scoop/issues/5929), [#5944](https://github.com/ScoopInstaller/Scoop/issues/5944))
- **decompress:** Use innounp-unicode as default Inno Setup Unpacker ([#6028](https://github.com/ScoopInstaller/Scoop/issues/6028))
- **install:** Added the ability to install specific version of app from URL/file link ([#5988](https://github.com/ScoopInstaller/Scoop/issues/5988))
### Bug Fixes
- **scoop-download|install|update:** Use consistent options ([#5956](https://github.com/ScoopInstaller/Scoop/issues/5956))
- **scoop-info:** Fix download size estimating ([#5958](https://github.com/ScoopInstaller/Scoop/issues/5958))
- **scoop-search:** Catch error of parsing invalid manifest ([#5930](https://github.com/ScoopInstaller/Scoop/issues/5930))
- **checkver:** Correct variable 'regex' to 'regexp' ([#5993](https://github.com/ScoopInstaller/Scoop/issues/5993))
- **checkver:** Correct error messages ([#6024](https://github.com/ScoopInstaller/Scoop/issues/6024))
- **core:** Search for Git executable instead of any cmdlet ([#5998](https://github.com/ScoopInstaller/Scoop/issues/5998))
- **core:** Use correct path in 'bash' ([#6006](https://github.com/ScoopInstaller/Scoop/issues/6006))
- **core:** Limit the number of commands to get when search for git executable ([#6013](https://github.com/ScoopInstaller/Scoop/pull/6013))
- **decompress:** Match `extract_dir`/`extract_to` and archives ([#5983](https://github.com/ScoopInstaller/Scoop/issues/5983))
- **json:** Serialize jsonpath return ([#5921](https://github.com/ScoopInstaller/Scoop/issues/5921))
- **shim:** Restore original path for JAR cmd ([#6030](https://github.com/ScoopInstaller/Scoop/issues/6030))
### Code Refactoring
- **decompress:** Use 7zip to extract Zstd archive ([#5973](https://github.com/ScoopInstaller/Scoop/issues/5973))
- **install:** Separate archive extraction from downloader ([#5951](https://github.com/ScoopInstaller/Scoop/issues/5951))
- **install:** Replace 'run_(un)installer()' with 'Invoke-Installer()' ([#5968](https://github.com/ScoopInstaller/Scoop/issues/5968), [#5971](https://github.com/ScoopInstaller/Scoop/issues/5971))
## [v0.4.2](https://github.com/ScoopInstaller/Scoop/compare/v0.4.1...v0.4.2) - 2024-05-14
### Bug Fixes

View File

@@ -275,7 +275,7 @@ while ($in_progress -gt 0) {
$ver = $Version
if (!$ver) {
if (!$regex -and $replace) {
if (!$regexp -and $replace) {
next "'replace' requires 're' or 'regex'"
continue
}
@@ -294,13 +294,15 @@ while ($in_progress -gt 0) {
}
$page = (New-Object System.IO.StreamReader($ms, (Get-Encoding $wc))).ReadToEnd()
}
$source = $url
if ($script) {
$page = Invoke-Command ([scriptblock]::Create($script -join "`r`n"))
$source = 'the output of script'
}
if ($jsonpath) {
# Return only a single value if regex is absent
$noregex = [String]::IsNullOrEmpty($regex)
$noregex = [String]::IsNullOrEmpty($regexp)
# If reverse is ON and regex is ON,
# Then reverse would have no effect because regex handles reverse
# on its own
@@ -310,7 +312,7 @@ while ($in_progress -gt 0) {
$ver = json_path_legacy $page $jsonpath
}
if (!$ver) {
next "couldn't find '$jsonpath' in $url"
next "couldn't find '$jsonpath' in $source"
continue
}
}
@@ -332,7 +334,7 @@ while ($in_progress -gt 0) {
# Getting version from XML, using XPath
$ver = $xml.SelectSingleNode($xpath, $nsmgr).'#text'
if (!$ver) {
next "couldn't find '$($xpath -replace 'ns:', '')' in $url"
next "couldn't find '$($xpath -replace 'ns:', '')' in $source"
continue
}
}
@@ -348,31 +350,31 @@ while ($in_progress -gt 0) {
}
if ($regexp) {
$regex = New-Object System.Text.RegularExpressions.Regex($regexp)
$re = New-Object System.Text.RegularExpressions.Regex($regexp)
if ($reverse) {
$match = $regex.Matches($page) | Select-Object -Last 1
$match = $re.Matches($page) | Select-Object -Last 1
} else {
$match = $regex.Matches($page) | Select-Object -First 1
$match = $re.Matches($page) | Select-Object -First 1
}
if ($match -and $match.Success) {
$matchesHashtable = @{}
$regex.GetGroupNames() | ForEach-Object { $matchesHashtable.Add($_, $match.Groups[$_].Value) }
$re.GetGroupNames() | ForEach-Object { $matchesHashtable.Add($_, $match.Groups[$_].Value) }
$ver = $matchesHashtable['1']
if ($replace) {
$ver = $regex.Replace($match.Value, $replace)
$ver = $re.Replace($match.Value, $replace)
}
if (!$ver) {
$ver = $matchesHashtable['version']
}
} else {
next "couldn't match '$regexp' in $url"
next "couldn't match '$regexp' in $source"
continue
}
}
if (!$ver) {
next "couldn't find new version in $url"
next "couldn't find new version in $source"
continue
}
}

View File

@@ -42,7 +42,7 @@ function do_uninstall($app, $global) {
$architecture = $install.architecture
Write-Output "Uninstalling '$app'"
run_uninstaller $manifest $architecture $dir
Invoke-Installer -Path $dir -Manifest $manifest -ProcessorArchitecture $architecture -Uninstall
rm_shims $app $manifest $global $architecture
# If a junction was used during install, that will have been used

View File

@@ -154,8 +154,17 @@ function add_bucket($name, $repo) {
}
ensure $bucketsdir | Out-Null
$dir = ensure $dir
Invoke-Git -ArgumentList @('clone', $repo, $dir, '-q')
$out = Invoke-Git -ArgumentList @('clone', $repo, $dir, '-q')
if ($LASTEXITCODE -ne 0) {
error "Failed to clone '$repo' to '$dir'.`n`nError given:`n$out`n`nPlease check the repository URL or network connection and try again."
Remove-Item $dir -Recurse -Force -ErrorAction SilentlyContinue
return 1
}
Write-Host 'OK'
if (get_config USE_SQLITE_CACHE) {
info 'Updating cache...'
Set-ScoopDB -Path (Get-ChildItem (Find-BucketDirectory $name) -Filter '*.json' -Recurse).FullName
}
success "The $name bucket was added successfully."
return 0
}
@@ -168,6 +177,11 @@ function rm_bucket($name) {
}
Remove-Item $dir -Recurse -Force -ErrorAction Stop
if (get_config USE_SQLITE_CACHE) {
info 'Updating cache...'
Remove-ScoopDBItem -Bucket $name
}
success "The $name bucket was removed successfully."
return 0
}

View File

@@ -1,3 +1,7 @@
# Description: Functions for managing commands and aliases.
## Functions for commands
function command_files {
(Get-ChildItem "$PSScriptRoot\..\libexec") + (Get-ChildItem "$scoopdir\shims") |
Where-Object 'scoop-.*?\.ps1$' -Property Name -Match
@@ -19,11 +23,10 @@ function command_path($cmd) {
# get path from shim
$shim_path = "$scoopdir\shims\scoop-$cmd.ps1"
$line = ((Get-Content $shim_path) | Where-Object { $_.startswith('$path') })
if($line) {
if ($line) {
Invoke-Command ([scriptblock]::Create($line)) -NoNewScope
$cmd_path = $path
}
else { $cmd_path = $shim_path }
} else { $cmd_path = $shim_path }
}
$cmd_path
@@ -34,3 +37,82 @@ function exec($cmd, $arguments) {
& $cmd_path @arguments
}
## Functions for aliases
function add_alias {
param(
[ValidateNotNullOrEmpty()]
[string]$name,
[ValidateNotNullOrEmpty()]
[string]$command,
[string]$description
)
$aliases = get_config ALIAS ([PSCustomObject]@{})
if ($aliases.$name) {
abort "Alias '$name' already exists."
}
$alias_script_name = "scoop-$name"
$shimdir = shimdir $false
if (Test-Path "$shimdir\$alias_script_name.ps1") {
abort "File '$alias_script_name.ps1' already exists in shims directory."
}
$script = @(
"# Summary: $description",
"$command"
) -join "`n"
try {
$script | Out-UTF8File "$shimdir\$alias_script_name.ps1"
} catch {
abort $_.Exception
}
# Add the new alias to the config.
$aliases | Add-Member -MemberType NoteProperty -Name $name -Value $alias_script_name
set_config ALIAS $aliases | Out-Null
}
function rm_alias {
param(
[ValidateNotNullOrEmpty()]
[string]$name
)
$aliases = get_config ALIAS ([PSCustomObject]@{})
if (!$aliases.$name) {
abort "Alias '$name' doesn't exist."
}
info "Removing alias '$name'..."
Remove-Item "$(shimdir $false)\scoop-$name.ps1"
$aliases.PSObject.Properties.Remove($name)
set_config ALIAS $aliases | Out-Null
}
function list_aliases {
param(
[bool]$verbose
)
$aliases = get_config ALIAS ([PSCustomObject]@{})
$alias_info = $aliases.PSObject.Properties.Name | Where-Object { $_ } | ForEach-Object {
$content = Get-Content (command_path $_)
[PSCustomObject]@{
Name = $_
Summary = (summary $content).Trim()
Command = ($content | Select-Object -Skip 1).Trim()
}
}
if (!$alias_info) {
info 'No alias found.'
return
}
$alias_info = $alias_info | Sort-Object Name
$properties = @('Name', 'Command')
if ($verbose) {
$properties += 'Summary'
}
$alias_info | Select-Object $properties
}

View File

@@ -216,6 +216,16 @@ function Complete-ConfigChange {
}
}
}
if ($Name -eq 'use_sqlite_cache' -and $Value -eq $true) {
if ((Get-DefaultArchitecture) -eq 'arm64') {
abort 'SQLite cache is not supported on ARM64 platform.'
}
. "$PSScriptRoot\..\lib\database.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
info 'Initializing SQLite cache in progress... This may take a while, please wait.'
Set-ScoopDB
}
}
function setup_proxy() {
@@ -314,10 +324,6 @@ function Invoke-GitLog {
# helper functions
function coalesce($a, $b) { if($a) { return $a } $b }
function format($str, $hash) {
$hash.keys | ForEach-Object { set-variable $_ $hash[$_] }
$executionContext.invokeCommand.expandString($str)
}
function is_admin {
$admin = [security.principal.windowsbuiltinrole]::administrator
$id = [security.principal.windowsidentity]::getcurrent()
@@ -399,7 +405,22 @@ function currentdir($app, $global) {
function persistdir($app, $global) { "$(basedir $global)\persist\$app" }
function usermanifestsdir { "$(basedir)\workspace" }
function usermanifest($app) { "$(usermanifestsdir)\$app.json" }
function cache_path($app, $version, $url) { "$cachedir\$app#$version#$($url -replace '[^\w\.\-]+', '_')" }
function cache_path($app, $version, $url) {
$underscoredUrl = $url -replace '[^\w\.\-]+', '_'
$filePath = Join-Path $cachedir "$app#$version#$underscoredUrl"
# NOTE: Scoop cache files migration. Remove this 6 months after the feature ships.
if (Test-Path $filePath) {
return $filePath
}
$urlStream = [System.IO.MemoryStream]::new([System.Text.Encoding]::UTF8.GetBytes($url))
$sha = (Get-FileHash -Algorithm SHA256 -InputStream $urlStream).Hash.ToLower().Substring(0, 7)
$extension = [System.IO.Path]::GetExtension($url)
$filePath = $filePath -replace "$underscoredUrl", "$sha$extension"
return $filePath
}
# apps
function sanitary_path($path) { return [regex]::replace($path, "[/\\?:*<>|]", "") }
@@ -477,7 +498,7 @@ function Get-HelperPath {
[OutputType([String])]
param(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[ValidateSet('Git', '7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2', 'Zstd')]
[ValidateSet('Git', '7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2')]
[String]
$Helper
)
@@ -491,12 +512,17 @@ function Get-HelperPath {
if ($internalgit) {
$HelperPath = $internalgit
} else {
$HelperPath = (Get-Command git -ErrorAction Ignore).Source
$HelperPath = (Get-Command git -CommandType Application -TotalCount 1 -ErrorAction Ignore).Source
}
}
'7zip' { $HelperPath = Get-AppFilePath '7zip' '7z.exe' }
'Lessmsi' { $HelperPath = Get-AppFilePath 'lessmsi' 'lessmsi.exe' }
'Innounp' { $HelperPath = Get-AppFilePath 'innounp' 'innounp.exe' }
'Innounp' {
$HelperPath = Get-AppFilePath 'innounp-unicode' 'innounp.exe'
if ([String]::IsNullOrEmpty($HelperPath)) {
$HelperPath = Get-AppFilePath 'innounp' 'innounp.exe'
}
}
'Dark' {
$HelperPath = Get-AppFilePath 'wixtoolset' 'wix.exe'
if ([String]::IsNullOrEmpty($HelperPath)) {
@@ -504,7 +530,6 @@ function Get-HelperPath {
}
}
'Aria2' { $HelperPath = Get-AppFilePath 'aria2' 'aria2c.exe' }
'Zstd' { $HelperPath = Get-AppFilePath 'zstd' 'zstd.exe' }
}
return $HelperPath
@@ -551,7 +576,7 @@ function Test-HelperInstalled {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[ValidateSet('7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2', 'Zstd')]
[ValidateSet('7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2')]
[String]
$Helper
)
@@ -1013,19 +1038,20 @@ function shim($path, $global, $name, $arg) {
warn_on_overwrite "$shim.cmd" $path
@(
"@rem $resolved_path",
"@cd /d $(Split-Path $resolved_path -Parent)"
"@java -jar `"$resolved_path`" $arg %*"
"@pushd $(Split-Path $resolved_path -Parent)",
"@java -jar `"$resolved_path`" $arg %*",
"@popd"
) -join "`r`n" | Out-UTF8File "$shim.cmd"
warn_on_overwrite $shim $path
@(
"#!/bin/sh",
"# $resolved_path",
"if [ `$(echo `$WSL_DISTRO_NAME) ]",
"if [ `$WSL_INTEROP ]",
'then',
" cd `$(wslpath -u '$(Split-Path $resolved_path -Parent)')",
'else',
" cd `"$((Split-Path $resolved_path -Parent).Replace('\', '/'))`"",
" cd `$(cygpath -u '$(Split-Path $resolved_path -Parent)')",
'fi',
"java.exe -jar `"$resolved_path`" $arg `"$@`""
) -join "`n" | Out-UTF8File $shim -NoNewLine
@@ -1038,7 +1064,7 @@ function shim($path, $global, $name, $arg) {
warn_on_overwrite $shim $path
@(
"#!/bin/sh",
'#!/bin/sh',
"# $resolved_path",
"python.exe `"$resolved_path`" $arg `"$@`""
) -join "`n" | Out-UTF8File $shim -NoNewLine
@@ -1046,14 +1072,22 @@ function shim($path, $global, $name, $arg) {
warn_on_overwrite "$shim.cmd" $path
@(
"@rem $resolved_path",
"@bash `"$resolved_path`" $arg %*"
"@bash `"`$(wslpath -u '$resolved_path')`" $arg %* 2>nul",
'@if %errorlevel% neq 0 (',
" @bash `"`$(cygpath -u '$resolved_path')`" $arg %* 2>nul",
')'
) -join "`r`n" | Out-UTF8File "$shim.cmd"
warn_on_overwrite $shim $path
@(
"#!/bin/sh",
'#!/bin/sh',
"# $resolved_path",
"`"$resolved_path`" $arg `"$@`""
"if [ `$WSL_INTEROP ]",
'then',
" `"`$(wslpath -u '$resolved_path')`" $arg `"$@`"",
'else',
" `"`$(cygpath -u '$resolved_path')`" $arg `"$@`"",
'fi'
) -join "`n" | Out-UTF8File $shim -NoNewLine
}
}
@@ -1172,7 +1206,7 @@ function applist($apps, $global) {
}
function parse_app([string]$app) {
if ($app -match '^(?:(?<bucket>[a-zA-Z0-9-_.]+)/)?(?<app>.*\.json$|[a-zA-Z0-9-_.]+)(?:@(?<version>.*))?$') {
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

390
lib/database.ps1 Normal file
View File

@@ -0,0 +1,390 @@
# Description: Functions for interacting with the Scoop database cache
<#
.SYNOPSIS
Get SQLite .NET driver
.DESCRIPTION
Download and extract the SQLite .NET driver from NuGet.
.PARAMETER Version
System.String
The version of the SQLite .NET driver to download.
.INPUTS
None
.OUTPUTS
System.Boolean
True if the SQLite .NET driver was successfully downloaded and extracted, otherwise false.
#>
function Get-SQLite {
param (
[string]$Version = '1.0.118'
)
# Install SQLite
try {
Write-Host "Downloading SQLite $Version..." -ForegroundColor DarkYellow
$sqlitePkgPath = "$env:TEMP\sqlite.zip"
$sqliteTempPath = "$env:TEMP\sqlite"
$sqlitePath = "$PSScriptRoot\..\supporting\sqlite"
Invoke-WebRequest -Uri "https://api.nuget.org/v3-flatcontainer/stub.system.data.sqlite.core.netframework/$version/stub.system.data.sqlite.core.netframework.$version.nupkg" -OutFile $sqlitePkgPath
Write-Host "Extracting SQLite $Version..." -ForegroundColor DarkYellow -NoNewline
Expand-Archive -Path $sqlitePkgPath -DestinationPath $sqliteTempPath -Force
New-Item -Path $sqlitePath -ItemType Directory -Force | Out-Null
Move-Item -Path "$sqliteTempPath\build\net451\*", "$sqliteTempPath\lib\net451\System.Data.SQLite.dll" -Destination $sqlitePath -Force
Remove-Item -Path $sqlitePkgPath, $sqliteTempPath -Recurse -Force
Write-Host ' Done' -ForegroundColor DarkYellow
return $true
} catch {
return $false
}
}
<#
.SYNOPSIS
Open Scoop SQLite database.
.DESCRIPTION
Open Scoop SQLite database connection and create the necessary tables if not exists.
.INPUTS
None
.OUTPUTS
System.Data.SQLite.SQLiteConnection
The SQLite database connection if **PassThru** is used.
#>
function Open-ScoopDB {
# Load System.Data.SQLite
if (!('System.Data.SQLite.SQLiteConnection' -as [Type])) {
try {
if (!(Test-Path -Path "$PSScriptRoot\..\supporting\sqlite\System.Data.SQLite.dll")) {
Get-SQLite | Out-Null
}
Add-Type -Path "$PSScriptRoot\..\supporting\sqlite\System.Data.SQLite.dll"
} catch {
throw "Scoop's Database cache requires the ADO.NET driver:`n`thttp://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki"
}
}
$dbPath = Join-Path $scoopdir 'scoop.db'
$db = New-Object -TypeName System.Data.SQLite.SQLiteConnection
$db.ConnectionString = "Data Source=$dbPath"
$db.ParseViaFramework = $true # Allow UNC path
$db.Open()
$tableCommand = $db.CreateCommand()
$tableCommand.CommandText = "CREATE TABLE IF NOT EXISTS 'app' (
name TEXT NOT NULL COLLATE NOCASE,
description TEXT NOT NULL,
version TEXT NOT NULL,
bucket VARCHAR NOT NULL,
manifest JSON NOT NULL,
binary TEXT,
shortcut TEXT,
dependency TEXT,
suggest TEXT,
PRIMARY KEY (name, version, bucket)
)"
$tableCommand.CommandType = [System.Data.CommandType]::Text
$tableCommand.ExecuteNonQuery() | Out-Null
$tableCommand.Dispose()
return $db
}
<#
.SYNOPSIS
Set Scoop database item(s).
.DESCRIPTION
Insert or replace item(s) into the Scoop SQLite database.
.PARAMETER InputObject
System.Object[]
The database item(s) to insert or replace.
.INPUTS
System.Object[]
.OUTPUTS
None
#>
function Set-ScoopDBItem {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0, ValueFromPipeline)]
[psobject[]]
$InputObject
)
begin {
$db = Open-ScoopDB
$dbTrans = $db.BeginTransaction()
# TODO Support [hashtable]$InputObject
$colName = @($InputObject | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name)
$dbQuery = "INSERT OR REPLACE INTO app ($($colName -join ', ')) VALUES ($('@' + ($colName -join ', @')))"
$dbCommand = $db.CreateCommand()
$dbCommand.CommandText = $dbQuery
$dbCommand.CommandType = [System.Data.CommandType]::Text
}
process {
foreach ($item in $InputObject) {
$item.PSObject.Properties | ForEach-Object {
$dbCommand.Parameters.AddWithValue("@$($_.Name)", $_.Value) | Out-Null
}
$dbCommand.ExecuteNonQuery() | Out-Null
}
}
end {
try {
$dbTrans.Commit()
} catch {
$dbTrans.Rollback()
throw $_
} finally {
$dbCommand.Dispose()
$dbTrans.Dispose()
$db.Dispose()
}
}
}
<#
.SYNOPSIS
Set Scoop app database item(s).
.DESCRIPTION
Insert or replace Scoop app(s) into the database.
.PARAMETER Path
System.String
The path to the bucket.
.PARAMETER CommitHash
System.String
The commit hash to compare with the HEAD.
.INPUTS
None
.OUTPUTS
None
#>
function Set-ScoopDB {
[CmdletBinding()]
param (
[Parameter(Position = 0, ValueFromPipeline)]
[string[]]
$Path
)
begin {
$list = [System.Collections.Generic.List[psobject]]::new()
$arch = Get-DefaultArchitecture
}
process {
if ($Path.Count -eq 0) {
$bucketPath = Get-LocalBucket | ForEach-Object { Find-BucketDirectory $_ }
$Path = (Get-ChildItem $bucketPath -Filter '*.json' -Recurse).FullName
}
$Path | ForEach-Object {
$manifestRaw = [System.IO.File]::ReadAllText($_)
$manifest = ConvertFrom-Json $manifestRaw -ErrorAction SilentlyContinue
if ($null -ne $manifest.version) {
$list.Add([pscustomobject]@{
name = $($_ -replace '.*[\\/]([^\\/]+)\.json$', '$1')
description = if ($manifest.description) { $manifest.description } else { '' }
version = $manifest.version
bucket = $($_ -replace '.*buckets[\\/]([^\\/]+)(?:[\\/].*)', '$1')
manifest = $manifestRaw
binary = $(
$result = @()
@(arch_specific 'bin' $manifest $arch) | ForEach-Object {
if ($_ -is [System.Array]) {
$result += "$($_[1]).$($_[0].Split('.')[-1])"
} else {
$result += $_
}
}
$result -replace '.*?([^\\/]+)?(\.(exe|bat|cmd|ps1|jar|py))$', '$1' -join ' | '
)
shortcut = $(
$result = @()
@(arch_specific 'shortcuts' $manifest $arch) | ForEach-Object {
$result += $_[1]
}
$result -replace '.*?([^\\/]+$)', '$1' -join ' | '
)
dependency = $manifest.depends -join ' | '
suggest = $(
$suggest_output = @()
$manifest.suggest.PSObject.Properties | ForEach-Object {
$suggest_output += $_.Value -join ' | '
}
$suggest_output -join ' | '
)
})
}
}
}
end {
if ($list.Count -ne 0) {
Set-ScoopDBItem $list
}
}
}
<#
.SYNOPSIS
Select Scoop database item(s).
.DESCRIPTION
Select item(s) from the Scoop SQLite database.
The pattern is matched against the name, binaries, and shortcuts columns for apps.
.PARAMETER Pattern
System.String
The pattern to search for. If is an empty string, all items will be returned.
.PARAMETER From
System.String[]
The fields to search from.
.INPUTS
System.String
.OUTPUTS
System.Data.DataTable
The selected database item(s).
#>
function Select-ScoopDBItem {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0, ValueFromPipeline)]
[AllowEmptyString()]
[string]
$Pattern,
[Parameter(Mandatory, Position = 1)]
[ValidateNotNullOrEmpty()]
[string[]]
$From
)
begin {
$db = Open-ScoopDB
$dbAdapter = New-Object -TypeName System.Data.SQLite.SQLiteDataAdapter
$result = New-Object System.Data.DataTable
$dbQuery = "SELECT * FROM app WHERE $(($From -join ' LIKE @Pattern OR ') + ' LIKE @Pattern')"
$dbQuery = "SELECT * FROM ($($dbQuery + ' ORDER BY version DESC')) GROUP BY name, bucket"
$dbCommand = $db.CreateCommand()
$dbCommand.CommandText = $dbQuery
$dbCommand.CommandType = [System.Data.CommandType]::Text
$dbAdapter.SelectCommand = $dbCommand
}
process {
$dbCommand.Parameters.AddWithValue('@Pattern', $(if ($Pattern -eq '') { '%' } else { '%' + $Pattern + '%' })) | Out-Null
[void]$dbAdapter.Fill($result)
}
end {
$dbAdapter.Dispose()
$db.Dispose()
return $result
}
}
<#
.SYNOPSIS
Get Scoop database item.
.DESCRIPTION
Get item from the Scoop SQLite database.
.PARAMETER Name
System.String
The name of the item to get.
.PARAMETER Bucket
System.String
The bucket of the item to get.
.PARAMETER Version
System.String
The version of the item to get. If not provided, the latest version will be returned.
.INPUTS
System.String
.OUTPUTS
System.Data.DataTable
The selected database item.
#>
function Get-ScoopDBItem {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0, ValueFromPipeline)]
[string]
$Name,
[Parameter(Mandatory, Position = 1)]
[string]
$Bucket,
[Parameter(Position = 2)]
[string]
$Version
)
begin {
$db = Open-ScoopDB
$dbAdapter = New-Object -TypeName System.Data.SQLite.SQLiteDataAdapter
$result = New-Object System.Data.DataTable
$dbQuery = 'SELECT * FROM app WHERE name = @Name AND bucket = @Bucket'
if ($Version) {
$dbQuery += ' AND version = @Version'
} else {
$dbQuery += ' ORDER BY version DESC LIMIT 1'
}
$dbCommand = $db.CreateCommand()
$dbCommand.CommandText = $dbQuery
$dbCommand.CommandType = [System.Data.CommandType]::Text
$dbAdapter.SelectCommand = $dbCommand
}
process {
$dbCommand.Parameters.AddWithValue('@Name', $Name) | Out-Null
$dbCommand.Parameters.AddWithValue('@Bucket', $Bucket) | Out-Null
$dbCommand.Parameters.AddWithValue('@Version', $Version) | Out-Null
[void]$dbAdapter.Fill($result)
}
end {
$dbAdapter.Dispose()
$db.Dispose()
return $result
}
}
<#
.SYNOPSIS
Remove Scoop database item(s).
.DESCRIPTION
Remove item(s) from the Scoop SQLite database.
.PARAMETER Name
System.String
The name of the item to remove.
.PARAMETER Bucket
System.String
The bucket of the item to remove.
.INPUTS
System.String
.OUTPUTS
None
#>
function Remove-ScoopDBItem {
[CmdletBinding()]
param (
[Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[string]
$Name,
[Parameter(Mandatory, Position = 1, ValueFromPipelineByPropertyName)]
[string]
$Bucket
)
begin {
$db = Open-ScoopDB
$dbTrans = $db.BeginTransaction()
$dbQuery = 'DELETE FROM app WHERE bucket = @Bucket'
$dbCommand = $db.CreateCommand()
$dbCommand.CommandText = $dbQuery
$dbCommand.CommandType = [System.Data.CommandType]::Text
}
process {
$dbCommand.Parameters.AddWithValue('@Bucket', $Bucket) | Out-Null
if ($Name) {
$dbCommand.CommandText = $dbQuery + ' AND name = @Name'
$dbCommand.Parameters.AddWithValue('@Name', $Name) | Out-Null
}
$dbCommand.ExecuteNonQuery() | Out-Null
}
end {
try {
$dbTrans.Commit()
} catch {
$dbTrans.Rollback()
throw $_
} finally {
$dbCommand.Dispose()
$dbTrans.Dispose()
$db.Dispose()
}
}
}

View File

@@ -1,3 +1,67 @@
# Description: Functions for decompressing archives or installers
function Invoke-Extraction {
param (
[string]
$Path,
[string[]]
$Name,
[psobject]
$Manifest,
[Alias('Arch', 'Architecture')]
[string]
$ProcessorArchitecture
)
$uri = @(url $Manifest $ProcessorArchitecture)
# 'extract_dir' and 'extract_to' are paired
$extractDir = @(extract_dir $Manifest $ProcessorArchitecture)
$extractTo = @(extract_to $Manifest $ProcessorArchitecture)
$extracted = 0
for ($i = 0; $i -lt $Name.Length; $i++) {
# work out extraction method, if applicable
$extractFn = $null
switch -regex ($Name[$i]) {
'\.zip$' {
if ((Test-HelperInstalled -Helper 7zip) -or ((get_config 7ZIPEXTRACT_USE_EXTERNAL) -and (Test-CommandAvailable 7z))) {
$extractFn = 'Expand-7zipArchive'
} else {
$extractFn = 'Expand-ZipArchive'
}
continue
}
'\.msi$' {
$extractFn = 'Expand-MsiArchive'
continue
}
'\.exe$' {
if ($Manifest.innosetup) {
$extractFn = 'Expand-InnoArchive'
}
continue
}
{ Test-7zipRequirement -Uri $_ } {
$extractFn = 'Expand-7zipArchive'
continue
}
}
if ($extractFn) {
$fnArgs = @{
Path = Join-Path $Path $Name[$i]
DestinationPath = Join-Path $Path $extractTo[$extracted]
ExtractDir = $extractDir[$extracted]
}
Write-Host 'Extracting ' -NoNewline
Write-Host $(url_remote_filename $uri[$i]) -ForegroundColor Cyan -NoNewline
Write-Host ' ... ' -NoNewline
& $extractFn @fnArgs -Removal
Write-Host 'done.' -ForegroundColor Green
$extracted++
}
}
}
function Expand-7zipArchive {
[CmdletBinding()]
param (
@@ -96,37 +160,9 @@ function Expand-ZstdArchive {
[Switch]
$Removal
)
$ZstdPath = Get-HelperPath -Helper Zstd
$LogPath = Join-Path (Split-Path $Path) 'zstd.log'
$DestinationPath = $DestinationPath.TrimEnd('\')
ensure $DestinationPath | Out-Null
$ArgList = @('-d', $Path, '--output-dir-flat', $DestinationPath, '-f', '-v')
if ($Switches) {
$ArgList += (-split $Switches)
}
if ($Removal) {
# Remove original archive file
$ArgList += '--rm'
}
$Status = Invoke-ExternalCommand $ZstdPath $ArgList -LogPath $LogPath
if (!$Status) {
abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $LogPath)`n$(new_issue_msg $app $bucket 'decompress error')"
}
$IsTar = (strip_ext $Path) -match '\.tar$'
if ($IsTar) {
# Check for tar
$TarFile = Join-Path $DestinationPath (strip_ext (fname $Path))
Expand-7zipArchive -Path $TarFile -DestinationPath $DestinationPath -ExtractDir $ExtractDir -Removal
}
if (!$IsTar -and $ExtractDir) {
movedir (Join-Path $DestinationPath $ExtractDir) $DestinationPath | Out-Null
# Remove temporary directory
Remove-Item "$DestinationPath\$($ExtractDir -replace '[\\/].*')" -Recurse -Force -ErrorAction Ignore
}
if (Test-Path $LogPath) {
Remove-Item $LogPath -Force
}
# TODO: Remove this function after 2024/12/31
Show-DeprecatedWarning $MyInvocation 'Expand-7zipArchive'
Expand-7zipArchive -Path $Path -DestinationPath $DestinationPath -ExtractDir $ExtractDir -Switches $Switches -Removal:$Removal
}
function Expand-MsiArchive {

View File

@@ -118,11 +118,8 @@ function Get-InstallationHelper {
if ($script -like '*Expand-DarkArchive *') {
$helper += 'dark'
}
if ((Test-ZstdRequirement -Uri $url) -or ($script -like '*Expand-ZstdArchive *')) {
$helper += 'zstd'
}
if (!$All) {
'7zip', 'lessmsi', 'innounp', 'dark', 'zstd' | ForEach-Object {
'7zip', 'lessmsi', 'innounp', 'dark' | ForEach-Object {
if (Test-HelperInstalled -Helper $_) {
$helper = $helper -ne $_
}
@@ -144,22 +141,10 @@ function Test-7zipRequirement {
$Uri
)
return ($Uri | Where-Object {
$_ -match '\.((gz)|(tar)|(t[abgpx]z2?)|(lzma)|(bz2?)|(7z)|(001)|(rar)|(iso)|(xz)|(lzh)|(nupkg))(\.[^\d.]+)?$'
$_ -match '\.(001|7z|bz(ip)?2?|gz|img|iso|lzma|lzh|nupkg|rar|tar|t[abgpx]z2?|t?zst|xz)(\.[^\d.]+)?$'
}).Count -gt 0
}
function Test-ZstdRequirement {
[CmdletBinding()]
[OutputType([Boolean])]
param (
[Parameter(Mandatory = $true)]
[AllowNull()]
[String[]]
$Uri
)
return ($Uri | Where-Object { $_ -match '\.zst$' }).Count -gt 0
}
function Test-LessmsiRequirement {
[CmdletBinding()]
[OutputType([Boolean])]

View File

@@ -50,22 +50,23 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
$persist_dir = persistdir $app $global
$fname = Invoke-ScoopDownload $app $version $manifest $bucket $architecture $dir $use_cache $check_hash
Invoke-HookScript -HookType 'pre_install' -Manifest $manifest -Arch $architecture
Invoke-Extraction -Path $dir -Name $fname -Manifest $manifest -ProcessorArchitecture $architecture
Invoke-HookScript -HookType 'pre_install' -Manifest $manifest -ProcessorArchitecture $architecture
run_installer $fname $manifest $architecture $dir $global
Invoke-Installer -Path $dir -Name $fname -Manifest $manifest -ProcessorArchitecture $architecture -AppName $app -Global:$global
ensure_install_dir_not_in_path $dir $global
$dir = link_current $dir
create_shims $manifest $dir $global $architecture
create_startmenu_shortcuts $manifest $dir $global $architecture
install_psmodule $manifest $dir $global
env_add_path $manifest $dir $global $architecture
env_set $manifest $dir $global $architecture
env_set $manifest $global $architecture
# persist data
persist_data $manifest $original_dir $persist_dir
persist_permission $manifest $global
Invoke-HookScript -HookType 'post_install' -Manifest $manifest -Arch $architecture
Invoke-HookScript -HookType 'post_install' -Manifest $manifest -ProcessorArchitecture $architecture
# save info for uninstall
save_installed_manifest $app $bucket $dir $url
@@ -237,7 +238,7 @@ function Invoke-CachedAria2Download ($app, $version, $manifest, $architecture, $
foreach ($url in $urls) {
$data.$url = @{
'target' = "$dir\$(url_filename $url)"
'target' = Join-Path $dir (url_filename $url)
'cachename' = fname (cache_path $app $version $url)
'source' = cache_path $app $version $url
}
@@ -539,21 +540,12 @@ function Invoke-ScoopDownload ($app, $version, $manifest, $bucket, $architecture
# we only want to show this warning once
if (!$use_cache) { warn 'Cache is being ignored.' }
# can be multiple urls: if there are, then installer should go last,
# so that $fname is set properly
# can be multiple urls: if there are, then installer should go first to make 'installer.args' section work
$urls = @(script:url $manifest $architecture)
# can be multiple cookies: they will be used for all HTTP requests.
$cookies = $manifest.cookie
$fname = $null
# extract_dir and extract_to in manifest are like queues: for each url that
# needs to be extracted, will get the next dir from the queue
$extract_dirs = @(extract_dir $manifest $architecture)
$extract_tos = @(extract_to $manifest $architecture)
$extracted = 0
# download first
if (Test-Aria2Enabled) {
Invoke-CachedAria2Download $app $version $manifest $architecture $dir $cookies $use_cache $check_hash
@@ -587,44 +579,7 @@ function Invoke-ScoopDownload ($app, $version, $manifest, $bucket, $architecture
}
}
foreach ($url in $urls) {
$fname = url_filename $url
$extract_dir = $extract_dirs[$extracted]
$extract_to = $extract_tos[$extracted]
# work out extraction method, if applicable
$extract_fn = $null
if ($manifest.innosetup) {
$extract_fn = 'Expand-InnoArchive'
} elseif ($fname -match '\.zip$') {
# Use 7zip when available (more fast)
if (((get_config USE_EXTERNAL_7ZIP) -and (Test-CommandAvailable 7z)) -or (Test-HelperInstalled -Helper 7zip)) {
$extract_fn = 'Expand-7zipArchive'
} else {
$extract_fn = 'Expand-ZipArchive'
}
} elseif ($fname -match '\.msi$') {
$extract_fn = 'Expand-MsiArchive'
} elseif (Test-ZstdRequirement -Uri $fname) {
# Zstd first
$extract_fn = 'Expand-ZstdArchive'
} elseif (Test-7zipRequirement -Uri $fname) {
# 7zip
$extract_fn = 'Expand-7zipArchive'
}
if ($extract_fn) {
Write-Host 'Extracting ' -NoNewline
Write-Host $fname -f Cyan -NoNewline
Write-Host ' ... ' -NoNewline
& $extract_fn -Path "$dir\$fname" -DestinationPath "$dir\$extract_to" -ExtractDir $extract_dir -Removal
Write-Host 'done.' -f Green
$extracted++
}
}
$fname # returns the last downloaded file
return $urls.ForEach({ url_filename $_ })
}
function cookie_header($cookies) {
@@ -696,71 +651,90 @@ function check_hash($file, $hash, $app_name) {
return $true, $null
}
# for dealing with installers
function args($config, $dir, $global) {
if ($config) { return $config | ForEach-Object { (format $_ @{'dir' = $dir; 'global' = $global }) } }
@()
}
function run_installer($fname, $manifest, $architecture, $dir, $global) {
$installer = installer $manifest $architecture
if ($installer.script) {
Write-Output 'Running installer script...'
Invoke-Command ([scriptblock]::Create($installer.script -join "`r`n"))
return
}
if ($installer) {
$prog = "$dir\$(coalesce $installer.file "$fname")"
if (!(is_in_dir $dir $prog)) {
abort "Error in manifest: Installer $prog is outside the app directory."
function Invoke-Installer {
[CmdletBinding()]
param (
[string]
$Path,
[string[]]
$Name,
[psobject]
$Manifest,
[Alias('Arch', 'Architecture')]
[ValidateSet('32bit', '64bit', 'arm64')]
[string]
$ProcessorArchitecture,
[string]
$AppName,
[switch]
$Global,
[switch]
$Uninstall
)
$type = if ($Uninstall) { 'uninstaller' } else { 'installer' }
$installer = arch_specific $type $Manifest $ProcessorArchitecture
if ($installer.file -or $installer.args) {
# Installer filename is either explicit defined ('installer.file') or file name in the first URL
if (!$Name) {
$Name = url_filename @(url $manifest $architecture)
}
$arg = @(args $installer.args $dir $global)
if ($prog.endswith('.ps1')) {
& $prog @arg
$progName = "$Path\$(coalesce $installer.file $Name[0])"
if (!(is_in_dir $Path $progName)) {
abort "Error in manifest: $((Get-Culture).TextInfo.ToTitleCase($type)) $progName is outside the app directory."
} elseif (!(Test-Path $progName)) {
abort "$((Get-Culture).TextInfo.ToTitleCase($type)) $progName is missing."
}
$substitutions = @{
'$dir' = $Path
'$global' = $Global
'$version' = $Manifest.version
}
$fnArgs = substitute $installer.args $substitutions
if ($progName.EndsWith('.ps1')) {
& $progName @fnArgs
} else {
$installed = Invoke-ExternalCommand $prog $arg -Activity 'Running installer...'
if (!$installed) {
abort "Installation aborted. You might need to run 'scoop uninstall $app' before trying again."
}
# Don't remove installer if "keep" flag is set to true
if (!($installer.keep -eq 'true')) {
Remove-Item $prog
}
}
}
}
function run_uninstaller($manifest, $architecture, $dir) {
$uninstaller = uninstaller $manifest $architecture
$version = $manifest.version
if ($uninstaller.script) {
Write-Output 'Running uninstaller script...'
Invoke-Command ([scriptblock]::Create($uninstaller.script -join "`r`n"))
return
}
if ($uninstaller.file) {
$prog = "$dir\$($uninstaller.file)"
$arg = args $uninstaller.args
if (!(is_in_dir $dir $prog)) {
warn "Error in manifest: Installer $prog is outside the app directory, skipping."
$prog = $null
} elseif (!(Test-Path $prog)) {
warn "Uninstaller $prog is missing, skipping."
$prog = $null
}
if ($prog) {
if ($prog.endswith('.ps1')) {
& $prog @arg
} else {
$uninstalled = Invoke-ExternalCommand $prog $arg -Activity 'Running uninstaller...'
if (!$uninstalled) {
$status = Invoke-ExternalCommand $progName -ArgumentList $fnArgs -Activity "Running $type ..."
if (!$status) {
if ($Uninstall) {
abort 'Uninstallation aborted.'
} else {
abort "Installation aborted. You might need to run 'scoop uninstall $AppName' before trying again."
}
}
# Don't remove installer if "keep" flag is set to true
if (!$installer.keep) {
Remove-Item $progName
}
}
}
Invoke-HookScript -HookType $type -Manifest $Manifest -ProcessorArchitecture $ProcessorArchitecture
}
function Invoke-HookScript {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateSet('installer', 'pre_install', 'post_install', 'uninstaller', 'pre_uninstall', 'post_uninstall')]
[String] $HookType,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[PSCustomObject] $Manifest,
[Parameter(Mandatory = $true)]
[Alias('Arch', 'Architecture')]
[ValidateSet('32bit', '64bit', 'arm64')]
[string]
$ProcessorArchitecture
)
$script = arch_specific $HookType $Manifest $ProcessorArchitecture
if ($HookType -in @('installer', 'uninstaller')) {
$script = $script.script
}
if ($script) {
Write-Host "Running $HookType script..." -NoNewline
Invoke-Command ([scriptblock]::Create($script -join "`r`n"))
Write-Host 'done.' -ForegroundColor Green
}
}
# get target, name, arguments for shim
@@ -924,12 +898,12 @@ function env_rm_path($manifest, $dir, $global, $arch) {
}
}
function env_set($manifest, $dir, $global, $arch) {
function env_set($manifest, $global, $arch) {
$env_set = arch_specific 'env_set' $manifest $arch
if ($env_set) {
$env_set | Get-Member -Member NoteProperty | ForEach-Object {
$name = $_.name
$val = format $env_set.$($_.name) @{ 'dir' = $dir }
$env_set | Get-Member -MemberType NoteProperty | ForEach-Object {
$name = $_.Name
$val = $ExecutionContext.InvokeCommand.ExpandString($env_set.$($name))
Set-EnvVar -Name $name -Value $val -Global:$global
Set-Content env:\$name $val
}
@@ -938,36 +912,14 @@ function env_set($manifest, $dir, $global, $arch) {
function env_rm($manifest, $global, $arch) {
$env_set = arch_specific 'env_set' $manifest $arch
if ($env_set) {
$env_set | Get-Member -Member NoteProperty | ForEach-Object {
$name = $_.name
$env_set | Get-Member -MemberType NoteProperty | ForEach-Object {
$name = $_.Name
Set-EnvVar -Name $name -Value $null -Global:$global
if (Test-Path env:\$name) { Remove-Item env:\$name }
}
}
}
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', 'arm64')]
[String] $Arch
)
$script = arch_specific $HookType $Manifest $Arch
if ($script) {
Write-Output "Running $HookType script..."
Invoke-Command ([scriptblock]::Create($script -join "`r`n"))
}
}
function show_notes($manifest, $dir, $original_dir, $persist_dir) {
if ($manifest.notes) {
Write-Output 'Notes'

View File

@@ -110,13 +110,13 @@ function json_path([String] $json, [String] $jsonpath, [Hashtable] $substitution
# Return versions in reverse order
$result = [System.Linq.Enumerable]::Reverse($result)
}
if ($single) {
if ([System.Linq.Enumerable]::Count($result) -eq 1 -or $single) {
# Extract First value
$result = [System.Linq.Enumerable]::First($result)
# Convert first value to string
$result = $result.ToString()
} else {
$result = "$([String]::Join('\n', $result))"
$result = [Newtonsoft.Json.JsonConvert]::SerializeObject($result)
}
return $result
} catch [Exception] {

View File

@@ -23,7 +23,7 @@ function url_manifest($url) {
} catch {
throw
}
if(!$str) { return $null }
if (!$str) { return $null }
try {
$str | ConvertFrom-Json -ErrorAction Stop
} catch {
@@ -137,16 +137,26 @@ function generate_user_manifest($app, $bucket, $version) {
warn "Given version ($version) does not match manifest ($($manifest.version))"
warn "Attempting to generate manifest for '$app' ($version)"
ensure (usermanifestsdir) | Out-Null
$manifest_path = "$(usermanifestsdir)\$app.json"
if (get_config USE_SQLITE_CACHE) {
$cached_manifest = (Get-ScoopDBItem -Name $app -Bucket $bucket -Version $version).manifest
if ($cached_manifest) {
$cached_manifest | Out-UTF8File $manifest_path
return $manifest_path
}
}
if (!($manifest.autoupdate)) {
abort "'$app' does not have autoupdate capability`r`ncouldn't find manifest for '$app@$version'"
}
ensure (usermanifestsdir) | out-null
try {
Invoke-AutoUpdate $app "$(Convert-Path (usermanifestsdir))\$app.json" $manifest $version $(@{ })
return Convert-Path (usermanifest $app)
Invoke-AutoUpdate $app $manifest_path $manifest $version $(@{ })
return $manifest_path
} catch {
write-host -f darkred "Could not install $app@$version"
Write-Host -ForegroundColor DarkRed "Could not install $app@$version"
}
return $null
@@ -156,5 +166,5 @@ function url($manifest, $arch) { arch_specific 'url' $manifest $arch }
function installer($manifest, $arch) { arch_specific 'installer' $manifest $arch }
function uninstaller($manifest, $arch) { arch_specific 'uninstaller' $manifest $arch }
function hash($manifest, $arch) { arch_specific 'hash' $manifest $arch }
function extract_dir($manifest, $arch) { arch_specific 'extract_dir' $manifest $arch}
function extract_to($manifest, $arch) { arch_specific 'extract_to' $manifest $arch}
function extract_dir($manifest, $arch) { arch_specific 'extract_dir' $manifest $arch }
function extract_to($manifest, $arch) { arch_specific 'extract_to' $manifest $arch }

View File

@@ -1,117 +1,68 @@
# Usage: scoop alias add|list|rm [<args>]
# Usage: scoop alias <subcommand> [options] [<args>]
# Summary: Manage scoop aliases
# Help: Add, remove or list Scoop aliases
# Help: Available subcommands: add, rm, list.
#
# Aliases are custom Scoop subcommands that can be created to make common tasks
# easier.
# Aliases are custom Scoop subcommands that can be created to make common tasks easier.
#
# To add an Alias:
# scoop alias add <name> <command> <description>
# To add an alias:
#
# e.g.:
# scoop alias add rm 'scoop uninstall $args[0]' 'Uninstalls an app'
# scoop alias add upgrade 'scoop update *' 'Updates all apps, just like brew or apt'
# scoop alias add <name> <command> [<description>]
#
# e.g.,
#
# scoop alias add rm 'scoop uninstall $args[0]' 'Uninstall an app'
# scoop alias add upgrade 'scoop update *' 'Update all apps, just like "brew" or "apt"'
#
# To remove an alias:
#
# scoop alias rm <name>
#
# To list all aliases:
#
# scoop alias list [-v|--verbose]
#
# Options:
# -v, --verbose Show alias description and table headers (works only for 'list')
# -v, --verbose Show alias description and table headers (works only for "list")
param(
[String]$opt,
[String]$name,
[String]$command,
[String]$description,
[Switch]$verbose = $false
)
param($SubCommand)
. "$PSScriptRoot\..\lib\install.ps1" # shim related
. "$PSScriptRoot\..\lib\getopt.ps1"
$script:config_alias = 'alias'
function init_alias_config {
$aliases = get_config $script:config_alias
if ($aliases) {
$aliases
$SubCommands = @('add', 'rm', 'list')
if ($SubCommand -notin $SubCommands) {
if (!$SubCommand) {
error '<subcommand> missing'
} else {
New-Object -TypeName PSObject
error "'$SubCommand' is not one of available subcommands: $($SubCommands -join ', ')"
}
my_usage
exit 1
}
function add_alias($name, $command) {
if (!$command) {
abort "Can't create an empty alias."
$opt, $other, $err = getopt $Args 'v' 'verbose'
if ($err) { "scoop alias: $err"; exit 1 }
$name, $command, $description = $other
$verbose = $opt.v -or $opt.verbose
switch ($SubCommand) {
'add' {
if (!$name -or !$command) {
error "<name> and <command> must be specified for subcommand 'add'"
exit 1
}
add_alias $name $command $description
}
# get current aliases from config
$aliases = init_alias_config
if ($aliases.$name) {
abort "Alias '$name' already exists."
'rm' {
if (!$name) {
error "<name> must be specified for subcommand 'rm'"
exit 1
}
rm_alias $name
}
$alias_file = "scoop-$name"
# generate script
$shimdir = shimdir $false
if (Test-Path "$shimdir\$alias_file.ps1") {
abort "File '$alias_file.ps1' already exists in shims directory."
'list' {
list_aliases $verbose
}
$script =
@(
"# Summary: $description",
"$command"
) -join "`r`n"
$script | Out-UTF8File "$shimdir\$alias_file.ps1"
# add alias to config
$aliases | Add-Member -MemberType NoteProperty -Name $name -Value $alias_file
set_config $script:config_alias $aliases | Out-Null
}
function rm_alias($name) {
$aliases = init_alias_config
if (!$name) {
abort 'Alias to be removed has not been specified!'
}
if ($aliases.$name) {
info "Removing alias '$name'..."
rm_shim $aliases.$name (shimdir $false)
$aliases.PSObject.Properties.Remove($name)
set_config $script:config_alias $aliases | Out-Null
} else {
abort "Alias '$name' doesn't exist."
}
}
function list_aliases {
$aliases = @()
(init_alias_config).PSObject.Properties.GetEnumerator() | ForEach-Object {
$content = Get-Content (command_path $_.Name)
$command = ($content | Select-Object -Skip 1).Trim()
$summary = (summary $content).Trim()
$aliases += New-Object psobject -Property @{Name = $_.name; Summary = $summary; Command = $command }
}
if (!$aliases.count) {
info "No alias found."
}
$aliases = $aliases.GetEnumerator() | Sort-Object Name
if ($verbose) {
return $aliases | Select-Object Name, Command, Summary
} else {
return $aliases | Select-Object Name, Command
}
}
switch ($opt) {
'add' { add_alias $name $command }
'rm' { rm_alias $name }
'list' { list_aliases }
default { my_usage; exit 1 }
}
exit 0

View File

@@ -19,6 +19,11 @@
# scoop bucket known
param($cmd, $name, $repo)
if (get_config USE_SQLITE_CACHE) {
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\database.ps1"
}
$usage_add = 'usage: scoop bucket add <name> [<repo>]'
$usage_rm = 'usage: scoop bucket rm <name>'

View File

@@ -16,7 +16,7 @@ param($cmd)
function cacheinfo($file) {
$app, $version, $url = $file.Name -split '#'
New-Object PSObject -Property @{ Name = $app; Version = $version; Length = $file.Length; URL = $url }
New-Object PSObject -Property @{ Name = $app; Version = $version; Length = $file.Length }
}
function cacheshow($app) {
@@ -28,7 +28,7 @@ function cacheshow($app) {
$files = @(Get-ChildItem $cachedir | Where-Object -Property Name -Value "^$app#" -Match)
$totalLength = ($files | Measure-Object -Property Length -Sum).Sum
$files | ForEach-Object { cacheinfo $_ } | Select-Object Name, Version, Length, URL
$files | ForEach-Object { cacheinfo $_ } | Select-Object Name, Version, Length
Write-Host "Total: $($files.Length) $(pluralize $files.Length 'file' 'files'), $(filesize $totalLength)" -ForegroundColor Yellow
}
@@ -48,7 +48,7 @@ function cacheremove($app) {
$files | ForEach-Object {
$curr = cacheinfo $_
Write-Host "Removing $($curr.URL)..."
Write-Host "Removing $($_.Name)..."
Remove-Item $_.FullName
if(Test-Path "$cachedir\$($curr.Name).txt") {
Remove-Item "$cachedir\$($curr.Name).txt"

View File

@@ -27,6 +27,9 @@
# use_lessmsi: $true|$false
# Prefer lessmsi utility over native msiexec.
#
# use_sqlite_cache: $true|$false
# Use SQLite database for caching. This is useful for speeding up 'scoop search' and 'scoop shim' commands.
#
# no_junction: $true|$false
# The 'current' version alias will not be used. Shims and shortcuts will point to specific version instead.
#

View File

@@ -15,7 +15,7 @@
#
# Options:
# -f, --force Force download (overwrite cache)
# -h, --no-hash-check Skip hash verification (use with caution!)
# -s, --skip-hash-check Skip hash verification (use with caution!)
# -u, --no-update-scoop Don't update Scoop before downloading if it's outdated
# -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it
@@ -24,11 +24,14 @@
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest'
. "$PSScriptRoot\..\lib\install.ps1"
if (get_config USE_SQLITE_CACHE) {
. "$PSScriptRoot\..\lib\database.ps1"
}
$opt, $apps, $err = getopt $args 'fhua:' 'force', 'no-hash-check', 'no-update-scoop', 'arch='
$opt, $apps, $err = getopt $args 'fsua:' 'force', 'skip-hash-check', 'no-update-scoop', 'arch='
if ($err) { error "scoop download: $err"; exit 1 }
$check_hash = !($opt.h -or $opt.'no-hash-check')
$check_hash = !($opt.s -or $opt.'skip-hash-check')
$use_cache = !($opt.f -or $opt.force)
$architecture = Get-DefaultArchitecture
try {

View File

@@ -29,14 +29,13 @@ if ($global -and !(is_admin)) {
exit 1
}
$apps | ForEach-Object {
$app = $_
foreach ($app in $apps) {
if ($app -eq 'scoop') {
$hold_update_until = [System.DateTime]::Now.AddDays(1)
set_config HOLD_UPDATE_UNTIL $hold_update_until.ToString('o') | Out-Null
success "$app is now held and might not be updated until $($hold_update_until.ToLocalTime())."
return
continue
}
if (!(installed $app $global)) {
if ($global) {
@@ -44,7 +43,7 @@ $apps | ForEach-Object {
} else {
error "'$app' is not installed."
}
return
continue
}
if (get_config NO_JUNCTION) {

View File

@@ -166,7 +166,7 @@ if ($status.installed) {
$cached = $null
}
[int]$urlLength = (Invoke-WebRequest $url -Method Head).Headers.'Content-Length'[0]
$urlLength = (Invoke-WebRequest $url -Method Head).Headers.'Content-Length' | ForEach-Object { [int]$_ }
$totalPackage += $urlLength
} catch [System.Management.Automation.RuntimeException] {
$totalPackage = 0
@@ -210,7 +210,7 @@ $env_set = arch_specific 'env_set' $manifest $install.architecture
if ($env_set) {
$env_vars = @()
$env_set | Get-Member -member noteproperty | ForEach-Object {
$env_vars += "$($_.name) = $(format $env_set.$($_.name) @{ "dir" = $dir })"
$env_vars += "$($_.name) = $(substitute $env_set.$($_.name) @{ '$dir' = $dir })"
}
$item.Environment = $env_vars -join "`n"
}

View File

@@ -10,15 +10,21 @@
# To install an app from a manifest at a URL:
# scoop install https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/runat.json
#
# To install a different version of the app from a URL:
# scoop install https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/neovim.json@0.9.0
#
# To install an app from a manifest on your computer
# scoop install \path\to\app.json
#
# To install an app from a manifest on your computer
# scoop install \path\to\app.json@version
#
# Options:
# -g, --global Install the app globally
# -i, --independent Don't install dependencies automatically
# -k, --no-cache Don't use the download cache
# -s, --skip-hash-check Skip hash validation (use with caution!)
# -u, --no-update-scoop Don't update Scoop before installing if it's outdated
# -s, --skip Skip hash validation (use with caution!)
# -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it
. "$PSScriptRoot\..\lib\getopt.ps1"
@@ -32,12 +38,15 @@
. "$PSScriptRoot\..\lib\psmodules.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
. "$PSScriptRoot\..\lib\depends.ps1"
if (get_config USE_SQLITE_CACHE) {
. "$PSScriptRoot\..\lib\database.ps1"
}
$opt, $apps, $err = getopt $args 'gikusa:' 'global', 'independent', 'no-cache', 'no-update-scoop', 'skip', 'arch='
$opt, $apps, $err = getopt $args 'giksua:' 'global', 'independent', 'no-cache', 'skip-hash-check', 'no-update-scoop', 'arch='
if ($err) { "scoop install: $err"; exit 1 }
$global = $opt.g -or $opt.global
$check_hash = !($opt.s -or $opt.skip)
$check_hash = !($opt.s -or $opt.'skip-hash-check')
$independent = $opt.i -or $opt.independent
$use_cache = !($opt.k -or $opt.'no-cache')
$architecture = Get-DefaultArchitecture
@@ -82,7 +91,7 @@ $specific_versions = $apps | Where-Object {
}
# compare object does not like nulls
if ($specific_versions.length -gt 0) {
if ($specific_versions.Count -gt 0) {
$difference = Compare-Object -ReferenceObject $apps -DifferenceObject $specific_versions -PassThru
} else {
$difference = $apps
@@ -91,13 +100,13 @@ if ($specific_versions.length -gt 0) {
$specific_versions_paths = $specific_versions | ForEach-Object {
$app, $bucket, $version = parse_app $_
if (installed_manifest $app $version) {
warn "'$app' ($version) is already installed.`nUse 'scoop update $app$(if ($global) { " --global" })' to install a new version."
warn "'$app' ($version) is already installed.`nUse 'scoop update $app$(if ($global) { ' --global' })' to install a new version."
continue
}
generate_user_manifest $app $bucket $version
}
$apps = @(($specific_versions_paths + $difference) | Where-Object { $_ } | Sort-Object -Unique)
$apps = @((@($specific_versions_paths) + $difference) | Where-Object { $_ } | Select-Object -Unique)
# remember which were explictly requested so that we can
# differentiate after dependencies are added

View File

@@ -84,7 +84,7 @@ $apps | ForEach-Object {
env_rm_path $manifest $dir $global $architecture
env_rm $manifest $global $architecture
env_add_path $manifest $dir $global $architecture
env_set $manifest $dir $global $architecture
env_set $manifest $global $architecture
# unlink all potential old link before re-persisting
unlink_persist_data $manifest $original_dir
persist_data $manifest $original_dir $persist_dir

View File

@@ -3,6 +3,8 @@
# Help: Searches for apps that are available to install.
#
# If used with [query], shows app names that match the query.
# - With 'use_sqlite_cache' enabled, [query] is partially matched against app names, binaries, and shortcuts.
# - Without 'use_sqlite_cache', [query] can be a regular expression to match against app names and binaries.
# Without [query], shows all the available apps.
param($query)
@@ -11,16 +13,10 @@ param($query)
$list = [System.Collections.Generic.List[PSCustomObject]]::new()
try {
$query = New-Object Regex $query, 'IgnoreCase'
} catch {
abort "Invalid regular expression: $($_.Exception.InnerException.Message)"
}
$githubtoken = Get-GitHubToken
$authheader = @{}
if ($githubtoken) {
$authheader = @{'Authorization' = "token $githubtoken"}
$authheader = @{'Authorization' = "token $githubtoken" }
}
function bin_match($manifest, $query) {
@@ -39,16 +35,16 @@ function bin_match($manifest, $query) {
function bin_match_json($json, $query) {
[System.Text.Json.JsonElement]$bin = [System.Text.Json.JsonElement]::new()
if (!$json.RootElement.TryGetProperty("bin", [ref] $bin)) { return $false }
if (!$json.RootElement.TryGetProperty('bin', [ref] $bin)) { return $false }
$bins = @()
if($bin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($bin) -match $query) {
if ($bin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($bin) -match $query) {
$bins += [System.IO.Path]::GetFileName($bin)
} elseif ($bin.ValueKind -eq [System.Text.Json.JsonValueKind]::Array) {
foreach($subbin in $bin.EnumerateArray()) {
if($subbin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($subbin) -match $query) {
foreach ($subbin in $bin.EnumerateArray()) {
if ($subbin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($subbin) -match $query) {
$bins += [System.IO.Path]::GetFileName($subbin)
} elseif ($subbin.ValueKind -eq [System.Text.Json.JsonValueKind]::Array) {
if([System.IO.Path]::GetFileNameWithoutExtension($subbin[0]) -match $query) {
if ([System.IO.Path]::GetFileNameWithoutExtension($subbin[0]) -match $query) {
$bins += [System.IO.Path]::GetFileName($subbin[0])
} elseif ($subbin.GetArrayLength() -ge 2 -and $subbin[1] -match $query) {
$bins += $subbin[1]
@@ -65,25 +61,33 @@ function search_bucket($bucket, $query) {
$apps = Get-ChildItem (Find-BucketDirectory $bucket) -Filter '*.json' -Recurse
$apps | ForEach-Object {
$json = [System.Text.Json.JsonDocument]::Parse([System.IO.File]::ReadAllText($_.FullName))
$filepath = $_.FullName
$json = try {
[System.Text.Json.JsonDocument]::Parse([System.IO.File]::ReadAllText($filepath))
} catch {
debug "Failed to parse manifest file: $filepath (error: $_)"
return
}
$name = $_.BaseName
if ($name -match $query) {
$list.Add([PSCustomObject]@{
Name = $name
Version = $json.RootElement.GetProperty("version")
Source = $bucket
Binaries = ""
})
Name = $name
Version = $json.RootElement.GetProperty('version')
Source = $bucket
Binaries = ''
})
} else {
$bin = bin_match_json $json $query
if ($bin) {
$list.Add([PSCustomObject]@{
Name = $name
Version = $json.RootElement.GetProperty("version")
Source = $bucket
Binaries = $bin -join ' | '
})
Name = $name
Version = $json.RootElement.GetProperty('version')
Source = $bucket
Binaries = $bin -join ' | '
})
}
}
}
@@ -99,20 +103,20 @@ function search_bucket_legacy($bucket, $query) {
if ($name -match $query) {
$list.Add([PSCustomObject]@{
Name = $name
Version = $manifest.Version
Source = $bucket
Binaries = ""
})
Name = $name
Version = $manifest.Version
Source = $bucket
Binaries = ''
})
} else {
$bin = bin_match $manifest $query
if ($bin) {
$list.Add([PSCustomObject]@{
Name = $name
Version = $manifest.Version
Source = $bucket
Binaries = $bin -join ' | '
})
Name = $name
Version = $manifest.Version
Source = $bucket
Binaries = $bin -join ' | '
})
}
}
}
@@ -154,7 +158,7 @@ function search_remotes($query) {
$names = $buckets | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name
$results = $names | Where-Object { !(Test-Path $(Find-BucketDirectory $_)) } | ForEach-Object {
@{ "bucket" = $_; "results" = (search_remote $_ $query) }
@{ 'bucket' = $_; 'results' = (search_remote $_ $query) }
} | Where-Object { $_.results }
if ($results.count -gt 0) {
@@ -175,25 +179,45 @@ function search_remotes($query) {
$remote_list
}
$jsonTextAvailable = [System.AppDomain]::CurrentDomain.GetAssemblies() | Where-object { [System.IO.Path]::GetFileNameWithoutExtension($_.Location) -eq "System.Text.Json" }
if (get_config USE_SQLITE_CACHE) {
. "$PSScriptRoot\..\lib\database.ps1"
Select-ScoopDBItem $query -From @('name', 'binary', 'shortcut') |
Select-Object -Property name, version, bucket, binary |
ForEach-Object {
$list.Add([PSCustomObject]@{
Name = $_.name
Version = $_.version
Source = $_.bucket
Binaries = $_.binary
})
}
} else {
try {
$query = New-Object Regex $query, 'IgnoreCase'
} catch {
abort "Invalid regular expression: $($_.Exception.InnerException.Message)"
}
Get-LocalBucket | ForEach-Object {
if ($jsonTextAvailable) {
search_bucket $_ $query
} else {
search_bucket_legacy $_ $query
$jsonTextAvailable = [System.AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { [System.IO.Path]::GetFileNameWithoutExtension($_.Location) -eq 'System.Text.Json' }
Get-LocalBucket | ForEach-Object {
if ($jsonTextAvailable) {
search_bucket $_ $query
} else {
search_bucket_legacy $_ $query
}
}
}
if ($list.Count -gt 0) {
Write-Host "Results from local buckets..."
Write-Host 'Results from local buckets...'
$list
}
if ($list.Count -eq 0 -and !(github_ratelimit_reached)) {
$remote_results = search_remotes $query
if (!$remote_results) {
warn "No matches found."
warn 'No matches found.'
exit 1
}
$remote_results

View File

@@ -74,7 +74,7 @@ if (!$apps) { exit 0 }
continue
}
run_uninstaller $manifest $architecture $dir
Invoke-Installer -Path $dir -Manifest $manifest -ProcessorArchitecture $architecture -Uninstall
rm_shims $app $manifest $global $architecture
rm_startmenu_shortcuts $manifest $global $architecture

View File

@@ -6,13 +6,13 @@
# You can use '*' in place of <app> to update all apps.
#
# Options:
# -f, --force Force update even when there isn't a newer version
# -g, --global Update a globally installed app
# -i, --independent Don't install dependencies automatically
# -k, --no-cache Don't use the download cache
# -s, --skip Skip hash validation (use with caution!)
# -q, --quiet Hide extraneous messages
# -a, --all Update all apps (alternative to '*')
# -f, --force Force update even when there isn't a newer version
# -g, --global Update a globally installed app
# -i, --independent Don't install dependencies automatically
# -k, --no-cache Don't use the download cache
# -s, --skip-hash-check Skip hash validation (use with caution!)
# -q, --quiet Hide extraneous messages
# -a, --all Update all apps (alternative to '*')
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1" # 'save_install_info' in 'manifest.ps1' (indirectly)
@@ -24,12 +24,15 @@
. "$PSScriptRoot\..\lib\versions.ps1"
. "$PSScriptRoot\..\lib\depends.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
if (get_config USE_SQLITE_CACHE) {
. "$PSScriptRoot\..\lib\database.ps1"
}
$opt, $apps, $err = getopt $args 'gfiksqa' 'global', 'force', 'independent', 'no-cache', 'skip', 'quiet', 'all'
$opt, $apps, $err = getopt $args 'gfiksqa' 'global', 'force', 'independent', 'no-cache', 'skip-hash-check', 'quiet', 'all'
if ($err) { "scoop update: $err"; exit 1 }
$global = $opt.g -or $opt.global
$force = $opt.f -or $opt.force
$check_hash = !($opt.s -or $opt.skip)
$check_hash = !($opt.s -or $opt.'skip-hash-check')
$use_cache = !($opt.k -or $opt.'no-cache')
$quiet = $opt.q -or $opt.quiet
$independent = $opt.i -or $opt.independent
@@ -38,21 +41,21 @@ $all = $opt.a -or $opt.all
# load config
$configRepo = get_config SCOOP_REPO
if (!$configRepo) {
$configRepo = "https://github.com/ScoopInstaller/Scoop"
$configRepo = 'https://github.com/ScoopInstaller/Scoop'
set_config SCOOP_REPO $configRepo | Out-Null
}
# Find current update channel from config
$configBranch = get_config SCOOP_BRANCH
if (!$configBranch) {
$configBranch = "master"
$configBranch = 'master'
set_config SCOOP_BRANCH $configBranch | Out-Null
}
if(($PSVersionTable.PSVersion.Major) -lt 5) {
if (($PSVersionTable.PSVersion.Major) -lt 5) {
# check powershell version
Write-Output "PowerShell 5 or later is required to run Scoop."
Write-Output "Upgrade PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows"
Write-Output 'PowerShell 5 or later is required to run Scoop.'
Write-Output 'Upgrade PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows'
break
}
$show_update_log = get_config SHOW_UPDATE_LOG $true
@@ -63,14 +66,14 @@ function Sync-Scoop {
[Switch]$Log
)
# Test if Scoop Core is hold
if(Test-ScoopCoreOnHold) {
if (Test-ScoopCoreOnHold) {
return
}
# check for git
if (!(Test-GitAvailable)) { abort "Scoop uses Git to update itself. Run 'scoop install git' and try again." }
Write-Host "Updating Scoop..."
Write-Host 'Updating Scoop...'
$currentdir = versiondir 'scoop' 'current'
if (!(Test-Path "$currentdir\.git")) {
$newdir = "$currentdir\..\new"
@@ -108,10 +111,10 @@ function Sync-Scoop {
# Stash uncommitted changes
if (Invoke-Git -Path $currentdir -ArgumentList @('diff', 'HEAD', '--name-only')) {
if (get_config AUTOSTASH_ON_CONFLICT) {
warn "Uncommitted changes detected. Stashing..."
warn 'Uncommitted changes detected. Stashing...'
Invoke-Git -Path $currentdir -ArgumentList @('stash', 'push', '-m', "WIP at $([System.DateTime]::Now.ToString('o'))", '-u', '-q')
} else {
warn "Uncommitted changes detected. Update aborted."
warn 'Uncommitted changes detected. Update aborted.'
return
}
}
@@ -152,7 +155,7 @@ function Sync-Bucket {
Param (
[Switch]$Log
)
Write-Host "Updating Buckets..."
Write-Host 'Updating Buckets...'
if (!(Test-Path (Join-Path (Find-BucketDirectory 'main' -Root) '.git'))) {
info "Converting 'main' bucket to git repo..."
@@ -170,40 +173,88 @@ function Sync-Bucket {
$buckets = Get-LocalBucket | ForEach-Object {
$path = Find-BucketDirectory $_ -Root
return @{
name = $_
name = $_
valid = Test-Path (Join-Path $path '.git')
path = $path
path = $path
}
}
$buckets | Where-Object { !$_.valid } | ForEach-Object { Write-Host "'$($_.name)' is not a git repository. Skipped." }
$updatedFiles = [System.Collections.ArrayList]::Synchronized([System.Collections.ArrayList]::new())
$removedFiles = [System.Collections.ArrayList]::Synchronized([System.Collections.ArrayList]::new())
if ($PSVersionTable.PSVersion.Major -ge 7) {
# Parallel parameter is available since PowerShell 7
$buckets | Where-Object { $_.valid } | ForEach-Object -ThrottleLimit 5 -Parallel {
. "$using:PSScriptRoot\..\lib\core.ps1"
. "$using:PSScriptRoot\..\lib\buckets.ps1"
$bucketLoc = $_.path
$name = $_.name
$bucketLoc = $_.path
$innerBucketLoc = Find-BucketDirectory $name
$previousCommit = Invoke-Git -Path $bucketLoc -ArgumentList @('rev-parse', 'HEAD')
Invoke-Git -Path $bucketLoc -ArgumentList @('pull', '-q')
if ($using:Log) {
Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit
}
if (get_config USE_SQLITE_CACHE) {
Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-status', $previousCommit) | ForEach-Object {
$status, $file = $_ -split '\s+', 2
$filePath = Join-Path $bucketLoc $file
if ($filePath -match "^$([regex]::Escape($innerBucketLoc)).*\.json$") {
switch ($status) {
{ $_ -in 'A', 'M', 'R' } {
[void]($using:updatedFiles).Add($filePath)
}
'D' {
[void]($using:removedFiles).Add([pscustomobject]@{
Name = ([System.IO.FileInfo]$file).BaseName
Bucket = $name
})
}
}
}
}
}
}
} else {
$buckets | Where-Object { $_.valid } | ForEach-Object {
$bucketLoc = $_.path
$name = $_.name
$bucketLoc = $_.path
$innerBucketLoc = Find-BucketDirectory $name
$previousCommit = Invoke-Git -Path $bucketLoc -ArgumentList @('rev-parse', 'HEAD')
Invoke-Git -Path $bucketLoc -ArgumentList @('pull', '-q')
if ($Log) {
Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit
}
if (get_config USE_SQLITE_CACHE) {
Invoke-Git -Path $bucketLoc -ArgumentList @('diff', '--name-status', $previousCommit) | ForEach-Object {
$status, $file = $_ -split '\s+', 2
$filePath = Join-Path $bucketLoc $file
if ($filePath -match "^$([regex]::Escape($innerBucketLoc)).*\.json$") {
switch ($status) {
{ $_ -in 'A', 'M', 'R' } {
[void]($updatedFiles).Add($filePath)
}
'D' {
[void]($removedFiles).Add([pscustomobject]@{
Name = ([System.IO.FileInfo]$file).BaseName
Bucket = $name
})
}
}
}
}
}
}
}
if ((get_config USE_SQLITE_CACHE) -and ($updatedFiles.Count -gt 0 -or $removedFiles.Count -gt 0)) {
info 'Updating cache...'
Set-ScoopDB -Path $updatedFiles
$removedFiles | Remove-ScoopDBItem
}
}
function update($app, $global, $quiet = $false, $independent, $suggested, $use_cache = $true, $check_hash = $true) {
@@ -251,7 +302,7 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
# region Workaround
# Workaround for https://github.com/ScoopInstaller/Scoop/issues/2220 until install is refactored
# Remove and replace whole region after proper fix
Write-Host "Downloading new version"
Write-Host 'Downloading new version'
if (Test-Aria2Enabled) {
Invoke-CachedAria2Download $app $version $manifest $architecture $cachedir $manifest.cookie $true $check_hash
} else {
@@ -269,12 +320,12 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
error $err
if (Test-Path $source) {
# rm cached file
Remove-Item -force $source
Remove-Item -Force $source
}
if ($url.Contains('sourceforge.net')) {
Write-Host -f yellow 'SourceForge.net is known for causing hash validation fails. Please try again before opening a ticket.'
}
abort $(new_issue_msg $app $bucket "hash check failed")
abort $(new_issue_msg $app $bucket 'hash check failed')
}
}
}
@@ -289,7 +340,7 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
Invoke-HookScript -HookType 'pre_uninstall' -Manifest $old_manifest -Arch $architecture
Write-Host "Uninstalling '$app' ($old_version)"
run_uninstaller $old_manifest $architecture $dir
Invoke-Installer -Path $dir -Manifest $old_manifest -ProcessorArchitecture $architecture -Uninstall
rm_shims $app $old_manifest $global $architecture
# If a junction was used during install, that will have been used
@@ -404,11 +455,11 @@ if (-not ($apps -or $all)) {
} elseif ($outdated.Length -eq 0) {
Write-Host -f Green "Latest versions for all apps are installed! For more information try 'scoop status'"
} else {
Write-Host -f DarkCyan "Updating one outdated app:"
Write-Host -f DarkCyan 'Updating one outdated app:'
}
}
$suggested = @{};
$suggested = @{}
# $outdated is a list of ($app, $global) tuples
$outdated | ForEach-Object { update @_ $quiet $independent $suggested $use_cache $check_hash }
}

View File

@@ -36,8 +36,9 @@
$opt, $apps, $err = getopt $args 'asnup' @('all', 'scan', 'no-depends', 'no-update-scoop', 'passthru')
if ($err) { "scoop virustotal: $err"; exit 1 }
if (!$apps -and -$all) { my_usage; exit 1 }
$architecture = Format-ArchitectureString
$all = $apps -eq '*' -or $opt.a -or $opt.all
if (!$apps -and !$all) { my_usage; exit 1 }
$architecture = Get-DefaultArchitecture
if (is_scoop_outdated) {
if ($opt.u -or $opt.'no-update-scoop') {
@@ -47,11 +48,8 @@ if (is_scoop_outdated) {
}
}
$apps_param = $apps
if ($apps_param -eq '*' -or $opt.a -or $opt.all) {
$apps = installed_apps $false
$apps += installed_apps $true
if ($all) {
$apps = (installed_apps $false) + (installed_apps $true)
}
if (!$opt.n -and !$opt.'no-depends') {

View File

@@ -1,4 +1,4 @@
e1e27af7b07eeedf5ce71a9255f0422816a6fc5849a483c6714e1b472044fa9d *Newtonsoft.Json.dll
9f1a8f06c284a4ee01f704d89003ddc7061846f2008094071e9adf08267849f9 *Newtonsoft.Json.Schema.dll
d11b660612ce821ec03772b73aa3b8884a0479275c70085c7e143913a41a2d28 *Scoop.Validator.dll
7496d5349a123a6e3696085662b2ff17b156ccdb0e30e0c396ac72d2da36ce1c *Newtonsoft.Json.Schema.dll
83b1006443e8c340ca4c631614fc2ce0d5cb9a28c851e3b59724299f58b1397f *Scoop.Validator.dll
87f8f8db2202a3fbef6f431d0b7e20cec9d32095c441927402041f3c4076c1b6 *validator.exe

View File

@@ -1,4 +1,4 @@
56eb7f070929b239642dab729537dde2c2287bdb852ad9e80b5358c74b14bc2b2dded910d0e3b6304ea27eb587e5f19db0a92e1cbae6a70fb20b4ef05057e4ac *Newtonsoft.Json.dll
551e772fe2ee72b349d5c4ed5d5f8d8957d50cfcbbde7af5d5740d9652bcad626a2c00bc0d9223db7c874962187a90f9160397f243eadee1c594585ba2b155e0 *Newtonsoft.Json.Schema.dll
0a31d192c82bbd8ce50fb75dd5fe813c98bb870d54c112c600ae2e2436063cb2bd94bb206675dfe31ce89922e9a04a3d520ed579ab7198835190b67a6321a74e *Scoop.Validator.dll
78b12beb1e67ac4f6efa0fcba57b4b34ea6a31d8b369934d6b6a6617386ef9939ea453ac262916e5857ce0359eb809424ea33c676a87a8fdfd77a59b2ce96db0 *Newtonsoft.Json.Schema.dll
e9da4370aee4df47eedcf15d9749712eee513e5a9115b808617ddfcfde5bc47a0410edfb57508fcf51033c0be967611b2fd2c2ba944de7290c020cc67f77ac57 *Scoop.Validator.dll
58a0c37e98cac17822c7756bf6686a5fb74e711b8d986d13bd2f689f6b3b1f485fcd908d92cbc6a162a0e5974c2c5a43de57d15f1996be0aa405e41ec2ec8393 *validator.exe

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net45" />
<package id="Newtonsoft.Json.Schema" version="3.0.15" targetFramework="net45" />
<package id="Microsoft.Net.Compilers.Toolset" version="4.9.2" targetFramework="net45" developmentDependency="true" />
<package id="Newtonsoft.Json.Schema" version="4.0.1" targetFramework="net45" />
<package id="Microsoft.Net.Compilers.Toolset" version="4.10.0" targetFramework="net45"
developmentDependency="true" />
</packages>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import
Project="packages\Microsoft.Net.Compilers.Toolset.4.9.2\build\Microsoft.Net.Compilers.Toolset.props"
Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.9.2\build\Microsoft.Net.Compilers.Toolset.props')" />
Project="packages\Microsoft.Net.Compilers.Toolset.4.10.0\build\Microsoft.Net.Compilers.Toolset.props"
Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.10.0\build\Microsoft.Net.Compilers.Toolset.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@@ -23,9 +23,8 @@
<Private>True</Private>
</Reference>
<Reference
Include="Newtonsoft.Json.Schema, Version=3.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<HintPath>
packages\Newtonsoft.Json.Schema.3.0.15\lib\net45\Newtonsoft.Json.Schema.dll</HintPath>
Include="Newtonsoft.Json.Schema, Version=4.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<HintPath>packages\Newtonsoft.Json.Schema.4.0.1\lib\net45\Newtonsoft.Json.Schema.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
@@ -52,7 +51,7 @@
http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error
Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.9.2\build\Microsoft.Net.Compilers.Toolset.props')"
Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.9.2\build\Microsoft.Net.Compilers.Toolset.props'))" />
Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.10.0\build\Microsoft.Net.Compilers.Toolset.props')"
Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.10.0\build\Microsoft.Net.Compilers.Toolset.props'))" />
</Target>
</Project>

View File

@@ -1,8 +1,7 @@
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\help.ps1"
. "$PSScriptRoot\..\libexec\scoop-alias.ps1" | Out-Null
. "$PSScriptRoot\..\lib\commands.ps1"
}
Describe 'Manipulate Alias' -Tag 'Scoop' {
@@ -15,32 +14,32 @@ Describe 'Manipulate Alias' -Tag 'Scoop' {
ensure $shimdir
}
It 'Creates a new alias if alias doesn''t exist' {
$alias_file = "$shimdir\scoop-rm.ps1"
$alias_file | Should -Not -Exist
It 'Creates a new alias if it does not exist' {
$alias_script = "$shimdir\scoop-rm.ps1"
$alias_script | Should -Not -Exist
add_alias 'rm' '"hello, world!"'
& $alias_file | Should -Be 'hello, world!'
& $alias_script | Should -Be 'hello, world!'
}
It 'Does not change existing file if its filename same as alias name' {
$alias_file = "$shimdir\scoop-rm.ps1"
It 'Skips an existing alias' {
$alias_script = "$shimdir\scoop-rm.ps1"
Mock abort {}
New-Item $alias_file -Type File -Force
$alias_file | Should -Exist
New-Item $alias_script -Type File -Force
$alias_script | Should -Exist
add_alias 'rm' '"test"'
Should -Invoke -CommandName abort -Times 1 -ParameterFilter { $msg -eq "File 'scoop-rm.ps1' already exists in shims directory." }
}
It 'Removes an existing alias' {
$alias_file = "$shimdir\scoop-rm.ps1"
$alias_file | Should -Exist
$alias_script = "$shimdir\scoop-rm.ps1"
$alias_script | Should -Exist
Mock get_config { @(@{'rm' = 'scoop-rm' }) }
Mock info {}
rm_alias 'rm'
$alias_file | Should -Not -Exist
$alias_script | Should -Not -Exist
Should -Invoke -CommandName info -Times 1 -ParameterFilter { $msg -eq "Removing alias 'rm'..." }
}
}

View File

@@ -259,6 +259,23 @@ Describe 'get_app_name_from_shim' -Tag 'Scoop', 'Windows' {
}
}
Describe 'cache_path' -Tag 'Scoop' {
It 'returns the correct cache path for a given input' {
$url = 'https://example.com/git.zip'
$ret = cache_path 'git' '2.44.0' $url
$inputStream = [System.IO.MemoryStream]::new([System.Text.Encoding]::UTF8.GetBytes($url))
$sha = (Get-FileHash -Algorithm SHA256 -InputStream $inputStream).Hash.ToLower().Substring(0, 7)
$ret | Should -Be "$cachedir\git#2.44.0#$sha.zip"
}
# # NOTE: Remove this 6 months after the feature ships.
It 'returns the old format cache path for a given input' {
Mock Test-Path { $true }
$ret = cache_path 'git' '2.44.0' 'https://example.com/git.zip'
$ret | Should -Be "$cachedir\git#2.44.0#https_example.com_git.zip"
}
}
Describe 'sanitary_path' -Tag 'Scoop' {
It 'removes invalid path characters from a string' {
$path = 'test?.json'

View File

@@ -25,7 +25,7 @@ Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' {
}
It 'Test cases should exist and hash should match' {
$testcases | Should -Exist
(Get-FileHash -Path $testcases -Algorithm SHA256).Hash.ToLower() | Should -Be '23a23a63e89ff95f5ef27f0cacf08055c2779cf41932266d8f509c2e200b8b63'
(Get-FileHash -Path $testcases -Algorithm SHA256).Hash.ToLower() | Should -Be 'afb86b0552187b8d630ce25d02835fb809af81c584f07e54cb049fb74ca134b6'
}
It 'Test cases should be extracted correctly' {
{ Microsoft.PowerShell.Archive\Expand-Archive -Path $testcases -DestinationPath $working_dir } | Should -Not -Throw
@@ -152,41 +152,6 @@ Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' {
}
}
Context 'zstd extraction' {
BeforeAll {
if ($env:CI) {
Mock Get-AppFilePath { $env:SCOOP_ZSTD_PATH } -ParameterFilter { $Helper -eq 'zstd' }
Mock Get-AppFilePath { '7z.exe' } -ParameterFilter { $Helper -eq '7zip' }
} elseif (!(installed zstd)) {
scoop install zstd
}
$test1 = "$working_dir\ZstdTest.zst"
$test2 = "$working_dir\ZstdTest.tar.zst"
}
It 'extract normal compressed file' {
$to = test_extract 'Expand-ZstdArchive' $test1
$to | Should -Exist
"$to\ZstdTest" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'extract nested compressed file' {
$to = test_extract 'Expand-ZstdArchive' $test2
$to | Should -Exist
"$to\ZstdTest" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'works with "-Removal" switch ($removal param)' {
$test1 | Should -Exist
test_extract 'Expand-ZstdArchive' $test1 $true
$test1 | Should -Not -Exist
}
}
Context 'msi extraction' {
BeforeAll {

View File

@@ -14,11 +14,6 @@ Describe 'Package Dependencies' -Tag 'Scoop' {
Test-7zipRequirement -Uri 'test.bin' | Should -BeFalse
Test-7zipRequirement -Uri @('test.xz', 'test.bin') | Should -BeTrue
}
It 'Test Zstd requirement' {
Test-ZstdRequirement -Uri 'test.zst' | Should -BeTrue
Test-ZstdRequirement -Uri 'test.bin' | Should -BeFalse
Test-ZstdRequirement -Uri @('test.zst', 'test.bin') | Should -BeTrue
}
It 'Test lessmsi requirement' {
Mock get_config { $true }
Test-LessmsiRequirement -Uri 'test.msi' | Should -BeTrue
@@ -27,7 +22,6 @@ Describe 'Package Dependencies' -Tag 'Scoop' {
}
It 'Allow $Uri be $null' {
Test-7zipRequirement -Uri $null | Should -BeFalse
Test-ZstdRequirement -Uri $null | Should -BeFalse
Test-LessmsiRequirement -Uri $null | Should -BeFalse
}
}

View File

@@ -70,19 +70,6 @@ if ($env:CI -eq $true) {
Invoke-WebRequest -Uri $source -OutFile $destination
& 7z.exe x "$env:SCOOP_HELPERS_PATH\innounp.rar" -o"$env:SCOOP_HELPERS_PATH\innounp" -y | Out-Null
}
# Only download zstd for AppVeyor, GitHub Actions has zstd installed by default
if ($env:BHBuildSystem -eq 'AppVeyor') {
$env:SCOOP_ZSTD_PATH = "$env:SCOOP_HELPERS_PATH\zstd\zstd.exe"
if (!(Test-Path $env:SCOOP_ZSTD_PATH)) {
$source = 'https://github.com/facebook/zstd/releases/download/v1.5.1/zstd-v1.5.1-win32.zip'
$destination = "$env:SCOOP_HELPERS_PATH\zstd.zip"
Invoke-WebRequest -Uri $source -OutFile $destination
& 7z.exe x "$env:SCOOP_HELPERS_PATH\zstd.zip" -o"$env:SCOOP_HELPERS_PATH\zstd" -y | Out-Null
}
} else {
$env:SCOOP_ZSTD_PATH = (Get-Command zstd.exe).Path
}
}
}

Binary file not shown.