28 Commits

Author SHA1 Message Date
Chawye Hsu
d285bb08d4 chore(release): Bump to version 0.4.1 (#5924)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-04-25 21:33:45 +08:00
Hsiao-nan Cheung
105e4161fc chore(release): Bump to version 0.4.0 (#5424)
Co-authored-by: Richard Kuhnt <r15ch13+git@gmail.com>
Co-authored-by: HUMORCE <humorce@outlook.com>
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
Co-authored-by: Ross Smith II <ross@smithii.com>
Co-authored-by: Ercolino <fabrizio.ciccarese@gmail.com>
Co-authored-by: Jeppe Frandsen <jeppe@algrens.dk>
Co-authored-by: Chawye Hsu <chawyehsu@hotmail.com>
Co-authored-by: 0x574859 <Hex574859@outlook.com>
Co-authored-by: Valinor <Valinor@users.noreply.github.com>
Co-authored-by: Stephen Albert-Moore <stephen@sherbet.space>
Co-authored-by: L. Yeung <lewis_yeung-ly@outlook.com>
Co-authored-by: Bill ZHANG <36790218+Lutra-Fs@users.noreply.github.com>
Co-authored-by: walpo <68171111+walpox@users.noreply.github.com>
Co-authored-by: Suhas <98212676+spider2048@users.noreply.github.com>
Co-authored-by: Gerardo Grignoli <gerardog@gmail.com>
Co-authored-by: Dustin <131928587+dooptydoo90x@users.noreply.github.com>
Co-authored-by: Hagai Gold <hagaigold@gmail.com>
Co-authored-by: David Watson <davidlwatsonjr@users.noreply.github.com>
Co-authored-by: András Svraka <svraka.andras@gmail.com>
Co-authored-by: Xuesong <amorphobia@users.noreply.github.com>
2024-04-19 00:07:58 +08:00
Hsiao-nan Cheung
78f6fecd60 docs(changelog): Rearrange and update CHANGELOG (#5904) 2024-04-18 20:05:13 +08:00
Hsiao-nan Cheung
5819b5a1ef feat(path): Isolate Scoop apps' PATH (#5840) 2024-04-18 19:14:53 +08:00
Hsiao-nan Cheung
fa06e921c2 refactor(shim): Remove CS shim codebase (#5903) 2024-04-18 19:09:08 +08:00
Hsiao-nan Cheung
98cf8ae4da fix(autoupdate): Fix bug that 'WebClient' doesn't auto-extract 'gzip' (#5901) 2024-04-17 22:29:20 +08:00
Hsiao-nan Cheung
7054c9d338 fix(decompress): Use wix.exe in WiX Toolset v4+ as primary extractor of Expand-DarkArchive() (#5871) 2024-04-12 16:09:11 +08:00
Hsiao-nan Cheung
6327146b97 fix(shim): Run JAR file from app's root directory (#5872) 2024-04-11 15:56:43 +08:00
Hsiao-nan Cheung
92b71c6057 refactor(core): Get rid of 'fullpath' (#3533) 2024-04-10 14:55:20 +08:00
HUMORCE
81e7dec78c fix(scoop-alias): Prevent overwrite existing file when adding alias (#5577)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2024-04-08 23:26:38 +08:00
Xuesong
b008fe5b11 fix(scoop-virustotal): escape character not available in PowerShell 5.1 (#5870) 2024-04-08 23:24:18 +08:00
HUMORCE
5a06eacd9c refactor(helper): Remove 7zip's fallback '7zip-zstd' (#5548) 2024-04-08 23:23:57 +08:00
András Svraka
0b135052ce fix(scoop-shim): Import system.ps1 (#5864) 2024-04-04 21:39:51 +08:00
David Watson
9ef03c24fb perf(scoop-update): Check for running process before wasting time on download (#5799) 2024-04-01 18:31:01 +08:00
Hsiao-nan Cheung
dfbeace066 fix(system): Remove EnvVar only if existed (#5858) 2024-04-01 18:02:32 +08:00
Hsiao-nan Cheung
6772e61b3d refactor(core): Rewrite and separate path-related functions to system.ps1 (#5836) 2024-03-27 17:32:39 +08:00
Hsiao-nan Cheung
77b66cc890 fix(core): Fix arguments parsing method of Invoke-ExternalCommand() (#5839) 2024-03-25 19:19:44 +08:00
Hsiao-nan Cheung
9770c86598 fix(shim): Update kiennq-shim to v3.1.1 (#5847) 2024-03-25 18:07:31 +08:00
Hsiao-nan Cheung
5153d7375b perf(shim): Update kiennq-shim to v3.1 (#5841) 2024-03-22 19:06:27 +08:00
Hsiao-nan Cheung
5354ab5d16 builds(supporting): Update Json to 13.0.3, Json.Schema to 3.0.15 (#5835) 2024-03-21 13:30:51 +08:00
Suhas
90766f9315 fix(shim): Allow GUI applications to attach to the shell's console when launched using the GUI shim (#5721) 2024-03-20 22:16:10 +08:00
Hsiao-nan Cheung
3186fef105 fix(update/uninstall): Remove items from PATH correctly (#5833) 2024-03-20 17:17:48 +08:00
Hsiao-nan Cheung
9d07c33e87 fix(decompress): Remove unused parent dir w/ 'extract_dir' (#5682) 2024-03-13 18:41:58 +08:00
Hsiao-nan Cheung
6f9ed1d464 fix(ci): Update 'psmodulecache' version to 'main' (#5828) 2024-03-13 13:22:58 +08:00
Hsiao-nan Cheung
54e0514833 fix(install): Fix bugs in #5715 (#5824) 2024-03-07 10:55:27 +08:00
Hsiao-nan Cheung
7e3dc73b83 refactor(core): Cleanup some old codes, e.g., msi section and config migration (#5715) 2024-03-06 21:04:46 +08:00
HUMORCE
48f793532c fix(manifest): Correct source of manifest (#5575) 2024-02-23 17:41:44 +08:00
Rashil Gandhi
5328bef269 fix(config): Warn users about misconfigured token (#5777)
* Warn users about misconfigured token

* Update CHANGELOG.md
2024-01-05 23:46:28 +05:30
62 changed files with 829 additions and 980 deletions

View File

@@ -14,7 +14,7 @@ jobs:
with:
fetch-depth: 2
- name: Init Test Suite
uses: potatoqualitee/psmodulecache@v5.1
uses: potatoqualitee/psmodulecache@main
with:
modules-to-cache: BuildHelpers
shell: powershell
@@ -30,7 +30,7 @@ jobs:
with:
fetch-depth: 2
- name: Init Test Suite
uses: potatoqualitee/psmodulecache@v5.1
uses: potatoqualitee/psmodulecache@main
with:
modules-to-cache: BuildHelpers
shell: pwsh

View File

@@ -1,63 +1,90 @@
## [Unreleased](https://github.com/ScoopInstaller/Scoop/compare/master...develop)
## [v0.4.1](https://github.com/ScoopInstaller/Scoop/compare/v0.4.0...v0.4.1) - 2024-04-25
### Bug Fixes
- **core:** Fix `Invoke-ExternalCommand` regression ([#5923](https://github.com/ScoopInstaller/Scoop/issues/5923))
## [v0.4.0](https://github.com/ScoopInstaller/Scoop/compare/v0.3.1...v0.4.0) - 2024-04-18
### Features
- **scoop-update:** Add support for parallel syncing buckets in PowerShell 7 and improve output ([#5122](https://github.com/ScoopInstaller/Scoop/issues/5122))
- **bucket:** Switch nirsoft bucket to ScoopInstaller/Nirsoft ([#5328](https://github.com/ScoopInstaller/Scoop/issues/5328))
- **config:** Support portable config file ([#5369](https://github.com/ScoopInstaller/Scoop/issues/5369))
- **bucket:** Make official buckets higher priority ([#5398](https://github.com/ScoopInstaller/Scoop/issues/5398))
- **config:** Support portable config file ([#5369](https://github.com/ScoopInstaller/Scoop/issues/5369))
- **core:** Add `-Quiet` switch for `Invoke-ExternalCommand` ([#5346](https://github.com/ScoopInstaller/Scoop/issues/5346))
- **core:** Allow global install of PowerShell modules ([#5611](https://github.com/ScoopInstaller/Scoop/issues/5611))
- **path:** Isolate Scoop apps' PATH ([#5840](https://github.com/ScoopInstaller/Scoop/issues/5840))
### Bug Fixes
- **shim:** Remove console window for GUI applications ([#5559](https://github.com/ScoopInstaller/Scoop/issues/5559))
- **decompress:** Exclude '*.nsis' that may cause error ([#5294](https://github.com/ScoopInstaller/Scoop/issues/5294))
- **scoop-alias:** Prevent overwrite existing file when adding alias ([#5577](https://github.com/ScoopInstaller/Scoop/issues/5577))
- **scoop-checkup:** Skip defender check in Windows Sandbox ([#5519](https://github.com/ScoopInstaller/Scoop/issues/5519))
- **scoop-checkup:** Change the message level of helpers from ERROR to WARN ([#5614](https://github.com/ScoopInstaller/Scoop/issues/5614))
- **scoop-checkup:** Don't throw 7zip error when external 7zip is used ([#5703](https://github.com/ScoopInstaller/Scoop/issues/5703))
- **scoop-(un)hold:** Correct output the messages when manifest not found, (already|not) held ([#5519](https://github.com/ScoopInstaller/Scoop/issues/5519))
- **scoop-info:** Fix errors in file size collection when `--verbose` ([#5352](https://github.com/ScoopInstaller/Scoop/issues/5352))
- **scoop-reset:** Don't abort when multiple apps are passed and an app is running ([#5687](https://github.com/ScoopInstaller/Scoop/issues/5687))
- **scoop-update:** Change error message to a better instruction ([#5677](https://github.com/ScoopInstaller/Scoop/issues/5677))
- **scoop-virustotal:** Fix `scoop-virustotal` when `--all` has been passed without app ([#5593](https://github.com/ScoopInstaller/Scoop/issues/5593))
- **scoop-virustotal:** Fix the issue that escape character not available in PowerShell 5.1 ([#5870](https://github.com/ScoopInstaller/Scoop/issues/5870))
- **autoupdate:** Fix file hash extraction ([#5295](https://github.com/ScoopInstaller/Scoop/issues/5295))
- **getopt:** Stop split arguments in `getopt()` and ensure array by explicit arguments type ([#5326](https://github.com/ScoopInstaller/Scoop/issues/5326))
- **shortcuts:** Output correctly formatted path ([#5333](https://github.com/ScoopInstaller/Scoop/issues/5333))
- **autoupdate:** Fix bug that 'WebClient' doesn't auto-extract 'gzip' ([#5901](https://github.com/ScoopInstaller/Scoop/issues/5901))
- **buckets:** Avoid error messages for unexpected dir ([#5549](https://github.com/ScoopInstaller/Scoop/issues/5549))
- **config:** Warn users about misconfigured GitHub token ([#5777](https://github.com/ScoopInstaller/Scoop/issues/5777))
- **core:** Fix scripts' calling parameters ([#5365](https://github.com/ScoopInstaller/Scoop/issues/5365))
- **core:** Fix `is_in_dir` under Unix ([#5391](https://github.com/ScoopInstaller/Scoop/issues/5391))
- **core:** Rewrite config file when needed ([#5439](https://github.com/ScoopInstaller/Scoop/issues/5439))
- **core:** Prevents leaking HTTP(S)_PROXY env vars to current sessions after Invoke-Git in parallel execution ([#5436](https://github.com/ScoopInstaller/Scoop/pull/5436))
- **core:** Prevents leaking HTTP(S)_PROXY env vars to current sessions after Invoke-Git in parallel execution ([#5436](https://github.com/ScoopInstaller/Scoop/issues/5436))
- **core:** Handle scoop aliases and broken(edited,copied) shim ([#5551](https://github.com/ScoopInstaller/Scoop/issues/5551))
- **env:** Avoid automatic expansion of `%%` in env ([#5395](https://github.com/ScoopInstaller/Scoop/issues/5395), [#5452](https://github.com/ScoopInstaller/Scoop/pull/5452), [#5631](https://github.com/ScoopInstaller/Scoop/pull/5631))
- **install:** Fix download from private GitHub repositories ([#5361](https://github.com/ScoopInstaller/Scoop/issues/5361))
- **install:** Avoid error when unlinking non-existent junction/hardlink ([#5552](https://github.com/ScoopInstaller/Scoop/issues/5552))
- **scoop-info:** Fix errors in file size collection when `--verbose` ([#5352](https://github.com/ScoopInstaller/Scoop/pull/5352))
- **shim:** Use bash executable directly ([#5433](https://github.com/ScoopInstaller/Scoop/issues/5433))
- **core:** Avoid error messages when deleting non-existent environment variable ([#5547](https://github.com/ScoopInstaller/Scoop/issues/5547))
- **core:** Use relative path as fallback of `$scoopdir` ([#5544](https://github.com/ScoopInstaller/Scoop/issues/5544))
- **core:** Fix detection of Git ([#5545](https://github.com/ScoopInstaller/Scoop/issues/5545))
- **scoop-checkup:** Skip defender check in Windows Sandbox ([#5519](https://github.com/ScoopInstaller/Scoop/issues/5519))
- **buckets:** Avoid error messages for unexpected dir ([#5549](https://github.com/ScoopInstaller/Scoop/issues/5549))
- **scoop-virustotal:** Fix `scoop-virustotal` when `--all` has been passed without app ([#5593](https://github.com/ScoopInstaller/Scoop/pull/5593))
- **scoop-checkup:** Change the message level of helpers from ERROR to WARN ([#5549](https://github.com/ScoopInstaller/Scoop/issues/5614))
- **scoop-(un)hold:** Correct output the messages when manifest not found, (already|not) held ([#5519](https://github.com/ScoopInstaller/Scoop/issues/5519))
- **scoop-update:** Change error message to a better instruction ([#5677](https://github.com/ScoopInstaller/Scoop/issues/5677))
- **core:** Do not call `scoop` externally from inside the code ([#5695](https://github.com/ScoopInstaller/Scoop/issues/5695))
- **core:** Fix arguments parsing method of `Invoke-ExternalCommand()` ([#5839](https://github.com/ScoopInstaller/Scoop/issues/5839))
- **decompress:** Exclude '*.nsis' that may cause error ([#5294](https://github.com/ScoopInstaller/Scoop/issues/5294))
- **decompress:** Remove unused parent dir w/ 'extract_dir' ([#5682](https://github.com/ScoopInstaller/Scoop/issues/5682))
- **decompress:** Use `wix.exe` in WiX Toolset v4+ as primary extractor of `Expand-DarkArchive()` ([#5871](https://github.com/ScoopInstaller/Scoop/issues/5871))
- **env:** Avoid automatic expansion of `%%` in env ([#5395](https://github.com/ScoopInstaller/Scoop/issues/5395), [#5452](https://github.com/ScoopInstaller/Scoop/issues/5452), [#5631](https://github.com/ScoopInstaller/Scoop/issues/5631))
- **getopt:** Stop split arguments in `getopt()` and ensure array by explicit arguments type ([#5326](https://github.com/ScoopInstaller/Scoop/issues/5326))
- **install:** Fix download from private GitHub repositories ([#5361](https://github.com/ScoopInstaller/Scoop/issues/5361))
- **install:** Avoid error when unlinking non-existent junction/hardlink ([#5552](https://github.com/ScoopInstaller/Scoop/issues/5552))
- **manifest:** Correct source of manifest ([#5575](https://github.com/ScoopInstaller/Scoop/issues/5575))
- **shim:** Remove console window for GUI applications ([#5559](https://github.com/ScoopInstaller/Scoop/issues/5559))
- **shim:** Use bash executable directly ([#5433](https://github.com/ScoopInstaller/Scoop/issues/5433))
- **shim:** Check literal path in `Get-ShimPath` ([#5680](https://github.com/ScoopInstaller/Scoop/issues/5680))
- **shim:** Avoid unexpected output of `list` subcommand ([#5681](https://github.com/ScoopInstaller/Scoop/issues/5681))
- **scoop-reset:** Don't abort when multiple apps are passed and an app is running ([#5687](https://github.com/ScoopInstaller/Scoop/issues/5687))
- **core:** Do not call `scoop` externally from inside the code ([#5695](https://github.com/ScoopInstaller/Scoop/issues/5695))
- **scoop-checkup:** Don't throw 7zip error when external 7zip is used ([#5703](https://github.com/ScoopInstaller/Scoop/issues/5703))
- **shim:** Allow GUI applications to attach to the shell's console when launched using the GUI shim ([#5721](https://github.com/ScoopInstaller/Scoop/issues/5721))
- **shim:** Run JAR file from app's root directory ([#5872](https://github.com/ScoopInstaller/Scoop/issues/5872))
- **shortcuts:** Output correctly formatted path ([#5333](https://github.com/ScoopInstaller/Scoop/issues/5333))
- **update/uninstall:** Remove items from PATH correctly ([#5833](https://github.com/ScoopInstaller/Scoop/issues/5833))
### Performance Improvements
- **scoop-search:** Improve performance for local search ([#5644](https://github.com/ScoopInstaller/Scoop/issues/5644))
- **scoop-update:** Check for running process before wasting time on download ([#5799](https://github.com/ScoopInstaller/Scoop/issues/5799))
- **decompress:** Disable progress bar to improve `Expand-Archive` performance ([#5410](https://github.com/ScoopInstaller/Scoop/issues/5410))
- **scoop-search:** Improve performance for local search ([#5324](https://github.com/ScoopInstaller/Scoop/issues/5324))
- **shim:** Update kiennq-shim to v3.1.1 ([#5841](https://github.com/ScoopInstaller/Scoop/issues/5841), [#5847](https://github.com/ScoopInstaller/Scoop/issues/5847))
### Code Refactoring
- **git:** Use Invoke-Git() with direct path to git.exe to prevent spawning shim subprocesses ([#5122](https://github.com/ScoopInstaller/Scoop/issues/5122), [#5375](https://github.com/ScoopInstaller/Scoop/issues/5375))
- **scoop-download:** Output more detailed manifest information ([#5277](https://github.com/ScoopInstaller/Scoop/issues/5277))
- **core:** Cleanup some old codes, e.g., msi section and config migration ([#5715](https://github.com/ScoopInstaller/Scoop/issues/5715), [#5824](https://github.com/ScoopInstaller/Scoop/issues/5824))
- **core:** Rewrite and separate path-related functions to `system.ps1` ([#5836](https://github.com/ScoopInstaller/Scoop/issues/5836), [#5858](https://github.com/ScoopInstaller/Scoop/issues/5858), [#5864](https://github.com/ScoopInstaller/Scoop/issues/5864))
- **core:** Get rid of 'fullpath' ([#3533](https://github.com/ScoopInstaller/Scoop/issues/3533))
- **git:** Use Invoke-Git() with direct path to git.exe to prevent spawning shim subprocesses ([#5122](https://github.com/ScoopInstaller/Scoop/issues/5122), [#5375](https://github.com/ScoopInstaller/Scoop/issues/5375))
- **helper:** Remove 7zip's fallback '7zip-zstd' ([#5548](https://github.com/ScoopInstaller/Scoop/issues/5548))
- **shim:** Remove CS shim codebase ([#5903](https://github.com/ScoopInstaller/Scoop/issues/5903))
### Builds
- **checkver:** Read the private_host config variable ([#5381](https://github.com/ScoopInstaller/Scoop/issues/5381))
- **supporting:** Update Json to 13.0.3, Json.Schema to 3.0.15 ([#5835](https://github.com/ScoopInstaller/Scoop/issues/5835))
### Continuous Integration
- **dependabot:** Add dependabot.yml for GitHub Actions ([#5377](https://github.com/ScoopInstaller/Scoop/pull/5377))
- **dependabot:** Add dependabot.yml for GitHub Actions ([#5377](https://github.com/ScoopInstaller/Scoop/issues/5377))
- **module:** Update 'psmodulecache' version to 'main' ([#5828](https://github.com/ScoopInstaller/Scoop/issues/5828))
### Tests

View File

@@ -121,7 +121,7 @@ foreach ($current in $MANIFESTS) {
Invoke-CachedDownload $current.app $version $_ $null $null -use_cache:$UseCache
$to_check = fullpath (cache_path $current.app $version $_)
$to_check = cache_path $current.app $version $_
$actual_hash = (Get-FileHash -Path $to_check -Algorithm $algorithm).Hash.ToLower()
# Append type of algorithm to both expected and actual if it's not sha256
@@ -146,7 +146,7 @@ foreach ($current in $MANIFESTS) {
Write-Host "$($current.app): " -NoNewline
Write-Host 'Mismatch found ' -ForegroundColor Red
$mismatched | ForEach-Object {
$file = fullpath (cache_path $current.app $version $current.urls[$_])
$file = cache_path $current.app $version $current.urls[$_]
Write-Host "`tURL:`t`t$($current.urls[$_])"
if (Test-Path $file) {
Write-Host "`tFirst bytes:`t$((get_magic_bytes_pretty $file ' ').ToUpper())"

View File

@@ -260,6 +260,7 @@ while ($in_progress -gt 0) {
$in_progress--
$state = $ev.SourceEventArgs.UserState
$result = $ev.SourceEventArgs.Result
$app = $state.app
$file = $state.file
$json = $state.json
@@ -285,7 +286,13 @@ while ($in_progress -gt 0) {
}
if ($url) {
$page = (Get-Encoding($wc)).GetString($ev.SourceEventArgs.Result)
$ms = New-Object System.IO.MemoryStream
$ms.Write($result, 0, $result.Length)
$ms.Seek(0, 0) | Out-Null
if ($result[0] -eq 0x1F -and $result[1] -eq 0x8B) {
$ms = New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Decompress)
}
$page = (New-Object System.IO.StreamReader($ms, (Get-Encoding $wc))).ReadToEnd()
}
if ($script) {
$page = Invoke-Command ([scriptblock]::Create($script -join "`r`n"))

View File

@@ -12,6 +12,7 @@ param(
)
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\system.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\shortcuts.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
@@ -98,7 +99,9 @@ if ($purge) {
if ($global) { keep_onlypersist $globaldir }
}
remove_from_path (shimdir $false)
if ($global) { remove_from_path (shimdir $true) }
Remove-Path -Path (shimdir $global) -Global:$global
if (get_config USE_ISOLATED_PATH) {
Remove-Path -Path ('%' + $scoopPathEnvVar + '%') -Global:$global
}
success 'Scoop has been uninstalled.'

View File

@@ -37,7 +37,13 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
$wc.Headers.Add('Referer', (strip_filename $url))
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$data = $wc.DownloadData($url)
$hashfile = (Get-Encoding($wc)).GetString($data)
$ms = New-Object System.IO.MemoryStream
$ms.Write($data, 0, $data.Length)
$ms.Seek(0, 0) | Out-Null
if ($data[0] -eq 0x1F -and $data[1] -eq 0x8B) {
$ms = New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Decompress)
}
$hashfile = (New-Object System.IO.StreamReader($ms, (Get-Encoding $wc))).ReadToEnd()
} catch [system.net.webexception] {
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
@@ -93,7 +99,13 @@ function find_hash_in_json([String] $url, [Hashtable] $substitutions, [String] $
$wc.Headers.Add('Referer', (strip_filename $url))
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$data = $wc.DownloadData($url)
$json = (Get-Encoding($wc)).GetString($data)
$ms = New-Object System.IO.MemoryStream
$ms.Write($data, 0, $data.Length)
$ms.Seek(0, 0) | Out-Null
if ($data[0] -eq 0x1F -and $data[1] -eq 0x8B) {
$ms = New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Decompress)
}
$json = (New-Object System.IO.StreamReader($ms, (Get-Encoding $wc))).ReadToEnd()
} catch [System.Net.WebException] {
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
@@ -115,7 +127,13 @@ function find_hash_in_xml([String] $url, [Hashtable] $substitutions, [String] $x
$wc.Headers.Add('Referer', (strip_filename $url))
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$data = $wc.DownloadData($url)
$xml = [xml]((Get-Encoding($wc)).GetString($data))
$ms = New-Object System.IO.MemoryStream
$ms.Write($data, 0, $data.Length)
$ms.Seek(0, 0) | Out-Null
if ($data[0] -eq 0x1F -and $data[1] -eq 0x8B) {
$ms = New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Decompress)
}
$xml = [xml]((New-Object System.IO.StreamReader($ms, (Get-Encoding $wc))).ReadToEnd())
} catch [system.net.webexception] {
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
@@ -278,7 +296,7 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
return $null
}
$file = fullpath (cache_path $app $version $url)
$file = cache_path $app $version $url
$hash = (Get-FileHash -Path $file -Algorithm SHA256).Hash.ToLower()
Write-Host 'Computed hash: ' -ForegroundColor DarkYellow -NoNewline
Write-Host $hash -ForegroundColor Green

View File

@@ -132,6 +132,9 @@ function set_config {
$value = [System.Convert]::ToBoolean($value)
}
# Initialize config's change
Complete-ConfigChange -Name $name -Value $value
if ($null -eq $scoopConfig.$name) {
$scoopConfig | Add-Member -MemberType NoteProperty -Name $name -Value $value
} else {
@@ -147,6 +150,74 @@ function set_config {
return $scoopConfig
}
function Complete-ConfigChange {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0)]
[string]
$Name,
[Parameter(Mandatory, Position = 1)]
[AllowEmptyString()]
[string]
$Value
)
if ($Name -eq 'use_isolated_path') {
$oldValue = get_config USE_ISOLATED_PATH
if ($Value -eq $oldValue) {
return
} else {
$currPathEnvVar = $scoopPathEnvVar
}
. "$PSScriptRoot\..\lib\system.ps1"
if ($Value -eq $false -or $Value -eq '') {
info 'Turn off Scoop isolated path... This may take a while, please wait.'
$movedPath = Get-EnvVar -Name $currPathEnvVar
if ($movedPath) {
Add-Path -Path $movedPath -Quiet
Remove-Path -Path ('%' + $currPathEnvVar + '%') -Quiet
Set-EnvVar -Name $currPathEnvVar -Quiet
}
if (is_admin) {
$movedPath = Get-EnvVar -Name $currPathEnvVar -Global
if ($movedPath) {
Add-Path -Path $movedPath -Global -Quiet
Remove-Path -Path ('%' + $currPathEnvVar + '%') -Global -Quiet
Set-EnvVar -Name $currPathEnvVar -Global -Quiet
}
}
} else {
$newPathEnvVar = if ($Value -eq $true) {
'SCOOP_PATH'
} else {
$Value.ToUpperInvariant()
}
info "Turn on Scoop isolated path ('$newPathEnvVar')... This may take a while, please wait."
$movedPath = Remove-Path -Path "$scoopdir\apps\*" -TargetEnvVar $currPathEnvVar -Quiet -PassThru
if ($movedPath) {
Add-Path -Path $movedPath -TargetEnvVar $newPathEnvVar -Quiet
Add-Path -Path ('%' + $newPathEnvVar + '%') -Quiet
if ($currPathEnvVar -ne 'PATH') {
Remove-Path -Path ('%' + $currPathEnvVar + '%') -Quiet
Set-EnvVar -Name $currPathEnvVar -Quiet
}
}
if (is_admin) {
$movedPath = Remove-Path -Path "$globaldir\apps\*" -TargetEnvVar $currPathEnvVar -Global -Quiet -PassThru
if ($movedPath) {
Add-Path -Path $movedPath -TargetEnvVar $newPathEnvVar -Global -Quiet
Add-Path -Path ('%' + $newPathEnvVar + '%') -Global -Quiet
if ($currPathEnvVar -ne 'PATH') {
Remove-Path -Path ('%' + $currPathEnvVar + '%') -Global -Quiet
Set-EnvVar -Name $currPathEnvVar -Global -Quiet
}
}
}
}
}
}
function setup_proxy() {
# note: '@' and ':' in password must be escaped, e.g. 'p@ssword' -> p\@ssword'
$proxy = get_config PROXY
@@ -303,7 +374,7 @@ function filesize($length) {
} else {
if ($null -eq $length) {
$length = 0
}
}
"$($length) B"
}
}
@@ -423,18 +494,13 @@ function Get-HelperPath {
$HelperPath = (Get-Command git -ErrorAction Ignore).Source
}
}
'7zip' {
$HelperPath = Get-AppFilePath '7zip' '7z.exe'
if ([String]::IsNullOrEmpty($HelperPath)) {
$HelperPath = Get-AppFilePath '7zip-zstd' '7z.exe'
}
}
'7zip' { $HelperPath = Get-AppFilePath '7zip' '7z.exe' }
'Lessmsi' { $HelperPath = Get-AppFilePath 'lessmsi' 'lessmsi.exe' }
'Innounp' { $HelperPath = Get-AppFilePath 'innounp' 'innounp.exe' }
'Dark' {
$HelperPath = Get-AppFilePath 'dark' 'dark.exe'
$HelperPath = Get-AppFilePath 'wixtoolset' 'wix.exe'
if ([String]::IsNullOrEmpty($HelperPath)) {
$HelperPath = Get-AppFilePath 'wixtoolset' 'dark.exe'
$HelperPath = Get-AppFilePath 'dark' 'dark.exe'
}
}
'Aria2' { $HelperPath = Get-AppFilePath 'aria2' 'aria2c.exe' }
@@ -455,8 +521,8 @@ function Get-CommandPath {
)
begin {
$userShims = Convert-Path (shimdir $false)
$globalShims = fullpath (shimdir $true) # don't resolve: may not exist
$userShims = shimdir $false
$globalShims = shimdir $true
}
process {
@@ -576,17 +642,47 @@ function ensure($dir) {
}
Convert-Path -Path $dir
}
function Get-AbsolutePath {
<#
.SYNOPSIS
Get absolute path
.DESCRIPTION
Get absolute path, even if not existed
.PARAMETER Path
Path to manipulate
.OUTPUTS
System.String
Absolute path, may or maynot existed
#>
[CmdletBinding()]
[OutputType([string])]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string]
$Path
)
process {
return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
}
}
function fullpath($path) {
# should be ~ rooted
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
Show-DeprecatedWarning $MyInvocation 'Get-AbsolutePath'
return Get-AbsolutePath -Path $path
}
function friendly_path($path) {
$h = (Get-PsProvider 'FileSystem').home; if(!$h.endswith('\')) { $h += '\' }
if($h -eq '\') { return $path }
return "$path" -replace ([regex]::escape($h)), "~\"
$h = (Get-PSProvider 'FileSystem').Home
if (!$h.EndsWith('\')) {
$h += '\'
}
if ($h -eq '\') {
return $path
} else {
return $path -replace ([Regex]::Escape($h)), '~\'
}
}
function is_local($path) {
($path -notmatch '^https?://') -and (test-path $path)
($path -notmatch '^https?://') -and (Test-Path $path)
}
# operations
@@ -600,8 +696,7 @@ function Invoke-ExternalCommand {
[CmdletBinding(DefaultParameterSetName = "Default")]
[OutputType([Boolean])]
param (
[Parameter(Mandatory = $true,
Position = 0)]
[Parameter(Mandatory = $true, Position = 0)]
[Alias("Path")]
[ValidateNotNullOrEmpty()]
[String]
@@ -651,9 +746,15 @@ function Invoke-ExternalCommand {
$Process.StartInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
}
if ($ArgumentList.Length -gt 0) {
if ($FilePath -match '^((cmd|cscript|wscript|msiexec)(\.exe)?|.*\.(bat|cmd|js|vbs|wsf))$') {
$Process.StartInfo.Arguments = $ArgumentList -join ' '
} elseif ($Process.StartInfo.ArgumentList.Add) {
$ArgumentList = $ArgumentList | ForEach-Object { [regex]::Split($_.Replace('"', ''), '(?<=(?<![:\w])[/-]\w+) | (?=[/-])') }
# Use legacy argument escaping for commands having non-standard behavior
# with regard to argument passing. `msiexec` requires some args like
# `TARGETDIR="C:\Program Files"`, which is non-standard, therefore we
# treat it as a legacy command.
# ref-1: https://learn.microsoft.com/en-us/powershell/scripting/learn/experimental-features?view=powershell-7.4#psnativecommandargumentpassing
$LegacyCommand = $FilePath -match '^((cmd|cscript|find|sqlcmd|wscript|msiexec)(\.exe)?|.*\.(bat|cmd|js|vbs|wsf))$'
$SupportArgumentList = $Process.StartInfo.PSObject.Properties.Name -contains 'ArgumentList'
if ((-not $LegacyCommand) -and $SupportArgumentList) {
# ArgumentList is supported in PowerShell 6.1 and later (built on .NET Core 2.1+)
# ref-1: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.argumentlist?view=net-6.0
# ref-2: https://docs.microsoft.com/en-us/powershell/scripting/whats-new/differences-from-windows-powershell?view=powershell-7.2#net-framework-vs-net-core
@@ -661,14 +762,15 @@ function Invoke-ExternalCommand {
} else {
# escape arguments manually in lower versions, refer to https://docs.microsoft.com/en-us/previous-versions/17w5ykft(v=vs.85)
$escapedArgs = $ArgumentList | ForEach-Object {
# escape N consecutive backslash(es), which are followed by a double quote, to 2N consecutive ones
$s = $_ -replace '(\\+)"', '$1$1"'
# escape N consecutive backslash(es), which are at the end of the string, to 2N consecutive ones
$s = $s -replace '(\\+)$', '$1$1'
# escape double quotes
$s = $s -replace '"', '\"'
# quote the argument
"`"$s`""
# escape N consecutive backslash(es), which are followed by a double quote or at the end of the string, to 2N consecutive ones
$s = $_ -replace '(\\+)(""|$)', '$1$1$2'
# quote the path if it contains spaces and is not NSIS's '/D' argument
# ref: https://nsis.sourceforge.io/Docs/Chapter3.html
if ($s -match ' ' -and $s -notmatch '/D=[A-Z]:[\\/].*') {
$s -replace '([A-Z]:[\\/].*)', '"$1"'
} else {
$s
}
}
$Process.StartInfo.Arguments = $escapedArgs -join ' '
}
@@ -714,57 +816,6 @@ function Invoke-ExternalCommand {
return $true
}
function Publish-Env {
if (-not ("Win32.NativeMethods" -as [Type])) {
Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @"
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
"@
}
$HWND_BROADCAST = [IntPtr] 0xffff;
$WM_SETTINGCHANGE = 0x1a;
$result = [UIntPtr]::Zero
[Win32.Nativemethods]::SendMessageTimeout($HWND_BROADCAST,
$WM_SETTINGCHANGE,
[UIntPtr]::Zero,
"Environment",
2,
5000,
[ref] $result
) | Out-Null
}
function env($name, $global, $val = '__get') {
$RegisterKey = if ($global) {
Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
} else {
Get-Item -Path 'HKCU:'
}
$EnvRegisterKey = $RegisterKey.OpenSubKey('Environment', $val -ne '__get')
if ($val -eq '__get') {
$RegistryValueOption = [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames
$EnvRegisterKey.GetValue($name, $null, $RegistryValueOption)
} elseif ($val -eq $null) {
try { $EnvRegisterKey.DeleteValue($name) } catch { }
Publish-Env
} else {
$RegistryValueKind = if ($val.Contains('%')) {
[Microsoft.Win32.RegistryValueKind]::ExpandString
} elseif ($EnvRegisterKey.GetValue($name)) {
$EnvRegisterKey.GetValueKind($name)
} else {
[Microsoft.Win32.RegistryValueKind]::String
}
$EnvRegisterKey.SetValue($name, $val, $RegistryValueKind)
Publish-Env
}
}
function isFileLocked([string]$path) {
$file = New-Object System.IO.FileInfo $path
@@ -872,7 +923,7 @@ function warn_on_overwrite($shim, $path) {
function shim($path, $global, $name, $arg) {
if (!(Test-Path $path)) { abort "Can't shim '$(fname $path)': couldn't find '$path'." }
$abs_shimdir = ensure (shimdir $global)
ensure_in_path $abs_shimdir $global
Add-Path -Path $abs_shimdir -Global:$global
if (!$name) { $name = strip_ext (fname $path) }
$shim = "$abs_shimdir\$($name.tolower())"
@@ -958,6 +1009,7 @@ 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 %*"
) -join "`r`n" | Out-UTF8File "$shim.cmd"
@@ -965,6 +1017,12 @@ function shim($path, $global, $name, $arg) {
@(
"#!/bin/sh",
"# $resolved_path",
"if [ `$(echo `$WSL_DISTRO_NAME) ]",
'then',
" cd `$(wslpath -u '$(Split-Path $resolved_path -Parent)')",
'else',
" cd `"$((Split-Path $resolved_path -Parent).Replace('\', '/'))`"",
'fi',
"java.exe -jar `"$resolved_path`" $arg `"$@`""
) -join "`n" | Out-UTF8File $shim -NoNewLine
} elseif ($path -match '\.py$') {
@@ -997,38 +1055,17 @@ function shim($path, $global, $name, $arg) {
}
function get_shim_path() {
$shim_path = "$(versiondir 'scoop' 'current')\supporting\shims\kiennq\shim.exe"
$shim_version = get_config SHIM 'default'
switch ($shim_version) {
'71' { $shim_path = "$(versiondir 'scoop' 'current')\supporting\shims\71\shim.exe"; Break }
'scoopcs' { $shim_path = "$(versiondir 'scoop' 'current')\supporting\shimexe\bin\shim.exe"; Break }
'kiennq' { Break } # for backward compatibility
'default' { Break }
$shim_version = get_config SHIM 'kiennq'
$shim_path = switch ($shim_version) {
'scoopcs' { "$(versiondir 'scoop' 'current')\supporting\shims\scoopcs\shim.exe" }
'71' { "$(versiondir 'scoop' 'current')\supporting\shims\71\shim.exe" }
'kiennq' { "$(versiondir 'scoop' 'current')\supporting\shims\kiennq\shim.exe" }
'default' { "$(versiondir 'scoop' 'current')\supporting\shims\scoopcs\shim.exe" }
default { warn "Unknown shim version: '$shim_version'" }
}
return $shim_path
}
function search_in_path($target) {
$path = (env 'PATH' $false) + ";" + (env 'PATH' $true)
foreach($dir in $path.split(';')) {
if(test-path "$dir\$target" -pathType leaf) {
return "$dir\$target"
}
}
}
function ensure_in_path($dir, $global) {
$path = env 'PATH' $global
$dir = fullpath $dir
if($path -notmatch [regex]::escape($dir)) {
write-output "Adding $(friendly_path $dir) to $(if($global){'global'}else{'your'}) path."
env 'PATH' $global "$dir;$path" # for future sessions...
$env:PATH = "$dir;$env:PATH" # for this session
}
}
function Get-DefaultArchitecture {
$arch = get_config DEFAULT_ARCHITECTURE
$system = if (${env:ProgramFiles(Arm)}) {
@@ -1103,45 +1140,6 @@ function Confirm-InstallationStatus {
return , $Installed
}
function strip_path($orig_path, $dir) {
if($null -eq $orig_path) { $orig_path = '' }
$stripped = [string]::join(';', @( $orig_path.split(';') | Where-Object { $_ -and $_ -ne $dir } ))
return ($stripped -ne $orig_path), $stripped
}
function add_first_in_path($dir, $global) {
$dir = fullpath $dir
# future sessions
$null, $currpath = strip_path (env 'path' $global) $dir
env 'path' $global "$dir;$currpath"
# this session
$null, $env:PATH = strip_path $env:PATH $dir
$env:PATH = "$dir;$env:PATH"
}
function remove_from_path($dir, $global) {
$dir = fullpath $dir
# future sessions
$was_in_path, $newpath = strip_path (env 'path' $global) $dir
if($was_in_path) {
Write-Output "Removing $(friendly_path $dir) from your path."
env 'path' $global $newpath
}
# current session
$was_in_path, $newpath = strip_path $env:PATH $dir
if($was_in_path) { $env:PATH = $newpath }
}
function ensure_robocopy_in_path {
if(!(Test-CommandAvailable robocopy)) {
shim "C:\Windows\System32\Robocopy.exe" $false
}
}
function wraptext($text, $width) {
if(!$width) { $width = $host.ui.rawui.buffersize.width };
$width -= 1 # be conservative: doesn't seem to print the last char
@@ -1408,50 +1406,32 @@ if ($pathExpected) {
# ├─shims
# ├─config.json
# ```
$configPortablePath = fullpath "$coreRoot\..\..\..\config.json"
$configPortablePath = Get-AbsolutePath "$coreRoot\..\..\..\config.json"
if (Test-Path $configPortablePath) {
$configFile = $configPortablePath
}
}
$scoopConfig = load_cfg $configFile
# NOTE Scoop config file migration. Remove this after 2023/6/30
if ($scoopConfig -and $scoopConfig.PSObject.Properties.Name -contains 'lastUpdate') {
$newConfigNames = @{
'lastUpdate' = 'last_update'
'SCOOP_REPO' = 'scoop_repo'
'SCOOP_BRANCH' = 'scoop_branch'
'7ZIPEXTRACT_USE_EXTERNAL' = 'use_external_7zip'
'MSIEXTRACT_USE_LESSMSI' = 'use_lessmsi'
'NO_JUNCTIONS' = 'no_junction'
'manifest_review' = 'show_manifest'
'rootPath' = 'root_path'
'globalPath' = 'global_path'
'cachePath' = 'cache_path'
}
$newConfigNames.GetEnumerator() | ForEach-Object {
if ($null -ne $scoopConfig.$($_.Key)) {
$value = $scoopConfig.$($_.Key)
$scoopConfig.PSObject.Properties.Remove($_.Key)
$scoopConfig | Add-Member -MemberType NoteProperty -Name $_.Value -Value $value
}
}
ConvertTo-Json $scoopConfig | Out-UTF8File -FilePath $configFile
}
# END NOTE
# Scoop root directory
$scoopdir = $env:SCOOP, (get_config ROOT_PATH), (Resolve-Path "$PSScriptRoot\..\..\..\.."), "$([System.Environment]::GetFolderPath('UserProfile'))\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
$scoopdir = $env:SCOOP, (get_config ROOT_PATH), "$PSScriptRoot\..\..\..\..", "$([System.Environment]::GetFolderPath('UserProfile'))\scoop" | Where-Object { $_ } | Select-Object -First 1 | Get-AbsolutePath
# Scoop global apps directory
$globaldir = $env:SCOOP_GLOBAL, (get_config GLOBAL_PATH), "$([System.Environment]::GetFolderPath('CommonApplicationData'))\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
$globaldir = $env:SCOOP_GLOBAL, (get_config GLOBAL_PATH), "$([System.Environment]::GetFolderPath('CommonApplicationData'))\scoop" | Where-Object { $_ } | Select-Object -First 1 | Get-AbsolutePath
# 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 CACHE_PATH), "$scoopdir\cache" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
$cachedir = $env:SCOOP_CACHE, (get_config CACHE_PATH), "$scoopdir\cache" | Where-Object { $_ } | Select-Object -First 1 | Get-AbsolutePath
# Scoop apps' PATH Environment Variable
$scoopPathEnvVar = switch (get_config USE_ISOLATED_PATH) {
{ $_ -is [string] } { $_.ToUpperInvariant() }
$true { 'SCOOP_PATH' }
default { 'PATH' }
}
# OS information
$WindowsBuild = [System.Environment]::OSVersion.Version.Build

View File

@@ -46,12 +46,6 @@ function Expand-7zipArchive {
if (!$Status) {
abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $LogPath)`n$(new_issue_msg $app $bucket 'decompress error')"
}
if (!$IsTar -and $ExtractDir) {
movedir "$DestinationPath\$ExtractDir" $DestinationPath | Out-Null
}
if (Test-Path $LogPath) {
Remove-Item $LogPath -Force
}
if ($IsTar) {
# Check for tar
$Status = Invoke-ExternalCommand $7zPath @('l', $Path) -LogPath $LogPath
@@ -63,8 +57,15 @@ function Expand-7zipArchive {
abort "Failed to list files in $Path.`nNot a 7-Zip supported archive file."
}
}
if (!$IsTar -and $ExtractDir) {
movedir "$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
}
if ($Removal) {
# Remove original archive file
if (($Path -replace '.*\.([^\.]*)$', '$1') -eq '001') {
# Remove splited 7-zip archive parts
Get-ChildItem "$($Path -replace '\.[^\.]*$', '').???" | Remove-Item -Force
@@ -72,6 +73,7 @@ function Expand-7zipArchive {
# Remove splitted RAR archive parts
Get-ChildItem "$($Path -replace '\.part(\d+)\.rar$', '').part*.rar" | Remove-Item -Force
} else {
# Remove original archive file
Remove-Item $Path -Force
}
}
@@ -112,17 +114,19 @@ function Expand-ZstdArchive {
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 -and $ExtractDir) {
movedir (Join-Path $DestinationPath $ExtractDir) $DestinationPath | Out-Null
}
if (Test-Path $LogPath) {
Remove-Item $LogPath -Force
}
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
}
}
function Expand-MsiArchive {
@@ -275,14 +279,32 @@ function Expand-DarkArchive {
$Removal
)
$LogPath = "$(Split-Path $Path)\dark.log"
$ArgList = @('-nologo', '-x', $DestinationPath, $Path)
$DarkPath = Get-HelperPath -Helper Dark
if ((Split-Path $DarkPath -Leaf) -eq 'wix.exe') {
$ArgList = @('burn', 'extract', $Path, '-out', $DestinationPath, '-outba', "$DestinationPath\UX")
} else {
$ArgList = @('-nologo', '-x', $DestinationPath, $Path)
}
if ($Switches) {
$ArgList += (-split $Switches)
}
$Status = Invoke-ExternalCommand (Get-HelperPath -Helper Dark) $ArgList -LogPath $LogPath
$Status = Invoke-ExternalCommand $DarkPath $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')"
}
if (Test-Path "$DestinationPath\WixAttachedContainer") {
Rename-Item "$DestinationPath\WixAttachedContainer" 'AttachedContainer' -ErrorAction Ignore
} else {
if (Test-Path "$DestinationPath\AttachedContainer\a0") {
$Xml = [xml](Get-Content -Raw "$DestinationPath\UX\manifest.xml" -Encoding utf8)
$Xml.BurnManifest.UX.Payload | ForEach-Object {
Rename-Item "$DestinationPath\UX\$($_.SourcePath)" $_.FilePath -ErrorAction Ignore
}
$Xml.BurnManifest.Payload | ForEach-Object {
Rename-Item "$DestinationPath\AttachedContainer\$($_.SourcePath)" $_.FilePath -ErrorAction Ignore
}
}
}
if (Test-Path $LogPath) {
Remove-Item $LogPath -Force
}

View File

@@ -37,9 +37,9 @@ function Get-Dependency {
if (!$manifest) {
if (((Get-LocalBucket) -notcontains $bucket) -and $bucket) {
warn "Bucket '$bucket' not installed. Add it with 'scoop bucket add $bucket' or 'scoop bucket add $bucket <repo>'."
warn "Bucket '$bucket' not added. Add it with $(if($bucket -in (known_buckets)) { "'scoop bucket add $bucket' or " })'scoop bucket add $bucket <repo>'."
}
abort "Couldn't find manifest for '$AppName'$(if(!$bucket) { '.' } else { " from '$bucket' bucket." })"
abort "Couldn't find manifest for '$AppName'$(if($bucket) { " from '$bucket' bucket" } elseif($url) { " at '$url'" })."
}
$deps = @(Get-InstallationHelper $manifest $Architecture) + @($manifest.depends) | Select-Object -Unique

View File

@@ -8,13 +8,13 @@ function nightly_version($quiet = $false) {
function install_app($app, $architecture, $global, $suggested, $use_cache = $true, $check_hash = $true) {
$app, $manifest, $bucket, $url = Get-Manifest $app
if(!$manifest) {
abort "Couldn't find manifest for '$app'$(if($url) { " at the URL $url" })."
if (!$manifest) {
abort "Couldn't find manifest for '$app'$(if ($bucket) { " from '$bucket' bucket" } elseif ($url) { " at '$url'" })."
}
$version = $manifest.version
if(!$version) { abort "Manifest doesn't specify a version." }
if($version -match '[^\w\.\-\+_]') {
if (!$version) { abort "Manifest doesn't specify a version." }
if ($version -match '[^\w\.\-\+_]') {
abort "Manifest version has unsupported character '$($matches[0])'."
}
@@ -38,12 +38,12 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
} else {
$manifest | ConvertToPrettyJson
}
$answer = Read-Host -Prompt "Continue installation? [Y/n]"
$answer = Read-Host -Prompt 'Continue installation? [Y/n]'
if (($answer -eq 'n') -or ($answer -eq 'N')) {
return
}
}
Write-Output "Installing '$app' ($version) [$architecture]$(if ($bucket) { " from $bucket bucket" })"
Write-Output "Installing '$app' ($version) [$architecture]$(if ($bucket) { " from '$bucket' bucket" } else { " from '$url'" })"
$dir = ensure (versiondir $app $version $global)
$original_dir = $dir # keep reference to real (not linked) directory
@@ -71,7 +71,7 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
save_installed_manifest $app $bucket $dir $url
save_install_info @{ 'architecture' = $architecture; 'url' = $url; 'bucket' = $bucket } $dir
if($manifest.suggest) {
if ($manifest.suggest) {
$suggested[$app] = $manifest.suggest
}
@@ -81,13 +81,13 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
}
function Invoke-CachedDownload ($app, $version, $url, $to, $cookies = $null, $use_cache = $true) {
$cached = fullpath (cache_path $app $version $url)
$cached = cache_path $app $version $url
if(!(test-path $cached) -or !$use_cache) {
if (!(Test-Path $cached) -or !$use_cache) {
ensure $cachedir | Out-Null
Start-Download $url "$cached.download" $cookies
Move-Item "$cached.download" $cached -force
} else { write-host "Loading $(url_remote_filename $url) from cache"}
Move-Item "$cached.download" $cached -Force
} else { Write-Host "Loading $(url_remote_filename $url) from cache" }
if (!($null -eq $to)) {
if ($use_cache) {
@@ -100,55 +100,58 @@ function Invoke-CachedDownload ($app, $version, $url, $to, $cookies = $null, $us
function Start-Download ($url, $to, $cookies) {
$progress = [console]::isoutputredirected -eq $false -and
$host.name -ne 'Windows PowerShell ISE Host'
$host.name -ne 'Windows PowerShell ISE Host'
try {
$url = handle_special_urls $url
Invoke-Download $url $to $cookies $progress
} catch {
$e = $_.exception
if($e.innerexception) { $e = $e.innerexception }
if ($e.Response.StatusCode -eq 'Unauthorized') {
warn 'Token might be misconfigured.'
}
if ($e.innerexception) { $e = $e.innerexception }
throw $e
}
}
function aria_exit_code($exitcode) {
$codes = @{
0='All downloads were successful'
1='An unknown error occurred'
2='Timeout'
3='Resource was not found'
4='Aria2 saw the specified number of "resource not found" error. See --max-file-not-found option'
5='Download aborted because download speed was too slow. See --lowest-speed-limit option'
6='Network problem occurred.'
7='There were unfinished downloads. This error is only reported if all finished downloads were successful and there were unfinished downloads in a queue when aria2 exited by pressing Ctrl-C by an user or sending TERM or INT signal'
8='Remote server did not support resume when resume was required to complete download'
9='There was not enough disk space available'
10='Piece length was different from one in .aria2 control file. See --allow-piece-length-change option'
11='Aria2 was downloading same file at that moment'
12='Aria2 was downloading same info hash torrent at that moment'
13='File already existed. See --allow-overwrite option'
14='Renaming file failed. See --auto-file-renaming option'
15='Aria2 could not open existing file'
16='Aria2 could not create new file or truncate existing file'
17='File I/O error occurred'
18='Aria2 could not create directory'
19='Name resolution failed'
20='Aria2 could not parse Metalink document'
21='FTP command failed'
22='HTTP response header was bad or unexpected'
23='Too many redirects occurred'
24='HTTP authorization failed'
25='Aria2 could not parse bencoded file (usually ".torrent" file)'
26='".torrent" file was corrupted or missing information that aria2 needed'
27='Magnet URI was bad'
28='Bad/unrecognized option was given or unexpected option argument was given'
29='The remote server was unable to handle the request due to a temporary overloading or maintenance'
30='Aria2 could not parse JSON-RPC request'
31='Reserved. Not used'
32='Checksum validation failed'
0 = 'All downloads were successful'
1 = 'An unknown error occurred'
2 = 'Timeout'
3 = 'Resource was not found'
4 = 'Aria2 saw the specified number of "resource not found" error. See --max-file-not-found option'
5 = 'Download aborted because download speed was too slow. See --lowest-speed-limit option'
6 = 'Network problem occurred.'
7 = 'There were unfinished downloads. This error is only reported if all finished downloads were successful and there were unfinished downloads in a queue when aria2 exited by pressing Ctrl-C by an user or sending TERM or INT signal'
8 = 'Remote server did not support resume when resume was required to complete download'
9 = 'There was not enough disk space available'
10 = 'Piece length was different from one in .aria2 control file. See --allow-piece-length-change option'
11 = 'Aria2 was downloading same file at that moment'
12 = 'Aria2 was downloading same info hash torrent at that moment'
13 = 'File already existed. See --allow-overwrite option'
14 = 'Renaming file failed. See --auto-file-renaming option'
15 = 'Aria2 could not open existing file'
16 = 'Aria2 could not create new file or truncate existing file'
17 = 'File I/O error occurred'
18 = 'Aria2 could not create directory'
19 = 'Name resolution failed'
20 = 'Aria2 could not parse Metalink document'
21 = 'FTP command failed'
22 = 'HTTP response header was bad or unexpected'
23 = 'Too many redirects occurred'
24 = 'HTTP authorization failed'
25 = 'Aria2 could not parse bencoded file (usually ".torrent" file)'
26 = '".torrent" file was corrupted or missing information that aria2 needed'
27 = 'Magnet URI was bad'
28 = 'Bad/unrecognized option was given or unexpected option argument was given'
29 = 'The remote server was unable to handle the request due to a temporary overloading or maintenance'
30 = 'Aria2 could not parse JSON-RPC request'
31 = 'Reserved. Not used'
32 = 'Checksum validation failed'
}
if($null -eq $codes[$exitcode]) {
if ($null -eq $codes[$exitcode]) {
return 'An unknown error occurred'
}
return $codes[$exitcode]
@@ -157,7 +160,7 @@ function aria_exit_code($exitcode) {
function get_filename_from_metalink($file) {
$bytes = get_magic_bytes_pretty $file ''
# check if file starts with '<?xml'
if(!($bytes.StartsWith('3c3f786d6c'))) {
if (!($bytes.StartsWith('3c3f786d6c'))) {
return $null
}
@@ -167,7 +170,7 @@ function get_filename_from_metalink($file) {
$filename = $null
try {
$xr.ReadStartElement('metalink')
if($xr.ReadToFollowing('file') -and $xr.MoveToFirstAttribute()) {
if ($xr.ReadToFollowing('file') -and $xr.MoveToFirstAttribute()) {
$filename = $xr.Value
}
} catch [System.Xml.XmlException] {
@@ -192,22 +195,22 @@ function Invoke-CachedAria2Download ($app, $version, $manifest, $architecture, $
$options = @(
"--input-file='$urlstxt'"
"--user-agent='$(Get-UserAgent)'"
"--allow-overwrite=true"
"--auto-file-renaming=false"
'--allow-overwrite=true'
'--auto-file-renaming=false'
"--retry-wait=$(get_config 'aria2-retry-wait' 2)"
"--split=$(get_config 'aria2-split' 5)"
"--max-connection-per-server=$(get_config 'aria2-max-connection-per-server' 5)"
"--min-split-size=$(get_config 'aria2-min-split-size' '5M')"
"--console-log-level=warn"
"--enable-color=false"
"--no-conf=true"
"--follow-metalink=true"
"--metalink-preferred-protocol=https"
"--min-tls-version=TLSv1.2"
'--console-log-level=warn'
'--enable-color=false'
'--no-conf=true'
'--follow-metalink=true'
'--metalink-preferred-protocol=https'
'--min-tls-version=TLSv1.2'
"--stop-with-process=$PID"
"--continue"
"--summary-interval=0"
"--auto-save-interval=1"
'--continue'
'--summary-interval=0'
'--auto-save-interval=1'
)
if ($cookies) {
@@ -236,7 +239,7 @@ function Invoke-CachedAria2Download ($app, $version, $manifest, $architecture, $
$data.$url = @{
'target' = "$dir\$(url_filename $url)"
'cachename' = fname (cache_path $app $version $url)
'source' = fullpath (cache_path $app $version $url)
'source' = cache_path $app $version $url
}
if ((Test-Path $data.$url.source) -and -not((Test-Path "$($data.$url.source).aria2") -or (Test-Path $urlstxt)) -and $use_cache) {
@@ -246,7 +249,14 @@ function Invoke-CachedAria2Download ($app, $version, $manifest, $architecture, $
} else {
$download_finished = $false
# create aria2 input file content
$urlstxt_content += "$(handle_special_urls $url)`n"
try {
$try_url = handle_special_urls $url
} catch {
if ($_.Exception.Response.StatusCode -eq 'Unauthorized') {
warn 'Token might be misconfigured.'
}
}
$urlstxt_content += "$try_url`n"
if (!$url.Contains('sourceforge.net')) {
$urlstxt_content += " referer=$(strip_filename $url)`n"
}
@@ -296,7 +306,7 @@ function Invoke-CachedAria2Download ($app, $version, $manifest, $architecture, $
}
Write-Host ''
if($lastexitcode -gt 0) {
if ($lastexitcode -gt 0) {
error "Download failed! (Error $lastexitcode) $(aria_exit_code $lastexitcode)"
error $urlstxt_content
error $aria2
@@ -366,7 +376,7 @@ function Invoke-Download ($url, $to, $cookies, $progress) {
if ($url -match 'api\.github\.com/repos') {
$wreq.Accept = 'application/octet-stream'
$wreq.Headers['Authorization'] = "Bearer $(Get-GitHubToken)"
$wreq.Headers['X-GitHub-Api-Version'] = "2022-11-28"
$wreq.Headers['X-GitHub-Api-Version'] = '2022-11-28'
}
if ($cookies) {
$wreq.Headers.Add('Cookie', (cookie_header $cookies))
@@ -384,9 +394,9 @@ function Invoke-Download ($url, $to, $cookies, $progress) {
} catch [System.Net.WebException] {
$exc = $_.Exception
$handledCodes = @(
[System.Net.HttpStatusCode]::MovedPermanently, # HTTP 301
[System.Net.HttpStatusCode]::Found, # HTTP 302
[System.Net.HttpStatusCode]::SeeOther, # HTTP 303
[System.Net.HttpStatusCode]::MovedPermanently, # HTTP 301
[System.Net.HttpStatusCode]::Found, # HTTP 302
[System.Net.HttpStatusCode]::SeeOther, # HTTP 303
[System.Net.HttpStatusCode]::TemporaryRedirect # HTTP 307
)
@@ -415,7 +425,7 @@ function Invoke-Download ($url, $to, $cookies, $progress) {
}
$total = $wres.ContentLength
if($total -eq -1 -and $wreq -is [net.ftpwebrequest]) {
if ($total -eq -1 -and $wreq -is [net.ftpwebrequest]) {
$total = ftp_file_size($url)
}
@@ -425,7 +435,7 @@ function Invoke-Download ($url, $to, $cookies, $progress) {
Write-DownloadProgress $read $total $url
}
} else {
write-host "Downloading $url ($(filesize $total))..."
Write-Host "Downloading $url ($(filesize $total))..."
function Trace-DownloadProgress {
#no op
}
@@ -434,12 +444,12 @@ function Invoke-Download ($url, $to, $cookies, $progress) {
try {
$s = $wres.getresponsestream()
$fs = [io.file]::openwrite($to)
$buffer = new-object byte[] 2048
$buffer = New-Object byte[] 2048
$totalRead = 0
$sw = [diagnostics.stopwatch]::StartNew()
Trace-DownloadProgress $totalRead
while(($read = $s.read($buffer, 0, $buffer.length)) -gt 0) {
while (($read = $s.read($buffer, 0, $buffer.length)) -gt 0) {
$fs.write($buffer, 0, $read)
$totalRead += $read
if ($sw.elapsedmilliseconds -gt 100) {
@@ -452,13 +462,13 @@ function Invoke-Download ($url, $to, $cookies, $progress) {
} finally {
if ($progress) {
[console]::CursorVisible = $true
write-host
Write-Host
}
if ($fs) {
$fs.close()
}
if ($s) {
$s.close();
$s.close()
}
$wres.close()
}
@@ -472,31 +482,31 @@ function Format-DownloadProgress ($url, $read, $total, $console) {
# pre-generate LHS and RHS of progress string
# so we know how much space we have
$left = "$filename ($(filesize $total))"
$right = [string]::Format("{0,3}%", $p)
$left = "$filename ($(filesize $total))"
$right = [string]::Format('{0,3}%', $p)
# calculate remaining width for progress bar
$midwidth = $console.BufferSize.Width - ($left.Length + $right.Length + 8)
$midwidth = $console.BufferSize.Width - ($left.Length + $right.Length + 8)
# calculate how many characters are completed
$completed = [math]::Abs([math]::Round(($p / 100) * $midwidth, 0) - 1)
# generate dashes to symbolise completed
if ($completed -gt 1) {
$dashes = [string]::Join("", ((1..$completed) | ForEach-Object {"="}))
$dashes = [string]::Join('', ((1..$completed) | ForEach-Object { '=' }))
}
# this is why we calculate $completed - 1 above
$dashes += switch($p) {
100 {"="}
default {">"}
$dashes += switch ($p) {
100 { '=' }
default { '>' }
}
# the remaining characters are filled with spaces
$spaces = switch($dashes.Length) {
$midwidth {[string]::Empty}
$spaces = switch ($dashes.Length) {
$midwidth { [string]::Empty }
default {
[string]::Join("", ((1..($midwidth - $dashes.Length)) | ForEach-Object {" "}))
[string]::Join('', ((1..($midwidth - $dashes.Length)) | ForEach-Object { ' ' }))
}
}
@@ -504,32 +514,32 @@ function Format-DownloadProgress ($url, $read, $total, $console) {
}
function Write-DownloadProgress ($read, $total, $url) {
$console = $host.UI.RawUI;
$left = $console.CursorPosition.X;
$top = $console.CursorPosition.Y;
$width = $console.BufferSize.Width;
$console = $host.UI.RawUI
$left = $console.CursorPosition.X
$top = $console.CursorPosition.Y
$width = $console.BufferSize.Width
if($read -eq 0) {
if ($read -eq 0) {
$maxOutputLength = $(Format-DownloadProgress $url 100 $total $console).length
if (($left + $maxOutputLength) -gt $width) {
# not enough room to print progress on this line
# print on new line
write-host
Write-Host
$left = 0
$top = $top + 1
if($top -gt $console.CursorPosition.Y) { $top = $console.CursorPosition.Y }
$top = $top + 1
if ($top -gt $console.CursorPosition.Y) { $top = $console.CursorPosition.Y }
}
}
write-host $(Format-DownloadProgress $url $read $total $console) -nonewline
Write-Host $(Format-DownloadProgress $url $read $total $console) -NoNewline
[console]::SetCursorPosition($left, $top)
}
function Invoke-ScoopDownload ($app, $version, $manifest, $bucket, $architecture, $dir, $use_cache = $true, $check_hash = $true) {
# we only want to show this warning once
if(!$use_cache) { warn "Cache is being ignored." }
if (!$use_cache) { warn 'Cache is being ignored.' }
# can be multiple urls: if there are, then msi or installer should go last,
# can be multiple urls: if there are, then installer should go last,
# so that $fname is set properly
$urls = @(script:url $manifest $architecture)
@@ -542,42 +552,42 @@ function Invoke-ScoopDownload ($app, $version, $manifest, $bucket, $architecture
# 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;
$extracted = 0
# download first
if(Test-Aria2Enabled) {
if (Test-Aria2Enabled) {
Invoke-CachedAria2Download $app $version $manifest $architecture $dir $cookies $use_cache $check_hash
} else {
foreach($url in $urls) {
foreach ($url in $urls) {
$fname = url_filename $url
try {
Invoke-CachedDownload $app $version $url "$dir\$fname" $cookies $use_cache
} catch {
write-host -f darkred $_
Write-Host -f darkred $_
abort "URL $url is not valid"
}
if($check_hash) {
if ($check_hash) {
$manifest_hash = hash_for_url $manifest $url $architecture
$ok, $err = check_hash "$dir\$fname" $manifest_hash $(show_app $app $bucket)
if(!$ok) {
if (!$ok) {
error $err
$cached = cache_path $app $version $url
if(test-path $cached) {
if (Test-Path $cached) {
# rm cached file
Remove-Item -force $cached
Remove-Item -Force $cached
}
if($url.Contains('sourceforge.net')) {
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')
}
}
}
}
foreach($url in $urls) {
foreach ($url in $urls) {
$fname = url_filename $url
$extract_dir = $extract_dirs[$extracted]
@@ -587,32 +597,29 @@ function Invoke-ScoopDownload ($app, $version, $manifest, $bucket, $architecture
$extract_fn = $null
if ($manifest.innosetup) {
$extract_fn = 'Expand-InnoArchive'
} elseif($fname -match '\.zip$') {
} 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$') {
# check manifest doesn't use deprecated install method
if(msi $manifest $architecture) {
warn "MSI install is deprecated. If you maintain this manifest, please refer to the manifest reference docs."
} else {
$extract_fn = 'Expand-MsiArchive'
}
} elseif(Test-ZstdRequirement -Uri $fname) { # Zstd first
} 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
} elseif (Test-7zipRequirement -Uri $fname) {
# 7zip
$extract_fn = 'Expand-7zipArchive'
}
if($extract_fn) {
Write-Host "Extracting " -NoNewline
if ($extract_fn) {
Write-Host 'Extracting ' -NoNewline
Write-Host $fname -f Cyan -NoNewline
Write-Host " ... " -NoNewline
Write-Host ' ... ' -NoNewline
& $extract_fn -Path "$dir\$fname" -DestinationPath "$dir\$extract_to" -ExtractDir $extract_dir -Removal
Write-Host "done." -f Green
Write-Host 'done.' -f Green
$extracted++
}
}
@@ -621,7 +628,7 @@ function Invoke-ScoopDownload ($app, $version, $manifest, $bucket, $architecture
}
function cookie_header($cookies) {
if(!$cookies) { return }
if (!$cookies) { return }
$vals = $cookies.psobject.properties | ForEach-Object {
"$($_.name)=$($_.value)"
@@ -631,9 +638,7 @@ function cookie_header($cookies) {
}
function is_in_dir($dir, $check) {
$check = "$(fullpath $check)"
$dir = "$(fullpath $dir)"
$check -match "^$([regex]::Escape("$dir"))([/\\]|`$)"
$check -match "^$([regex]::Escape("$dir"))([/\\]|$)"
}
function ftp_file_size($url) {
@@ -644,29 +649,28 @@ function ftp_file_size($url) {
# hashes
function hash_for_url($manifest, $url, $arch) {
$hashes = @(hash $manifest $arch) | Where-Object { $_ -ne $null };
$hashes = @(hash $manifest $arch) | Where-Object { $_ -ne $null }
if($hashes.length -eq 0) { return $null }
if ($hashes.length -eq 0) { return $null }
$urls = @(script:url $manifest $arch)
$index = [array]::indexof($urls, $url)
if($index -eq -1) { abort "Couldn't find hash in manifest for '$url'." }
if ($index -eq -1) { abort "Couldn't find hash in manifest for '$url'." }
@($hashes)[$index]
}
# returns (ok, err)
function check_hash($file, $hash, $app_name) {
$file = fullpath $file
if(!$hash) {
if (!$hash) {
warn "Warning: No hash in manifest. SHA256 for '$(fname $file)' is:`n $((Get-FileHash -Path $file -Algorithm SHA256).Hash.ToLower())"
return $true, $null
}
Write-Host "Checking hash of " -NoNewline
Write-Host 'Checking hash of ' -NoNewline
Write-Host $(url_remote_filename $url) -f Cyan -NoNewline
Write-Host " ... " -nonewline
Write-Host ' ... ' -NoNewline
$algorithm, $expected = get_hash $hash
if ($null -eq $algorithm) {
return $false, "Hash type '$algorithm' isn't supported."
@@ -675,152 +679,85 @@ function check_hash($file, $hash, $app_name) {
$actual = (Get-FileHash -Path $file -Algorithm $algorithm).Hash.ToLower()
$expected = $expected.ToLower()
if($actual -ne $expected) {
if ($actual -ne $expected) {
$msg = "Hash check failed!`n"
$msg += "App: $app_name`n"
$msg += "URL: $url`n"
if(Test-Path $file) {
if (Test-Path $file) {
$msg += "First bytes: $((get_magic_bytes_pretty $file ' ').ToUpper())`n"
}
if($expected -or $actual) {
if ($expected -or $actual) {
$msg += "Expected: $expected`n"
$msg += "Actual: $actual"
}
return $false, $msg
}
Write-Host "ok." -f Green
Write-Host 'ok.' -f Green
return $true, $null
}
# for dealing with installers
function args($config, $dir, $global) {
if($config) { return $config | ForEach-Object { (format $_ @{'dir'=$dir;'global'=$global}) } }
if ($config) { return $config | ForEach-Object { (format $_ @{'dir' = $dir; 'global' = $global }) } }
@()
}
function run_installer($fname, $manifest, $architecture, $dir, $global) {
# MSI or other installer
$msi = msi $manifest $architecture
$installer = installer $manifest $architecture
if($installer.script) {
write-output "Running installer script..."
if ($installer.script) {
Write-Output 'Running installer script...'
Invoke-Command ([scriptblock]::Create($installer.script -join "`r`n"))
return
}
if($msi) {
install_msi $fname $dir $msi
} elseif($installer) {
install_prog $fname $dir $installer $global
}
}
# deprecated (see also msi_installed)
function install_msi($fname, $dir, $msi) {
$msifile = "$dir\$(coalesce $msi.file "$fname")"
if(!(is_in_dir $dir $msifile)) {
abort "Error in manifest: MSI file $msifile is outside the app directory."
}
if(!($msi.code)) { abort "Error in manifest: Couldn't find MSI code."}
if(msi_installed $msi.code) { abort "The MSI package is already installed on this system." }
$logfile = "$dir\install.log"
$arg = @("/i `"$msifile`"", '/norestart', "/lvp `"$logfile`"", "TARGETDIR=`"$dir`"",
"INSTALLDIR=`"$dir`"") + @(args $msi.args $dir)
if($msi.silent) { $arg += '/qn', 'ALLUSERS=2', 'MSIINSTALLPERUSER=1' }
else { $arg += '/qb-!' }
$continue_exit_codes = @{ 3010 = "a restart is required to complete installation" }
$installed = Invoke-ExternalCommand 'msiexec' $arg -Activity "Running installer..." -ContinueExitCodes $continue_exit_codes
if(!$installed) {
abort "Installation aborted. You might need to run 'scoop uninstall $app' before trying again."
}
Remove-Item $logfile
Remove-Item $msifile
}
# deprecated
# get-wmiobject win32_product is slow and checks integrity of each installed program,
# so this uses the [wmi] type accelerator instead
# http://blogs.technet.com/b/heyscriptingguy/archive/2011/12/14/use-powershell-to-find-and-uninstall-software.aspx
function msi_installed($code) {
$path = "hklm:\software\microsoft\windows\currentversion\uninstall\$code"
if(!(test-path $path)) { return $false }
$key = Get-Item $path
$name = $key.getvalue('displayname')
$version = $key.getvalue('displayversion')
$classkey = "IdentifyingNumber=`"$code`",Name=`"$name`",Version=`"$version`""
try { $wmi = [wmi]"Win32_Product.$classkey"; $true } catch { $false }
}
function install_prog($fname, $dir, $installer, $global) {
$prog = "$dir\$(coalesce $installer.file "$fname")"
if(!(is_in_dir $dir $prog)) {
abort "Error in manifest: Installer $prog is outside the app directory."
}
$arg = @(args $installer.args $dir $global)
if($prog.endswith('.ps1')) {
& $prog @arg
} 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."
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."
}
# Don't remove installer if "keep" flag is set to true
if(!($installer.keep -eq "true")) {
Remove-Item $prog
$arg = @(args $installer.args $dir $global)
if ($prog.endswith('.ps1')) {
& $prog @arg
} 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) {
$msi = msi $manifest $architecture
$uninstaller = uninstaller $manifest $architecture
$version = $manifest.version
if($uninstaller.script) {
write-output "Running uninstaller script..."
if ($uninstaller.script) {
Write-Output 'Running uninstaller script...'
Invoke-Command ([scriptblock]::Create($uninstaller.script -join "`r`n"))
return
}
if($msi -or $uninstaller) {
$exe = $null; $arg = $null; $continue_exit_codes = @{}
if($msi) {
$code = $msi.code
$exe = "msiexec";
$arg = @("/norestart", "/x $code")
if($msi.silent) {
$arg += '/qn', 'ALLUSERS=2', 'MSIINSTALLPERUSER=1'
} else {
$arg += '/qb-!'
}
$continue_exit_codes.1605 = 'not installed, skipping'
$continue_exit_codes.3010 = 'restart required'
} elseif($uninstaller) {
$exe = "$dir\$($uninstaller.file)"
$arg = args $uninstaller.args
if(!(is_in_dir $dir $exe)) {
warn "Error in manifest: Installer $exe is outside the app directory, skipping."
$exe = $null;
} elseif(!(test-path $exe)) {
warn "Uninstaller $exe is missing, skipping."
$exe = $null;
}
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($exe) {
if($exe.endswith('.ps1')) {
& $exe @arg
if ($prog) {
if ($prog.endswith('.ps1')) {
& $prog @arg
} else {
$uninstalled = Invoke-ExternalCommand $exe $arg -Activity "Running uninstaller..." -ContinueExitCodes $continue_exit_codes
if(!$uninstalled) { abort "Uninstallation aborted." }
$uninstalled = Invoke-ExternalCommand $prog $arg -Activity 'Running uninstaller...'
if (!$uninstalled) {
abort 'Uninstallation aborted.'
}
}
}
}
@@ -828,7 +765,7 @@ function run_uninstaller($manifest, $architecture, $dir) {
# get target, name, arguments for shim
function shim_def($item) {
if($item -is [array]) { return $item }
if ($item -is [array]) { return $item }
return $item, (strip_ext (fname $item)), $null
}
@@ -836,18 +773,18 @@ function create_shims($manifest, $dir, $global, $arch) {
$shims = @(arch_specific 'bin' $manifest $arch)
$shims | Where-Object { $_ -ne $null } | ForEach-Object {
$target, $name, $arg = shim_def $_
write-output "Creating shim for '$name'."
Write-Output "Creating shim for '$name'."
if(test-path "$dir\$target" -pathType leaf) {
if (Test-Path "$dir\$target" -PathType leaf) {
$bin = "$dir\$target"
} elseif(test-path $target -pathType leaf) {
} elseif (Test-Path $target -PathType leaf) {
$bin = $target
} else {
$bin = search_in_path $target
$bin = (Get-Command $target).Source
}
if(!$bin) { abort "Can't shim '$target': File doesn't exist."}
if (!$bin) { abort "Can't shim '$target': File doesn't exist." }
shim $bin $global $name (substitute $arg @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir})
shim $bin $global $name (substitute $arg @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir })
}
}
@@ -936,18 +873,18 @@ function unlink_current($versiondir) {
# to undo after installers add to path so that scoop manifest can keep track of this instead
function ensure_install_dir_not_in_path($dir, $global) {
$path = (env 'path' $global)
$path = (Get-EnvVar -Name 'PATH' -Global:$global)
$fixed, $removed = find_dir_or_subdir $path "$dir"
if($removed) {
$removed | ForEach-Object { "Installer added '$(friendly_path $_)' to path. Removing."}
env 'path' $global $fixed
if ($removed) {
$removed | ForEach-Object { "Installer added '$(friendly_path $_)' to path. Removing." }
Set-EnvVar -Name 'PATH' -Value $fixed -Global:$global
}
if(!$global) {
$fixed, $removed = find_dir_or_subdir (env 'path' $true) "$dir"
if($removed) {
$removed | ForEach-Object { warn "Installer added '$_' to system path. You might want to remove this manually (requires admin permission)."}
if (!$global) {
$fixed, $removed = find_dir_or_subdir (Get-EnvVar -Name 'PATH' -Global) "$dir"
if ($removed) {
$removed | ForEach-Object { warn "Installer added '$_' to system path. You might want to remove this manually (requires admin permission)." }
}
}
}
@@ -957,8 +894,8 @@ function find_dir_or_subdir($path, $dir) {
$fixed = @()
$removed = @()
$path.split(';') | ForEach-Object {
if($_) {
if(($_ -eq $dir) -or ($_ -like "$dir\*")) { $removed += $_ }
if ($_) {
if (($_ -eq $dir) -or ($_ -like "$dir\*")) { $removed += $_ }
else { $fixed += $_ }
}
}
@@ -969,29 +906,21 @@ function env_add_path($manifest, $dir, $global, $arch) {
$env_add_path = arch_specific 'env_add_path' $manifest $arch
$dir = $dir.TrimEnd('\')
if ($env_add_path) {
# GH-3785: Add path in ascending order.
[Array]::Reverse($env_add_path)
$env_add_path | Where-Object { $_ } | ForEach-Object {
if ($_ -eq '.') {
$path_dir = $dir
} else {
$path_dir = Join-Path $dir $_
}
if (!(is_in_dir $dir $path_dir)) {
abort "Error in manifest: env_add_path '$_' is outside the app directory."
}
add_first_in_path $path_dir $global
if (get_config USE_ISOLATED_PATH) {
Add-Path -Path ('%' + $scoopPathEnvVar + '%') -Global:$global
}
$path = $env_add_path.Where({ $_ }).ForEach({ Join-Path $dir $_ | Get-AbsolutePath }).Where({ is_in_dir $dir $_ })
Add-Path -Path $path -TargetEnvVar $scoopPathEnvVar -Global:$global -Force
}
}
function env_rm_path($manifest, $dir, $global, $arch) {
$env_add_path = arch_specific 'env_add_path' $manifest $arch
$env_add_path | Where-Object { $_ } | ForEach-Object {
$path_dir = Join-Path $dir $_
remove_from_path $path_dir $global
$dir = $dir.TrimEnd('\')
if ($env_add_path) {
$path = $env_add_path.Where({ $_ }).ForEach({ Join-Path $dir $_ | Get-AbsolutePath }).Where({ is_in_dir $dir $_ })
Remove-Path -Path $path -Global:$global # TODO: Remove after forced isolating Scoop path
Remove-Path -Path $path -TargetEnvVar $scoopPathEnvVar -Global:$global
}
}
@@ -999,9 +928,9 @@ function env_set($manifest, $dir, $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 $name $global $val
$name = $_.name
$val = format $env_set.$($_.name) @{ 'dir' = $dir }
Set-EnvVar -Name $name -Value $val -Global:$global
Set-Content env:\$name $val
}
}
@@ -1011,7 +940,7 @@ function env_rm($manifest, $global, $arch) {
if ($env_set) {
$env_set | Get-Member -Member NoteProperty | ForEach-Object {
$name = $_.name
env $name $global $null
Set-EnvVar -Name $name -Value $null -Global:$global
if (Test-Path env:\$name) { Remove-Item env:\$name }
}
}
@@ -1022,7 +951,7 @@ function Invoke-HookScript {
param(
[Parameter(Mandatory = $true)]
[ValidateSet('pre_install', 'post_install',
'pre_uninstall', 'post_uninstall')]
'pre_uninstall', 'post_uninstall')]
[String] $HookType,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
@@ -1040,10 +969,10 @@ function Invoke-HookScript {
}
function show_notes($manifest, $dir, $original_dir, $persist_dir) {
if($manifest.notes) {
write-output "Notes"
write-output "-----"
write-output (wraptext (substitute $manifest.notes @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir}))
if ($manifest.notes) {
Write-Output 'Notes'
Write-Output '-----'
Write-Output (wraptext (substitute $manifest.notes @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir }))
}
}
@@ -1088,23 +1017,23 @@ function ensure_none_failed($apps) {
function show_suggestions($suggested) {
$installed_apps = (installed_apps $true) + (installed_apps $false)
foreach($app in $suggested.keys) {
$features = $suggested[$app] | get-member -type noteproperty | ForEach-Object { $_.name }
foreach($feature in $features) {
foreach ($app in $suggested.keys) {
$features = $suggested[$app] | Get-Member -type noteproperty | ForEach-Object { $_.name }
foreach ($feature in $features) {
$feature_suggestions = $suggested[$app].$feature
$fulfilled = $false
foreach($suggestion in $feature_suggestions) {
foreach ($suggestion in $feature_suggestions) {
$suggested_app, $bucket, $null = parse_app $suggestion
if($installed_apps -contains $suggested_app) {
$fulfilled = $true;
break;
if ($installed_apps -contains $suggested_app) {
$fulfilled = $true
break
}
}
if(!$fulfilled) {
write-host "'$app' suggests installing '$([string]::join("' or '", $feature_suggestions))'."
if (!$fulfilled) {
Write-Host "'$app' suggests installing '$([string]::join("' or '", $feature_suggestions))'."
}
}
}
@@ -1129,22 +1058,22 @@ function persist_def($persist) {
function persist_data($manifest, $original_dir, $persist_dir) {
$persist = $manifest.persist
if($persist) {
if ($persist) {
$persist_dir = ensure $persist_dir
if ($persist -is [String]) {
$persist = @($persist);
$persist = @($persist)
}
$persist | ForEach-Object {
$source, $target = persist_def $_
write-host "Persisting $source"
Write-Host "Persisting $source"
$source = $source.TrimEnd("/").TrimEnd("\\")
$source = $source.TrimEnd('/').TrimEnd('\\')
$source = fullpath "$dir\$source"
$target = fullpath "$persist_dir\$target"
$source = "$dir\$source"
$target = "$persist_dir\$target"
# if we have had persist data in the store, just create link and go
if (Test-Path $target) {
@@ -1205,7 +1134,7 @@ function unlink_persist_data($manifest, $dir) {
# check whether write permission for Users usergroup is set to global persist dir, if not then set
function persist_permission($manifest, $global) {
if($global -and $manifest.persist -and (is_admin)) {
if ($global -and $manifest.persist -and (is_admin)) {
$path = persistdir $null $global
$user = New-Object System.Security.Principal.SecurityIdentifier 'S-1-5-32-545'
$target_rule = New-Object System.Security.AccessControl.FileSystemAccessRule($user, 'Write', 'ObjectInherit', 'none', 'Allow')

View File

@@ -7,7 +7,7 @@ function parse_json($path) {
try {
Get-Content $path -Raw -Encoding UTF8 | ConvertFrom-Json -ErrorAction Stop
} catch {
warn "Error parsing JSON at $path."
warn "Error parsing JSON at '$path'."
}
}
@@ -27,7 +27,7 @@ function url_manifest($url) {
try {
$str | ConvertFrom-Json -ErrorAction Stop
} catch {
warn "Error parsing JSON at $url."
warn "Error parsing JSON at '$url'."
}
}
@@ -44,24 +44,23 @@ function Get-Manifest($app) {
if ($bucket) {
$manifest = manifest $app $bucket
} else {
foreach ($bucket in Get-LocalBucket) {
$manifest = manifest $app $bucket
foreach ($tekcub in Get-LocalBucket) {
$manifest = manifest $app $tekcub
if ($manifest) {
$bucket = $tekcub
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
if (Test-Path $app) {
$url = Convert-Path $app
$app = appname_from_url $url
$manifest = url_manifest $url
} else {
if (($app -match '\\/') -or $app.EndsWith('.json')) { $url = $app }
$app = appname_from_url $app
}
}
}
@@ -156,7 +155,6 @@ function generate_user_manifest($app, $bucket, $version) {
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 msi($manifest, $arch) { arch_specific 'msi' $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}

View File

@@ -42,15 +42,13 @@ function uninstall_psmodule($manifest, $dir, $global) {
}
function ensure_in_psmodulepath($dir, $global) {
$path = env 'psmodulepath' $global
$path = Get-EnvVar -Name 'PSModulePath' -Global:$global
if (!$global -and $null -eq $path) {
$path = "$env:USERPROFILE\Documents\WindowsPowerShell\Modules"
}
$dir = fullpath $dir
if ($path -notmatch [Regex]::Escape($dir)) {
Write-Output "Adding $(friendly_path $dir) to $(if($global){'global'}else{'your'}) PowerShell module path."
env 'psmodulepath' $global "$dir;$path" # for future sessions...
$env:psmodulepath = "$dir;$env:psmodulepath" # for this session
Set-EnvVar -Name 'PSModulePath' -Value "$dir;$path" -Global:$global
}
}

176
lib/system.ps1 Normal file
View File

@@ -0,0 +1,176 @@
# System-related functions
## Environment Variables
function Publish-EnvVar {
if (-not ('Win32.NativeMethods' -as [Type])) {
Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @'
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
uint fuFlags, uint uTimeout, out UIntPtr lpdwResult
);
'@
}
$HWND_BROADCAST = [IntPtr] 0xffff
$WM_SETTINGCHANGE = 0x1a
$result = [UIntPtr]::Zero
[Win32.NativeMethods]::SendMessageTimeout($HWND_BROADCAST,
$WM_SETTINGCHANGE,
[UIntPtr]::Zero,
'Environment',
2,
5000,
[ref] $result
) | Out-Null
}
function Get-EnvVar {
param(
[string]$Name,
[switch]$Global
)
$registerKey = if ($Global) {
Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
} else {
Get-Item -Path 'HKCU:'
}
$envRegisterKey = $registerKey.OpenSubKey('Environment')
$registryValueOption = [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames
$envRegisterKey.GetValue($Name, $null, $registryValueOption)
}
function Set-EnvVar {
param(
[string]$Name,
[string]$Value,
[switch]$Global
)
$registerKey = if ($Global) {
Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
} else {
Get-Item -Path 'HKCU:'
}
$envRegisterKey = $registerKey.OpenSubKey('Environment', $true)
if ($null -eq $Value -or $Value -eq '') {
if ($envRegisterKey.GetValue($Name)) {
$envRegisterKey.DeleteValue($Name)
}
} else {
$registryValueKind = if ($Value.Contains('%')) {
[Microsoft.Win32.RegistryValueKind]::ExpandString
} elseif ($envRegisterKey.GetValue($Name)) {
$envRegisterKey.GetValueKind($Name)
} else {
[Microsoft.Win32.RegistryValueKind]::String
}
$envRegisterKey.SetValue($Name, $Value, $registryValueKind)
}
Publish-EnvVar
}
function Split-PathLikeEnvVar {
param(
[string[]]$Pattern,
[string]$Path
)
if ($null -eq $Path -and $Path -eq '') {
return $null, $null
} else {
$splitPattern = $Pattern.Split(';', [System.StringSplitOptions]::RemoveEmptyEntries)
$splitPath = $Path.Split(';', [System.StringSplitOptions]::RemoveEmptyEntries)
$inPath = @()
foreach ($p in $splitPattern) {
$inPath += $splitPath.Where({ $_ -like $p })
$splitPath = $splitPath.Where({ $_ -notlike $p })
}
return ($inPath -join ';'), ($splitPath -join ';')
}
}
function Add-Path {
param(
[string[]]$Path,
[string]$TargetEnvVar = 'PATH',
[switch]$Global,
[switch]$Force,
[switch]$Quiet
)
# future sessions
$inPath, $strippedPath = Split-PathLikeEnvVar $Path (Get-EnvVar -Name $TargetEnvVar -Global:$Global)
if (!$inPath -or $Force) {
if (!$Quiet) {
$Path | ForEach-Object {
Write-Host "Adding $(friendly_path $_) to $(if ($Global) {'global'} else {'your'}) path."
}
}
Set-EnvVar -Name $TargetEnvVar -Value ((@($Path) + $strippedPath) -join ';') -Global:$Global
}
# current session
$inPath, $strippedPath = Split-PathLikeEnvVar $Path $env:PATH
if (!$inPath -or $Force) {
$env:PATH = (@($Path) + $strippedPath) -join ';'
}
}
function Remove-Path {
param(
[string[]]$Path,
[string]$TargetEnvVar = 'PATH',
[switch]$Global,
[switch]$Quiet,
[switch]$PassThru
)
# future sessions
$inPath, $strippedPath = Split-PathLikeEnvVar $Path (Get-EnvVar -Name $TargetEnvVar -Global:$Global)
if ($inPath) {
if (!$Quiet) {
$Path | ForEach-Object {
Write-Host "Removing $(friendly_path $_) from $(if ($Global) {'global'} else {'your'}) path."
}
}
Set-EnvVar -Name $TargetEnvVar -Value $strippedPath -Global:$Global
}
# current session
$inSessionPath, $strippedPath = Split-PathLikeEnvVar $Path $env:PATH
if ($inSessionPath) {
$env:PATH = $strippedPath
}
if ($PassThru) {
return $inPath
}
}
## Deprecated functions
function env($name, $global, $val) {
if ($PSBoundParameters.ContainsKey('val')) {
Show-DeprecatedWarning $MyInvocation 'Set-EnvVar'
Set-EnvVar -Name $name -Value $val -Global:$global
} else {
Show-DeprecatedWarning $MyInvocation 'Get-EnvVar'
Get-EnvVar -Name $name -Global:$global
}
}
function strip_path($orig_path, $dir) {
Show-DeprecatedWarning $MyInvocation 'Split-PathLikeEnvVar'
Split-PathLikeEnvVar -Name $dir -Path $orig_path
}
function add_first_in_path($dir, $global) {
Show-DeprecatedWarning $MyInvocation 'Add-Path'
Add-Path -Path $dir -Global:$global -Force
}
function remove_from_path($dir, $global) {
Show-DeprecatedWarning $MyInvocation 'Remove-Path'
Remove-Path -Path $dir -Global:$global
}

View File

@@ -44,13 +44,16 @@ function add_alias($name, $command) {
# get current aliases from config
$aliases = init_alias_config
if ($aliases.$name) {
abort "Alias $name already exists."
abort "Alias '$name' already exists."
}
$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."
}
$script =
@(
"# Summary: $description",
@@ -67,18 +70,18 @@ function add_alias($name, $command) {
function rm_alias($name) {
$aliases = init_alias_config
if (!$name) {
abort 'Which alias should be removed?'
abort 'Alias to be removed has not been specified!'
}
if ($aliases.$name) {
"Removing alias $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."
abort "Alias '$name' doesn't exist."
}
}

View File

@@ -14,14 +14,14 @@ if (!$app) { error '<app> missing'; my_usage; exit 1 }
$null, $manifest, $bucket, $url = Get-Manifest $app
if ($manifest) {
$style = get_config CAT_STYLE
if ($style) {
$manifest | ConvertToPrettyJson | bat --no-paging --style $style --language json
} else {
$manifest | ConvertToPrettyJson
}
$style = get_config CAT_STYLE
if ($style) {
$manifest | ConvertToPrettyJson | bat --no-paging --style $style --language json
} else {
$manifest | ConvertToPrettyJson
}
} else {
abort "Couldn't find manifest for '$app'$(if($url) { " at the URL $url" })."
abort "Couldn't find manifest for '$app'$(if($bucket) { " from '$bucket' bucket" } elseif($url) { " at '$url'" })."
}
exit $exitCode

View File

@@ -20,7 +20,7 @@ $issues += !(check_long_paths)
$issues += !(Get-WindowsDeveloperModeStatus)
if (!(Test-HelperInstalled -Helper 7zip) -and !(get_config USE_EXTERNAL_7ZIP)) {
warn "'7-Zip' is not installed! It's required for unpacking most programs. Please Run 'scoop install 7zip' or 'scoop install 7zip-zstd'."
warn "'7-Zip' is not installed! It's required for unpacking most programs. Please Run 'scoop install 7zip'."
$issues++
}

View File

@@ -115,6 +115,11 @@
# Nightly version is formatted as 'nightly-yyyyMMdd' and will be updated after one day if this is set to $true.
# Otherwise, nightly version will not be updated unless `--force` is used.
#
# use_isolated_path: $true|$false|[string]
# When set to $true, Scoop will use `SCOOP_PATH` environment variable to store apps' `PATH`s.
# When set to arbitrary non-empty string, Scoop will use that string as the environment variable name instead.
# This is useful when you want to isolate Scoop from the system `PATH`.
#
# ARIA2 configuration
# -------------------
#
@@ -151,30 +156,12 @@ if (!$name) {
} elseif ($name -like '--help') {
my_usage
} elseif ($name -like 'rm') {
# NOTE Scoop config file migration. Remove this after 2023/6/30
if ($value -notin 'SCOOP_REPO', 'SCOOP_BRANCH' -and $value -in $newConfigNames.Keys) {
warn ('Config option "{0}" is deprecated, please use "{1}" instead next time.' -f $value, $newConfigNames.$value)
$value = $newConfigNames.$value
}
# END NOTE
set_config $value $null | Out-Null
Write-Host "'$value' has been removed"
} elseif ($null -ne $value) {
# NOTE Scoop config file migration. Remove this after 2023/6/30
if ($name -notin 'SCOOP_REPO', 'SCOOP_BRANCH' -and $name -in $newConfigNames.Keys) {
warn ('Config option "{0}" is deprecated, please use "{1}" instead next time.' -f $name, $newConfigNames.$name)
$name = $newConfigNames.$name
}
# END NOTE
set_config $name $value | Out-Null
Write-Host "'$name' has been set to '$value'"
} else {
# NOTE Scoop config file migration. Remove this after 2023/6/30
if ($name -notin 'SCOOP_REPO', 'SCOOP_BRANCH' -and $name -in $newConfigNames.Keys) {
warn ('Config option "{0}" is deprecated, please use "{1}" instead next time.' -f $name, $newConfigNames.$name)
$name = $newConfigNames.$name
}
# END NOTE
$value = get_config $name
if($null -eq $value) {
Write-Host "'$name' is not set"

View File

@@ -70,7 +70,7 @@ foreach ($curr_app in $apps) {
}
if(!$manifest) {
error "Couldn't find manifest for '$app'$(if($url) { " at the URL $url" })."
error "Couldn't find manifest for '$app'$(if($bucket) { " from '$bucket' bucket" } elseif($url) { " at '$url'" })."
continue
}
$version = $manifest.version

View File

@@ -160,7 +160,7 @@ if ($status.installed) {
$totalPackage = 0
foreach ($url in @(url $manifest (Get-DefaultArchitecture))) {
try {
if (Test-Path (fullpath (cache_path $app $manifest.version $url))) {
if (Test-Path (cache_path $app $manifest.version $url)) {
$cached = " (latest version is cached)"
} else {
$cached = $null

View File

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

View File

@@ -8,6 +8,7 @@
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\system.ps1" # 'env_add_path' (indirectly)
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\shortcuts.ps1"
@@ -79,6 +80,9 @@ $apps | ForEach-Object {
$dir = link_current $dir
create_shims $manifest $dir $global $architecture
create_startmenu_shortcuts $manifest $dir $global $architecture
# unset all potential old env before re-adding
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
# unlink all potential old link before re-persisting

View File

@@ -35,6 +35,7 @@ param($SubCommand)
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\install.ps1" # for rm_shim
. "$PSScriptRoot\..\lib\system.ps1" # 'Add-Path' (indirectly)
if ($SubCommand -notin @('add', 'rm', 'list', 'info', 'alter')) {
if (!$SubCommand) {

View File

@@ -8,7 +8,7 @@
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
# check if scoop needs updating
$currentdir = fullpath $(versiondir 'scoop' 'current')
$currentdir = versiondir 'scoop' 'current'
$needs_update = $false
$bucket_needs_update = $false
$script:network_failure = $false

View File

@@ -8,6 +8,7 @@
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\system.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\shortcuts.ps1"
. "$PSScriptRoot\..\lib\psmodules.ps1"

View File

@@ -16,6 +16,7 @@
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1" # 'save_install_info' in 'manifest.ps1' (indirectly)
. "$PSScriptRoot\..\lib\system.ps1"
. "$PSScriptRoot\..\lib\shortcuts.ps1"
. "$PSScriptRoot\..\lib\psmodules.ps1"
. "$PSScriptRoot\..\lib\decompress.ps1"
@@ -70,7 +71,7 @@ function Sync-Scoop {
if (!(Test-GitAvailable)) { abort "Scoop uses Git to update itself. Run 'scoop install git' and try again." }
Write-Host "Updating Scoop..."
$currentdir = fullpath $(versiondir 'scoop' 'current')
$currentdir = versiondir 'scoop' 'current'
if (!(Test-Path "$currentdir\.git")) {
$newdir = "$currentdir\..\new"
$olddir = "$currentdir\..\old"
@@ -240,6 +241,13 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
Write-Host "Updating '$app' ($old_version -> $version)"
#region Workaround for #2952
if (test_running_process $app $global) {
Write-Host 'Running process detected, skip updating.'
return
}
#endregion Workaround for #2952
# region Workaround
# Workaround for https://github.com/ScoopInstaller/Scoop/issues/2220 until install is refactored
# Remove and replace whole region after proper fix
@@ -254,7 +262,7 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
if ($check_hash) {
$manifest_hash = hash_for_url $manifest $url $architecture
$source = fullpath (cache_path $app $version $url)
$source = cache_path $app $version $url
$ok, $err = check_hash $source $manifest_hash $(show_app $app $bucket)
if (!$ok) {
@@ -280,24 +288,17 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
Invoke-HookScript -HookType 'pre_uninstall' -Manifest $old_manifest -Arch $architecture
#region Workaround for #2952
if (test_running_process $app $global) {
return
}
#endregion Workaround for #2952
Write-Host "Uninstalling '$app' ($old_version)"
run_uninstaller $old_manifest $architecture $dir
rm_shims $app $old_manifest $global $architecture
env_rm_path $old_manifest $dir $global $architecture
env_rm $old_manifest $global $architecture
# If a junction was used during install, that will have been used
# as the reference directory. Otherwise it will just be the version
# directory.
$refdir = unlink_current $dir
uninstall_psmodule $old_manifest $refdir $global
env_rm_path $old_manifest $refdir $global $architecture
env_rm $old_manifest $global $architecture
if ($force -and ($old_version -eq $version)) {
if (!(Test-Path "$dir/../_$version.old")) {

View File

@@ -136,7 +136,7 @@ Function Get-VirusTotalResultByHash ($hash, $url, $app) {
warn "$app`: $unsafe/$total, see $report_url"
}
Default {
warn "`e[31m$app`: $unsafe/$total, see $report_url`e[0m"
warn "$([char]0x1b)[31m$app`: $unsafe/$total, see $report_url$([char]0x1b)[0m"
}
}
$maliciousResults = $vendorResults |

View File

@@ -127,10 +127,6 @@
"installer": {
"$ref": "#/definitions/installer"
},
"msi": {
"$ref": "#/definitions/stringOrArrayOfStrings",
"description": "Deprecated"
},
"post_install": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
@@ -603,10 +599,6 @@
"license": {
"$ref": "#/definitions/license"
},
"msi": {
"$ref": "#/definitions/stringOrArrayOfStrings",
"description": "Deprecated"
},
"notes": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},

View File

@@ -1 +0,0 @@
packages/

View File

@@ -1 +0,0 @@
9726c3a429009a5b22bd92cb8ab96724c670e164e7240e83f27b7c8b7bd1ca39 *shim.exe

View File

@@ -1 +0,0 @@
18a737674afde4d5e7e1647d8d1e98471bb260513c57739651f92fdf1647d76c92f0cd0a9bb458daf4eae4bdab9d31404162acf6d74a041e6415752b75d722e0 *shim.exe

Binary file not shown.

View File

@@ -1,22 +0,0 @@
Param([Switch]$Fast)
Push-Location $PSScriptRoot
. "$PSScriptRoot\..\..\lib\core.ps1"
. "$PSScriptRoot\..\..\lib\install.ps1"
if (!$Fast) {
Write-Host "Install dependencies ..."
& "$PSScriptRoot\install.ps1"
}
$output = "$PSScriptRoot\bin"
Write-Output 'Compiling shim.cs ...'
& "$PSScriptRoot\packages\Microsoft.Net.Compilers.Toolset\tasks\net472\csc.exe" -deterministic -platform:anycpu -nologo -optimize -target:exe -out:"$output\shim.exe" shim.cs
Write-Output 'Computing checksums ...'
Remove-Item "$PSScriptRoot\bin\checksum.sha256" -ErrorAction Ignore
Remove-Item "$PSScriptRoot\bin\checksum.sha512" -ErrorAction Ignore
Get-ChildItem "$PSScriptRoot\bin\*" -Include *.exe, *.dll | ForEach-Object {
"$((Get-FileHash -Path $_ -Algorithm SHA256).Hash.ToLower()) *$($_.Name)" | Out-File "$PSScriptRoot\bin\checksum.sha256" -Append -Encoding oem
"$((Get-FileHash -Path $_ -Algorithm SHA512).Hash.ToLower()) *$($_.Name)" | Out-File "$PSScriptRoot\bin\checksum.sha512" -Append -Encoding oem
}
Pop-Location

View File

@@ -1,8 +0,0 @@
# https://github.com/edymtt/nugetstandalone
$destinationFolder = "$PSScriptRoot\packages"
if ((Test-Path -Path $destinationFolder)) {
Remove-Item -Path $destinationFolder -Recurse | Out-Null
}
New-Item $destinationFolder -Type Directory | Out-Null
nuget install packages.config -o $destinationFolder -ExcludeVersion

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Net.Compilers.Toolset" version="4.2.0" targetFramework="net45" developmentDependency="true" />
</packages>

View File

@@ -1,168 +0,0 @@
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace Scoop {
class Program {
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
static extern bool CreateProcess(string lpApplicationName,
string lpCommandLine, IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes, bool bInheritHandles,
uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
const int ERROR_ELEVATION_REQUIRED = 740;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFO {
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION {
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[DllImport("kernel32.dll", SetLastError=true)]
static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
const UInt32 INFINITE = 0xFFFFFFFF;
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode);
static int Main(string[] args) {
var exe = Assembly.GetExecutingAssembly().Location;
var dir = Path.GetDirectoryName(exe);
var name = Path.GetFileNameWithoutExtension(exe);
var configPath = Path.Combine(dir, name + ".shim");
if(!File.Exists(configPath)) {
Console.Error.WriteLine("Couldn't find " + Path.GetFileName(configPath) + " in " + dir);
return 1;
}
var config = Config(configPath);
var path = Get(config, "path");
var add_args = Get(config, "args");
var si = new STARTUPINFO();
var pi = new PROCESS_INFORMATION();
// create command line
var cmd_args = add_args ?? "";
var pass_args = GetArgs(Environment.CommandLine);
if(!string.IsNullOrEmpty(pass_args)) {
if(!string.IsNullOrEmpty(cmd_args)) cmd_args += " ";
cmd_args += pass_args;
}
if(!string.IsNullOrEmpty(cmd_args)) cmd_args = " " + cmd_args;
var cmd = "\"" + path + "\"" + cmd_args;
if(!CreateProcess(null, cmd, IntPtr.Zero, IntPtr.Zero,
bInheritHandles: true,
dwCreationFlags: 0,
lpEnvironment: IntPtr.Zero, // inherit parent
lpCurrentDirectory: null, // inherit parent
lpStartupInfo: ref si,
lpProcessInformation: out pi)) {
var error = Marshal.GetLastWin32Error();
if(error == ERROR_ELEVATION_REQUIRED) {
// Unfortunately, ShellExecute() does not allow us to run program without
// CREATE_NEW_CONSOLE, so we can not replace CreateProcess() completely.
// The good news is we are okay with CREATE_NEW_CONSOLE when we run program with elevation.
Process process = new Process();
process.StartInfo = new ProcessStartInfo(path, cmd_args);
process.StartInfo.UseShellExecute = true;
try {
process.Start();
}
catch(Win32Exception exception) {
return exception.ErrorCode;
}
process.WaitForExit();
return process.ExitCode;
}
return error;
}
WaitForSingleObject(pi.hProcess, INFINITE);
uint exit_code = 0;
GetExitCodeProcess(pi.hProcess, out exit_code);
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return (int)exit_code;
}
// now uses GetArgs instead
static string Serialize(string[] args) {
return string.Join(" ", args.Select(a => a.Contains(' ') ? '"' + a + '"' : a));
}
// strips the program name from the command line, returns just the arguments
static string GetArgs(string cmdLine) {
if(cmdLine.StartsWith("\"")) {
var endQuote = cmdLine.IndexOf("\" ", 1);
if(endQuote < 0) return "";
return cmdLine.Substring(endQuote + 1);
}
var space = cmdLine.IndexOf(' ');
if(space < 0 || space == cmdLine.Length - 1) return "";
return cmdLine.Substring(space + 1);
}
static string Get(Dictionary<string, string> dic, string key) {
string value = null;
dic.TryGetValue(key, out value);
return value;
}
static Dictionary<string, string> Config(string path) {
var config = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach(var line in File.ReadAllLines(path)) {
var m = Regex.Match(line, @"([^=]+)=(.*)");
if(m.Success) {
config[m.Groups[1].Value.Trim()] = m.Groups[2].Value.Trim();
}
}
return config;
}
}
}

View File

@@ -1,36 +0,0 @@
<?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.2.0\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{381F9D2E-2355-4F84-9206-06BB9175F97B}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>Scoop.Shim</RootNamespace>
<AssemblyName>Scoop.Shim</AssemblyName>
<TargetFrameworkVersion>v4.5.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="shim.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props'))" />
</Target>
</Project>

View File

@@ -1,10 +0,0 @@
# https://github.com/edymtt/nugetstandalone
$destinationFolder = "$PSScriptRoot\packages"
if (!(Test-Path -Path $destinationFolder)) {
Write-Host -f Red "Run .\install.ps1 first!"
exit 1
}
nuget update packages.config -r $destinationFolder
Remove-Item $destinationFolder -Force -Recurse | Out-Null
nuget install packages.config -o $destinationFolder -ExcludeVersion

View File

@@ -1,2 +0,0 @@
*.zip
*.bak

View File

@@ -1,52 +0,0 @@
VER?=2.2.1
ZIP=shimexe.zip
URL?=https://github.com/kiennq/scoop-better-shimexe/releases/download/$(VER)/$(ZIP)
LATEST_URL?=https://github.com/kiennq/scoop-better-shimexe/releases/latest
NEWVER=$(shell cat version.txt)
all: verify ## make download unzip verify
version.txt:
@curl --max-redirs 0 -s -D - -o /dev/null $(LATEST_URL) | grep -i ^location | sed -E -e "s|.*/([^/]+)$$|\1|" >version.txt
@printf "%s " "Latest version is:"
@cat version.txt
check: version.txt ## Check the version number in version.txt and update if needed
bump: check ## Bump version number in Makefile
@rm -f Makefile.bak
@sed -i.bak -e 's|=$(VER)|=$(NEWVER)|' Makefile
@cmp --quiet Makefile{,.bak} || echo "Makefile bumped from $(VER) to $(NEWVER)"
$(ZIP): version.txt
curl -L -s -o $(ZIP) $(URL)
@touch $@
download: $(ZIP) ## Download shim from https://github.com/kiennq/scoop-better-shimexe
shim.exe: $(ZIP)
unzip -z -j -o $(ZIP)
@touch $@
unzip: shim.exe ## Unzip download
verify: shim.exe ## Verify SHA256 checksum for shim.exe
sed -e "s|bin/||" checksum.sha256 | sha256sum -c
clean: ## Clean .zip files
rm -f *.zip
help: ## Display help text
@printf "%-8s %s\n" Target Description
@printf "%-8s %s\n" '--------' '------------------------------------------'
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "%-8s %s\n", $$1, $$2}'
.PHONY: all
.PHONY: bump
.PHONY: check
.PHONY: clean
.PHONY: download
.PHONY: help
.PHONY: unzip
.PHONY: verify

View File

@@ -1 +1 @@
aa685053f4a5c0e7145f2a27514c8a56ceae25b0824062326f04037937caa558 bin/shim.exe
410f84fe347cf55f92861ea3899d30b2d84a8bbc56bb3451d74697a4a0610b25 *shim.exe

View File

@@ -1 +1 @@
67c605c8163869d8ef8153c64eb09b82645cbae8228928c0fef944d0259a7b2d3791ecf4b4b01e23566916a878ee7977bfc1a59846bccf3c63bd6a1cf4f521b5 bin/shim.exe
9ce94adf48f7a31ab5773465582728c39db6f11a560fc43316fe6c1ad0a7b69a76aa3f9b52bb6b2e3be8043e4920985c8ca0bf157be9bf1e4a5a4d7c4ed195ba *shim.exe

Binary file not shown.

View File

@@ -1 +1 @@
2.2.1
v3.1.1

View File

@@ -0,0 +1 @@
0116068768fc992fc536738396b33db3dafe6b0cf0e6f54f6d1aa8b0331f3cec *shim.exe

View File

@@ -0,0 +1 @@
d734c528e9f20581ed3c7aa71a458f7dff7e2780fa0c319ccb9c813cd8dbf656bd7e550b81d2aa3ee8775bff9a4e507bc0b25f075697405adca0f47d37835848 *shim.exe

Binary file not shown.

View File

@@ -0,0 +1 @@
1.1.0

View File

@@ -1,4 +1,4 @@
b624949df8b0e3a6153fdfb730a7c6f4990b6592ee0d922e1788433d276610f3 *Newtonsoft.Json.dll
9abb57d73d82a2d77008321a85aff2b62e5ac68bebb54ece8668c96cc112e36b *Newtonsoft.Json.Schema.dll
0318c8221ce4d44806f8def619bcc02886be0902aab80080e6251c50c6ca53a9 *Scoop.Validator.dll
40a70bee96d108701f8f2e81392f9b79fd003f1cb4e1653ad2429753153fd7ee *validator.exe
e1e27af7b07eeedf5ce71a9255f0422816a6fc5849a483c6714e1b472044fa9d *Newtonsoft.Json.dll
9f1a8f06c284a4ee01f704d89003ddc7061846f2008094071e9adf08267849f9 *Newtonsoft.Json.Schema.dll
d11b660612ce821ec03772b73aa3b8884a0479275c70085c7e143913a41a2d28 *Scoop.Validator.dll
87f8f8db2202a3fbef6f431d0b7e20cec9d32095c441927402041f3c4076c1b6 *validator.exe

View File

@@ -1,4 +1,4 @@
2fdf035661f349206f58ea1feed8805b7f9517a21f9c113e7301c69de160f184c774350a12a710046e3ff6baa37345d319b6f47fd24fbba4e042d54014bee511 *Newtonsoft.Json.dll
855ab2e30c9d523c9f321ae861c5969244185f660fa47e05cec96df8e2970d19843dbd3d89a0fca845544641915d1adf4b4a2145ef568dd99da7791e5064d70e *Newtonsoft.Json.Schema.dll
338793e6127330c0b05728291fcf18441127ffb56e1bd5c0f0588cd7436605f4b852f4bb622f655896a7eb7b1262add142b200fd5f37391b47d1401becb6b81c *Scoop.Validator.dll
d497c27b48f44f4cff270d3c8801b0cecc74108f8786a4a7c40e57541308ae33a69f5456cfc43ae1ce4214038d20da9fbeac1bcf76cc58d972863b58dab18401 *validator.exe
56eb7f070929b239642dab729537dde2c2287bdb852ad9e80b5358c74b14bc2b2dded910d0e3b6304ea27eb587e5f19db0a92e1cbae6a70fb20b4ef05057e4ac *Newtonsoft.Json.dll
551e772fe2ee72b349d5c4ed5d5f8d8957d50cfcbbde7af5d5740d9652bcad626a2c00bc0d9223db7c874962187a90f9160397f243eadee1c594585ba2b155e0 *Newtonsoft.Json.Schema.dll
0a31d192c82bbd8ce50fb75dd5fe813c98bb870d54c112c600ae2e2436063cb2bd94bb206675dfe31ce89922e9a04a3d520ed579ab7198835190b67a6321a74e *Scoop.Validator.dll
58a0c37e98cac17822c7756bf6686a5fb74e711b8d986d13bd2f689f6b3b1f485fcd908d92cbc6a162a0e5974c2c5a43de57d15f1996be0aa405e41ec2ec8393 *validator.exe

View File

@@ -4,7 +4,7 @@ Push-Location $PSScriptRoot
. "$PSScriptRoot\..\..\lib\install.ps1"
if (!$Fast) {
Write-Host "Install dependencies ..."
Write-Host 'Install dependencies ...'
& "$PSScriptRoot\install.ps1"
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net45" />
<package id="Newtonsoft.Json.Schema" version="3.0.15-beta2" targetFramework="net45" />
<package id="Microsoft.Net.Compilers.Toolset" version="4.2.0" targetFramework="net45" developmentDependency="true" />
<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" />
</packages>

View File

@@ -1,7 +1,10 @@
<?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.2.0\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<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')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -14,12 +17,15 @@
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<HintPath>packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Reference
Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<HintPath>packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<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-beta2\lib\net45\Newtonsoft.Json.Schema.dll</HintPath>
<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>
<Private>True</Private>
</Reference>
<Reference Include="System" />
@@ -41,8 +47,12 @@
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
<ErrorText>This project references NuGet package(s) that are missing on this computer.
Enable NuGet Package Restore to download them. For more information, see
http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props'))" />
<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'))" />
</Target>
</Project>

View File

@@ -7,8 +7,7 @@ BeforeDiscovery {
'[\\/]\.git[\\/]',
'\.sublime-workspace$',
'\.DS_Store$',
'supporting(\\|/)validator(\\|/)packages(\\|/)*',
'supporting(\\|/)shimexe(\\|/)packages(\\|/)*'
'supporting(\\|/)validator(\\|/)packages(\\|/)*'
)
$repo_files = (Get-ChildItem $TestPath -File -Recurse).FullName |
Where-Object { $_ -inotmatch $($project_file_exclusions -join '|') }

View File

@@ -8,7 +8,7 @@ BeforeAll {
Describe 'Manipulate Alias' -Tag 'Scoop' {
BeforeAll {
Mock shimdir { "$TestDrive\shims" }
Mock set_config { }
Mock set_config {}
Mock get_config { @{} }
$shimdir = shimdir
@@ -23,23 +23,24 @@ Describe 'Manipulate Alias' -Tag 'Scoop' {
& $alias_file | Should -Be 'hello, world!'
}
It 'Does not change existing alias if alias exists' {
It 'Does not change existing file if its filename same as alias name' {
$alias_file = "$shimdir\scoop-rm.ps1"
Mock abort {}
New-Item $alias_file -Type File -Force
$alias_file | Should -Exist
add_alias 'rm' 'test'
& $alias_file | Should -Not -Be 'test'
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"
add_alias 'rm' '"hello, world!"'
$alias_file | Should -Exist
Mock get_config { @(@{'rm' = 'scoop-rm' }) }
Mock info {}
rm_alias 'rm'
$alias_file | Should -Not -Exist
Should -Invoke -CommandName info -Times 1 -ParameterFilter { $msg -eq "Removing alias 'rm'..." }
}
}

View File

@@ -1,6 +1,7 @@
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\system.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
}
@@ -167,7 +168,7 @@ Describe 'shim' -Tag 'Scoop', 'Windows' {
BeforeAll {
$working_dir = setup_working 'shim'
$shimdir = shimdir
$(ensure_in_path $shimdir) | Out-Null
Add-Path $shimdir
}
It "links a file onto the user's path" {
@@ -201,7 +202,7 @@ Describe 'rm_shim' -Tag 'Scoop', 'Windows' {
BeforeAll {
$working_dir = setup_working 'shim'
$shimdir = shimdir
$(ensure_in_path $shimdir) | Out-Null
Add-Path $shimdir
}
It 'removes shim from path' {
@@ -220,7 +221,7 @@ Describe 'get_app_name_from_shim' -Tag 'Scoop', 'Windows' {
BeforeAll {
$working_dir = setup_working 'shim'
$shimdir = shimdir
$(ensure_in_path $shimdir) | Out-Null
Add-Path $shimdir
Mock appsdir { $working_dir }
}
@@ -258,36 +259,6 @@ Describe 'get_app_name_from_shim' -Tag 'Scoop', 'Windows' {
}
}
Describe 'ensure_robocopy_in_path' -Tag 'Scoop', 'Windows' {
BeforeAll {
$shimdir = shimdir $false
Mock versiondir { "$PSScriptRoot\.." }
}
It 'shims robocopy when not on path' {
Mock Test-CommandAvailable { $false }
Test-CommandAvailable robocopy | Should -Be $false
ensure_robocopy_in_path
# "$shimdir/robocopy.ps1" | should -exist
"$shimdir/robocopy.exe" | Should -Exist
# clean up
rm_shim robocopy $(shimdir $false) | Out-Null
}
It 'does not shim robocopy when it is in path' {
Mock Test-CommandAvailable { $true }
Test-CommandAvailable robocopy | Should -Be $true
ensure_robocopy_in_path
# "$shimdir/robocopy.ps1" | should -not -exist
"$shimdir/robocopy.exe" | Should -Not -Exist
}
}
Describe 'sanitary_path' -Tag 'Scoop' {
It 'removes invalid path characters from a string' {
$path = 'test?.json'

View File

@@ -158,11 +158,13 @@ Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' {
} elseif (!(installed lessmsi)) {
scoop install lessmsi
}
Copy-Item "$working_dir\MSITest.msi" "$working_dir\MSI Test.msi"
$test1 = "$working_dir\MSITest.msi"
$test2 = "$working_dir\MSITestNull.msi"
$test2 = "$working_dir\MSI Test.msi"
$test3 = "$working_dir\MSITestNull.msi"
}
It 'extract normal MSI file' {
It 'extract normal MSI file using msiexec' {
Mock get_config { $false }
$to = test_extract 'Expand-MsiArchive' $test1
$to | Should -Exist
@@ -170,12 +172,32 @@ Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' {
(Get-ChildItem "$to\MSITest").Count | Should -Be 1
}
It 'extract empty MSI file using lessmsi' {
It 'extract normal MSI file with whitespace in path using msiexec' {
Mock get_config { $false }
$to = test_extract 'Expand-MsiArchive' $test2
$to | Should -Exist
"$to\MSITest\empty" | Should -Exist
(Get-ChildItem "$to\MSITest").Count | Should -Be 1
}
It 'extract normal MSI file using lessmsi' {
Mock get_config { $true }
$to = test_extract 'Expand-MsiArchive' $test1
$to | Should -Exist
}
It 'extract normal MSI file with whitespace in path using lessmsi' {
Mock get_config { $true }
$to = test_extract 'Expand-MsiArchive' $test2
$to | Should -Exist
}
It 'extract empty MSI file using lessmsi' {
Mock get_config { $true }
$to = test_extract 'Expand-MsiArchive' $test3
$to | Should -Exist
}
It 'works with "-Removal" switch ($removal param)' {
Mock get_config { $false }
$test1 | Should -Exist

View File

@@ -1,6 +1,7 @@
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\system.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
}
@@ -37,8 +38,6 @@ Describe 'is_in_dir' -Tag 'Scoop', 'Windows' {
It 'should work correctly' {
is_in_dir 'C:\test' 'C:\foo' | Should -BeFalse
is_in_dir 'C:\test' 'C:\test\foo\baz.zip' | Should -BeTrue
is_in_dir 'test' "$PSScriptRoot" | Should -BeTrue
is_in_dir "$PSScriptRoot\..\" "$PSScriptRoot" | Should -BeFalse
}
}
@@ -47,27 +46,28 @@ Describe 'env add and remove path' -Tag 'Scoop', 'Windows' {
BeforeAll {
# test data
$manifest = @{
'env_add_path' = @('foo', 'bar')
'env_add_path' = @('foo', 'bar', '.', '..')
}
$testdir = Join-Path $PSScriptRoot 'path-test-directory'
$global = $false
# store the original path to prevent leakage of tests
$origPath = $env:PATH
}
It 'should concat the correct path' {
Mock add_first_in_path {}
Mock remove_from_path {}
Mock Add-Path {}
Mock Remove-Path {}
# adding
env_add_path $manifest $testdir $global
Assert-MockCalled add_first_in_path -Times 1 -ParameterFilter { $dir -like "$testdir\foo" }
Assert-MockCalled add_first_in_path -Times 1 -ParameterFilter { $dir -like "$testdir\bar" }
Should -Invoke -CommandName Add-Path -Times 1 -ParameterFilter { $Path -like "$testdir\foo" }
Should -Invoke -CommandName Add-Path -Times 1 -ParameterFilter { $Path -like "$testdir\bar" }
Should -Invoke -CommandName Add-Path -Times 1 -ParameterFilter { $Path -like $testdir }
Should -Invoke -CommandName Add-Path -Times 0 -ParameterFilter { $Path -like $PSScriptRoot }
env_rm_path $manifest $testdir $global
Assert-MockCalled remove_from_path -Times 1 -ParameterFilter { $dir -like "$testdir\foo" }
Assert-MockCalled remove_from_path -Times 1 -ParameterFilter { $dir -like "$testdir\bar" }
Should -Invoke -CommandName Remove-Path -Times 1 -ParameterFilter { $Path -like "$testdir\foo" }
Should -Invoke -CommandName Remove-Path -Times 1 -ParameterFilter { $Path -like "$testdir\bar" }
Should -Invoke -CommandName Remove-Path -Times 1 -ParameterFilter { $Path -like $testdir }
Should -Invoke -CommandName Remove-Path -Times 0 -ParameterFilter { $Path -like $PSScriptRoot }
}
}

View File

@@ -99,7 +99,7 @@ Describe 'versions comparison' -Tag 'Scoop' {
Compare-Version 'nightly-20190801' 'nightly-20200801' | Should -Be 0
}
It 'handles nightly versions with `update_nightly`' {
It "handles nightly versions with 'update_nightly'" {
function get_config { $true }
Mock Get-Date { '20200801' }
Compare-Version 'nightly-20200801' 'nightly' | Should -Be 0