102 Commits

Author SHA1 Message Date
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
Rashil Gandhi
fb3169629f fix(scoop-checkup): Don't throw 7zip error when external 7zip is used (#5703)
* fix(scoop-checkup): Don't throw 7zip error when external 7zip is used

* Update CHANGELOG.md
2023-10-26 14:13:25 +05:30
Bill ZHANG
7e81e49152 fix(scoop-reset): Don't abort when multiple apps are passed and an app is running (#5687)
* fix(scoop-reset): change #2952 fix to be the same with scoop-install

* update changelog

* Update CHANGELOG.md

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-26 12:58:10 +05:30
Hagai Gold
7b35e19d4c fix(perf): Do not call scoop externally from inside the code (#5695)
* fix: do not call `scoop` externally from inside the code

* update CHANGELOG.md

* update CHANGELOG.md
2023-10-19 13:19:31 +05:30
Sp1d3R
6cdcc75ad8 fix(shim): Fix a minor issue with Get-Subsystem (#5684)
* Fix PE read error and refractor

* refactor Change-Subsystem -> Set-Subsystem and additional `catch` block

* refactor Change-Subsystem -> Set-Subsystem and additional `catch` block

* add a return value to `Set-PESubsystem`

* fix trailing whitespace
2023-10-14 16:04:46 +05:30
L. Yeung
14b38b4092 fix(scoop-shim): Check literal path in Get-ShimPath (#5680)
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-10 16:50:53 +05:30
L. Yeung
2847e0a37c fix(scoop-shim): Avoid unexpected output of list subcommand (#5681)
* fix(shim): Avoid unexpected output of `list` subcommand

* Update libexec/scoop-shim.ps1

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>

---------

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2023-10-10 16:47:46 +05:30
Dustin
3a3f41c556 fix(shim): Fix shim adding bug related to Resolve-Path(#5493)
* fixed shim add bug related to #5492

* Updated CHANGLOG.md per contributor guidelines
2023-10-08 19:04:36 +05:30
Rashil Gandhi
15f9bbec97 fix(update): Change error message to a better instruction (#5677)
* fix(update): Change error message to a better instruction

* Update CHANGELOG.md
2023-10-06 14:51:45 +05:30
Gerardo Grignoli
ab34b7fb61 feat(core): Allow global install of PowerShell modules (#5611)
* Allow global install of PowerShell modules to $env:ProgramFiles\WindowsPowerShell\Modules

* tabs vs spaces

* Updated CHANGELOG.md

* Changed global install of PowerShell modules to $globaldir\Modules
2023-10-05 16:40:03 +05:30
HUMORCE
863af42d4e fix(core): Fix detection of Git (#5545)
* fix(core): Fix `Test-GitAvailable`

* fix(core): Fallback git(32bit) to `Get-HelperPath`

* check value of `$internalgit` also

* changlog

* path already tested when invoke `Get-AppFilePAth`
2023-10-05 12:21:29 +05:30
Richard Kuhnt
b3c05e71fa perf(scoop-search): Improve performance for local search (#5644)
* perf(search): improve local search performance

* Update libexec/scoop-search.ps1

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>

* Update libexec/scoop-search.ps1

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>

* Update libexec/scoop-search.ps1

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>

* Update libexec/scoop-search.ps1

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>

* Update libexec/scoop-search.ps1

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>

* Update libexec/scoop-search.ps1

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>

* Added [JsonDocument]::Parse for testing

* Fix array length check

* Used wrong function

* Add fallback function for PowerShell 5

* Check for System.Text.Json in Assemblies instead

* Show help output

* Revert "Show help output"

This reverts commit d3d6b01d08.

* Update CHANGELOG.md

---------

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 19:35:45 +05:30
HUMORCE
acc271d115 fix(scoop-(un)hold): Correct output the messages when manifest not found, (already|not) held (#5554)
* fix(scoop-(un)hold): Abort when manifest not found

* abort when already held or not held

* use continue

* CHANGELOG

* Update CHANGELOG.md

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 19:27:01 +05:30
HUMORCE
6d79d62cc8 fix(core): Handle scoop aliases and broken (edited,copied) shim (#5551)
* fix(core): Avoid error messages when shim broked, Allow get path for scoop aliases

* update `scoop-which`

* Add CHANGELOG entry

* Update CHANGELOG.md

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 18:59:10 +05:30
HUMORCE
00c92b04d6 fix(install): Avoid error when unlinking non-existent junction/hardlink (#5552)
* fix(install): Avoid error when unlinking non-existent junction / hardlink

* CHANGELOG

* Update CHANGELOG.md

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 18:58:02 +05:30
HUMORCE
becc7a7b76 fix(core): Avoid error messages when deleting non-existent environment variable (#5547)
* fix(core): Check if the envrionment variable exists before delete

* CHANGELOG

* Use `try, catch`, value of env variable may is null/empty

* UPDATE CHANGELOG

* Brackets

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 16:29:44 +05:30
HUMORCE
6898773a8d fix(core): Use relative path as fallback of $scoopdir (#5544)
* fix(core): Use relative path as fallback of `$scoopdir`(Scoop root directory)

* typo

* changelog

* re

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 16:23:00 +05:30
Sp1d3R
353137f0a9 fix(shim): Remove console window for GUI apps (#5559)
* Change shim subsystem to prevent console window for GUI apps.

* Removed redundant log

* Update core.ps1

* Fixes file access rights and the log message

* update changelog

* Update CHANGELOG.md (PR Number)

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>

---------

Co-authored-by: Quasar <suhas.nandibhatla@gmail.com>
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-03 00:09:48 +05:30
Rashil Gandhi
43579714cc fix(env): Publish environment change when deleting variable (#5631)
* fix(env): Publish environment change when deleting variable

* Update CHANGELOG.md
2023-10-02 20:59:50 +05:30
walpo
aa09601503 docs(readme): Improve documentation language (#5638)
* readme.md: refactor HTML blocks

* readme.md: enhance style and fix typos and more

* readme.md: improve composition

* Update README.md

It seems GitHub does not work well with the 'style' attribute, so the deprecated attribute 'align' cannot be replaced.

* readme.md: update writing

* Update README.md

* Update README.md

* Update CHANGELOG.md

---------

Co-authored-by: walpo <3212100-walpo@users.noreply.gitlab.com>
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-10-02 14:36:06 +05:30
HUMORCE
6a35a22b0b fix(scoop-checkup): Change the message level of helpers from ERROR to WARN (#5614)
* fix(scoop-checkup): Downgrade the message level of helpers from ERROR to WARN

* update CHANGELOG

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-09-11 13:50:07 +05:30
Bill ZHANG
0b4919ca32 fix(scoop-virustotal): Continue execution when no app but -a flag is provided (#5593)
* fix(scoop-virustotal): continue exec when no app but -a provided

* update CHANGELOG.md

* CHANGELOG: fix typo

---------

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-09-11 00:28:53 +05:30
Bill ZHANG
efdd6dd7ca docs(CHANGELOG): Add missing brackets for the links (#5596)
add missing brackets for the links
2023-08-11 18:46:58 +05:30
HUMORCE
3dfb4bfd97 fix(buckets): Avoid error messages for unexpected dir (#5549) 2023-06-30 00:18:19 +08:00
HUMORCE
1d140585a4 fix(scoop-checkup): Skip defender check in Windows Sandbox (#5519) 2023-05-30 13:46:05 +08:00
0x574859
52059ca1ac fix(env): Apply env immediately by SendMessageTimeout (#5452) 2023-04-07 13:05:46 +08:00
HUMORCE
cddc52e03b docs(scoop-info): Fix help message (#5445) 2023-03-27 14:37:10 +08:00
Richard Kuhnt
682a1e2c07 fix(git): set HTTP(S)_PROXY only in process scope to prevent race condition (#5436) 2023-03-20 23:52:41 +08:00
Hsiao-nan Cheung
2accaae5d1 fix(core): Rewrite config file when needed (#5439) 2023-03-20 22:44:55 +08:00
Rashil Gandhi
a20bb4f1a6 fix(shim): Use bash executable directly (#5433)
* fix(shim): Use bash executable directly

* Update CHANGELOG.md
2023-03-16 20:09:18 +05:30
Ross Smith II
ad0f6178d0 feat(bucket): Switch nirsoft bucket to ScoopInstaller/Nirsoft (#5328) 2023-03-11 21:42:14 +08:00
L. Yeung
41620bb169 feat(core): Add -Silent switch for Invoke-ExternalCommand (#5346) 2023-03-11 21:39:45 +08:00
Hsiao-nan Cheung
559c6f9e64 feat(bucket): Make official buckets higher priority (#5398) 2023-03-10 17:24:41 +08:00
Richard Kuhnt
7826d6fe2d perf(decompress): disable progress bar to improve Expand-Archive performance (#5410)
See: https://github.com/ScoopInstaller/Install/pull/42

---------

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2023-02-27 20:11:40 +01:00
Stephen Albert-Moore
8acfeeefcf fix(scoop-info) --verbose file size collection (#5352) 2023-02-26 20:19:54 +01:00
Valinor
c00dd42cae fix(getopt): Stop split arguments in getopt() and ensure array by explicit arguments type (#5326)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2023-02-26 14:32:16 +08:00
Hsiao-nan Cheung
3f11454a3c fix(core): Fix scripts' calling parameters (#5365) 2023-02-26 14:31:38 +08:00
0x574859
0a39de86e2 fix(env): Avoid automatic expansion of %% in env (#5395)
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2023-02-25 20:38:09 +08:00
Chawye Hsu
c44e214743 feat(config): Support portable config file (#5369) 2023-02-25 20:22:56 +08:00
Jeppe Frandsen
7c6aeb240e fix(install): Fix downloading release assets from private GitHub repositories (#5361)
Fix download from private GitHub repos
2023-02-25 14:00:37 +05:30
Hsiao-nan Cheung
32ca856f63 fix(core): Fix is_in_dir under Unix (#5391) 2023-02-19 23:56:53 +08:00
Ercolino
1d0bd434ab builds(checkver): Read the private_host config variable (#5381) 2023-02-19 17:19:01 +08:00
Ross Smith II
54e3613fca ci(dependabot): Add dependabot.yml for GitHub Actions (#5377) 2023-02-18 22:50:08 +08:00
Rashil Gandhi
e2558ace75 fix(chore): Handle spaces in git command arguments (#5375)
* fix(chore): Handle spaces in git command arguments

* changelog

* Use splatting
2023-02-07 04:55:54 +05:30
HUMORCE
68760de1e8 fix(shortcuts): Output correctly formatted path (#5333) 2023-01-09 11:01:40 +08:00
Hsiao-nan Cheung
257304bbc7 fix(decompress): Exclude '*.nsis' that may cause error (#5294) 2022-12-18 23:22:41 +08:00
Hsiao-nan Cheung
52f9ce3a81 fix(autoupdate): Fix file hash extraction (#5295) 2022-12-18 23:15:03 +08:00
HUMORCE
6369ba60ba refactor(scoop-download): Output more detailed manifest information (#5277) 2022-12-10 23:38:30 +08:00
Hsiao-nan Cheung
af5ffcddab test(bucket): Skip manifest validation if no manifest changes (#5270) 2022-11-30 13:32:35 +08:00
Richard Kuhnt
360daa706a feat(chore): Improve git.exe execution and add parallel bucket updates (#5122) 2022-11-23 13:58:51 +08:00
Hsiao-nan Cheung
f93028001f chore(release): Bump to version 0.3.1 (#5247) 2022-11-15 10:50:18 +08:00
Hsiao-nan Cheung
c60df9cdad docs(changelog): Prepare for version 0.3.1 (#5248) 2022-11-12 23:41:12 +08:00
Hsiao-nan Cheung
ac71c6e1b7 feat(sysinternals): Add 'sysinternals' bucket to known (#5237) 2022-11-11 01:11:54 +08:00
Hsiao-nan Cheung
0f795733d8 refactor(unix): Remove unix.ps1 (#5235) 2022-11-11 01:10:52 +08:00
Hsiao-nan Cheung
29ed3cb050 builds(checkhashes): Use correct version number if UseCache (#5240) 2022-11-10 14:38:29 +08:00
Hsiao-nan Cheung
d7bfe52122 fix(scoop-config): Output [DateTime] as [String] (#5232) 2022-10-31 20:33:54 +08:00
Chawye Hsu
29e5898a45 feat(checkup): Add Windows Developer Mode check (#5233) 2022-10-31 11:24:40 +08:00
Hsiao-nan Cheung
ea6c73880a tests(bucket): Use BuildHelpers EnvVars (#5226) 2022-10-28 15:20:36 +08:00
Hsiao-nan Cheung
e4f9734b88 fix(shim): Exit if shim creating failed 'cause no git (#5225) 2022-10-28 13:17:53 +08:00
Hsiao-nan Cheung
1630e5f908 tests(pester): Update to Pester 5 (#5222) 2022-10-28 13:14:29 +08:00
Hsiao-nan Cheung
2474ab73e4 builds(schema): Add 'installer' and 'shortcuts' to 'autoupdate' (#5220) 2022-10-25 23:51:36 +08:00
Hsiao-nan Cheung
d65fee6d26 docs(scoop-cat): Fix help message (#5224) 2022-10-25 23:50:46 +08:00
Hsiao-nan Cheung
01fe9ccd63 fix(import): Fix scoop import command (#5210) 2022-10-24 04:06:31 +08:00
Hsiao-nan Cheung
1c6ab39e90 ci: Update modules version (#5209) 2022-10-20 09:33:56 +08:00
pynappo
c71376e12c feat(config): Allow scoop to check and update 'nightly' apps (#5166)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-10-18 23:46:13 +08:00
Amadeus
c9dc41e7c4 docs(config): Copyedit config description (#5197)
Copyedit config description
2022-10-18 01:31:05 +05:30
Hsiao-nan Cheung
5e6a9eeaa0 builds(pssa): Remove unused 'ExcludeRules' (#5201) 2022-10-17 22:41:32 +08:00
Hsiao-nan Cheung
b308769b14 (chore): Sync with 'master' 2022-10-15 23:24:39 +08:00
Hsiao-nan Cheung
1eea29b0d1 fix(decompress): Trim ending '/' (#5195) 2022-10-15 23:19:16 +08:00
Hsiao-nan Cheung
7dcb7c0030 fix(tests): Fix tests in Linux and macOS (#5179) 2022-10-15 23:12:52 +08:00
Hsiao-nan Cheung
9fda5428ae builds(checkver): Support XML default namespace (#5191) 2022-10-15 20:17:48 +08:00
Hsiao-nan Cheung
d0cbc36a58 fix(hash): Fix SF hash extraction (#5189) 2022-10-14 18:21:52 +08:00
yi_Xu
512ab44029 builds(auto-pr): Add CommitMessageFormat option (#5171) 2022-10-14 16:48:39 +08:00
Chawye Hsu
1f0f687a39 chore(installer): Drop the old installer (#5186)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-10-14 14:56:05 +08:00
Hsiao-nan Cheung
ec8161df6c fix(decompress): Use PS's default 'Expand-Archive()' (#5185) 2022-10-14 14:29:24 +08:00
Hsiao-nan Cheung
24301ac028 refactor(hash): Use 'Get-FileHash()' directly (#5177) 2022-10-13 19:15:32 +08:00
93 changed files with 2100 additions and 2097 deletions

8
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
---
# ~/.github/dependabot.yml
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/" # == /.github/workflows/
schedule:
interval: "daily"

View File

@@ -10,13 +10,13 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@main
with:
fetch-depth: 2
- name: Init Test Suite
uses: potatoqualitee/psmodulecache@v4
uses: potatoqualitee/psmodulecache@main
with:
modules-to-cache: PSScriptAnalyzer, BuildHelpers, Pester:4.10.1
modules-to-cache: BuildHelpers
shell: powershell
- name: Test Scoop Core
shell: powershell
@@ -26,13 +26,13 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@main
with:
fetch-depth: 2
- name: Init Test Suite
uses: potatoqualitee/psmodulecache@v4
uses: potatoqualitee/psmodulecache@main
with:
modules-to-cache: PSScriptAnalyzer, BuildHelpers, Pester:4.10.1
modules-to-cache: BuildHelpers
shell: pwsh
- name: Test Scoop Core
shell: pwsh

View File

@@ -1,3 +1,140 @@
## [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))
- **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
- **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))
- **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/issues/5436))
- **core:** Handle scoop aliases and broken(edited,copied) shim ([#5551](https://github.com/ScoopInstaller/Scoop/issues/5551))
- **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))
- **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))
- **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))
- **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
- **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/issues/5377))
- **module:** Update 'psmodulecache' version to 'main' ([#5828](https://github.com/ScoopInstaller/Scoop/issues/5828))
### Tests
- **bucket:** Skip manifest validation if no manifest changes ([#5270](https://github.com/ScoopInstaller/Scoop/issues/5270))
### Documentation
- **scoop-info:** Fix help message([#5445](https://github.com/ScoopInstaller/Scoop/issues/5445))
- **readme:** Improve documentation language ([#5638](https://github.com/ScoopInstaller/Scoop/issues/5638))
## [v0.3.1](https://github.com/ScoopInstaller/Scoop/compare/v0.3.0...v0.3.1) - 2022-11-15
### Features
- **config:** Allow Scoop to check if apps versioned as 'nightly' are outdated ([#5166](https://github.com/ScoopInstaller/Scoop/issues/5166))
- **checkup:** Add Windows Developer Mode check ([#5233](https://github.com/ScoopInstaller/Scoop/issues/5233))
- **bucket:** Add 'sysinternals' bucket to known ([#5237](https://github.com/ScoopInstaller/Scoop/issues/5237))
### Bug Fixes
- **decompress:** Use PS's default 'Expand-Archive()' ([#5185](https://github.com/ScoopInstaller/Scoop/issues/5185))
- **hash:** Fix SourceForge's hash extraction ([#5189](https://github.com/ScoopInstaller/Scoop/issues/5189))
- **decompress:** Trim ending '/' ([#5195](https://github.com/ScoopInstaller/Scoop/issues/5195))
- **shim:** Exit if shim creating failed 'cause no git ([#5225](https://github.com/ScoopInstaller/Scoop/issues/5225))
- **scoop-import:** Add correct architecture argument ([#5210](https://github.com/ScoopInstaller/Scoop/issues/5210))
- **scoop-config:** Output `[DateTime]` as `[String]` ([#5232](https://github.com/ScoopInstaller/Scoop/issues/5232))
- **shim:** fixed shim add bug related to Resolve-Path ([#5492](https://github.com/ScoopInstaller/Scoop/issues/5492))
### Code Refactoring
- **hash:** Use `Get-FileHash()` directly ([#5177](https://github.com/ScoopInstaller/Scoop/issues/5177))
- **installer:** Drop the old installer ([#5186](https://github.com/ScoopInstaller/Scoop/issues/5186))
- **unix:** Remove `unix.ps1` ([#5235](https://github.com/ScoopInstaller/Scoop/issues/5235))
### Builds
- **auto-pr:** Add `CommitMessageFormat` option ([#5171](https://github.com/ScoopInstaller/Scoop/issues/5171))
- **checkver:** Support XML default namespace ([#5191](https://github.com/ScoopInstaller/Scoop/issues/5191))
- **pssa:** Remove unused 'ExcludeRules' ([#5201](https://github.com/ScoopInstaller/Scoop/issues/5201))
- **schema:** Add 'installer' and 'shortcuts' to 'autoupdate' ([#5220](https://github.com/ScoopInstaller/Scoop/issues/5220))
- **checkhashes:** Use correct version number if `UseCache` ([#5240](https://github.com/ScoopInstaller/Scoop/issues/5240))
### Continuous Integration
- **module:** Update modules version ([#5209](https://github.com/ScoopInstaller/Scoop/issues/5209))
### Tests
- **unix:** Fix tests in Linux and macOS ([#5179](https://github.com/ScoopInstaller/Scoop/issues/5179))
- **pester:** Update to Pester 5 ([#5222](https://github.com/ScoopInstaller/Scoop/issues/5222))
- **bucket:** Use BuildHelpers' EnvVars ([#5226](https://github.com/ScoopInstaller/Scoop/issues/5226))
### Documentation
- **scoop-cat:** Fix help message([#5224](https://github.com/ScoopInstaller/Scoop/issues/5224))
## [v0.3.0](https://github.com/ScoopInstaller/Scoop/compare/v0.2.4...v0.3.0) - 2022-10-10
### Features

View File

@@ -1,6 +1,3 @@
# The PowerShell Script Analyzer will generate a warning
# diagnostic record for this file due to a bug -
# https://github.com/PowerShell/PSScriptAnalyzer/issues/472
@{
# Only diagnostic records of the specified severity will be generated.
# Uncomment the following line if you only want Errors and Warnings but
@@ -26,12 +23,6 @@
# will be excluded.
ExcludeRules = @(
# Currently Scoop widely uses Write-Host to output colored text.
'PSAvoidUsingWriteHost',
# Temporarily allow uses of Invoke-Expression,
# this command is used by some core functions and hard to be removed.
'PSAvoidUsingInvokeExpression',
# PSUseDeclaredVarsMoreThanAssignments doesn't currently work due to:
# https://github.com/PowerShell/PSScriptAnalyzer/issues/636
'PSUseDeclaredVarsMoreThanAssignments'
'PSAvoidUsingWriteHost'
)
}

109
README.md
View File

@@ -1,17 +1,17 @@
<p align="center">
<h1 align="center">Scoop</h1>
<!--<img src="scoop.png" alt="Long live Scoop!"/>-->
<h1 align="center">Scoop</h1>
</p>
<p align="center">
<b><a href="https://github.com/ScoopInstaller/Scoop#what-does-scoop-do">Features</a></b>
|
<b><a href="https://github.com/ScoopInstaller/Scoop#installation">Installation</a></b>
|
<b><a href="https://github.com/ScoopInstaller/Scoop/wiki">Documentation</a></b>
<a href="https://github.com/ScoopInstaller/Scoop#what-does-scoop-do">Features</a>
|
<a href="https://github.com/ScoopInstaller/Scoop#installation">Installation</a>
|
<a href="https://github.com/ScoopInstaller/Scoop/wiki">Documentation</a>
</p>
- - -
<p align="center" >
---
<p align="center">
<a href="https://github.com/ScoopInstaller/Scoop">
<img src="https://img.shields.io/github/languages/code-size/ScoopInstaller/Scoop.svg" alt="Code Size" />
</a>
@@ -36,43 +36,48 @@ Scoop is a command-line installer for Windows.
## What does Scoop do?
Scoop installs programs from the command line with a minimal amount of friction. It:
Scoop installs apps from the command line with a minimal amount of friction. It:
- Eliminates permission popup windows
- Hides GUI wizard-style installers
- Prevents PATH pollution from installing lots of programs
- Avoids unexpected side-effects from installing and uninstalling programs
- Finds and installs dependencies automatically
- Performs all the extra setup steps itself to get a working program
- Eliminates [User Account Control](https://learn.microsoft.com/windows/security/application-security/application-control/user-account-control/) (UAC) prompt notifications.
- Hides the graphical user interface (GUI) of wizard-style installers.
- Prevents polluting the `PATH` environment variable. Normally, this variable gets cluttered as different apps are installed on the device.
- Avoids unexpected side effects from installing and uninstalling apps.
- Resolves and installs dependencies automatically.
- Performs all the necessary steps to get an app to a working state.
Scoop is very scriptable, so you can run repeatable setups to get your environment just the way you like, e.g.:
Scoop is quite script-friendly. Your environment can become the way you like by using repeatable setups. For example:
```powershell
```console
scoop install sudo
sudo scoop install 7zip git openssh --global
scoop install aria2 curl grep sed less touch
scoop install python ruby go perl
```
If you've built software that you'd like others to use, Scoop is an alternative to building an installer (e.g. MSI or InnoSetup) — you just need to zip your program and provide a JSON manifest that describes how to install it.
If you have built software that you would like others to use, Scoop is an alternative to building an installer (like MSI or InnoSetup). You just need to compress your app to a `.zip` file and provide a JSON manifest that describes how to install it.
## Installation
Run the following command from a **non-admin** PowerShell to install scoop to its default location `C:\Users\<YOUR USERNAME>\scoop`.
Run the following commands from a regular (non-admin) PowerShell terminal to install Scoop:
```powershell
iwr -useb get.scoop.sh | iex
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
```
Advanced installation instruction and full documentation of the installer are available in [ScoopInstaller/Install](https://github.com/ScoopInstaller/Install). Please create new issues there if you have questions about the installation.
**Note**: The first command makes your device allow running the installation and management scripts. This is necessary because Windows 10 client devices restrict execution of any PowerShell scripts by default.
## [Documentation](https://github.com/ScoopInstaller/Scoop/wiki)
It will install Scoop to its default location:
`C:\Users\<YOUR USERNAME>\scoop`
You can find the complete documentation about the installer, including advanced installation configurations, in [ScoopInstaller/Install](https://github.com/ScoopInstaller/Install). Please create new issues there if you have questions about the installation.
## Multi-connection downloads with `aria2`
Scoop can utilize [`aria2`](https://github.com/aria2/aria2) to use multi-connection downloads. Simply install `aria2` through Scoop and it will be used for all downloads afterward.
```powershell
```console
scoop install aria2
```
@@ -90,50 +95,54 @@ You can tweak the following `aria2` settings with the `scoop config` command:
## Inspiration
- [Homebrew](http://mxcl.github.io/homebrew/)
- [sub](https://github.com/37signals/sub#readme)
- [Homebrew](https://brew.sh/)
- [Sub](https://signalvnoise.com/posts/3264-automating-with-convention-introducing-sub)
## What sort of apps can Scoop install?
The apps that install best with Scoop are commonly called "portable" apps: i.e. compressed program files that run stand-alone when extracted and don't have side-effects like changing the registry or putting files outside the program directory.
The apps that are most likely to get installed fine with Scoop are those referred to as "portable" apps. These apps are compressed files which can run standalone after being extracted. This type of apps does not produce side effects like changing the Windows Registry or placing files outside the app directory.
Since installers are common, Scoop supports them too (and their uninstallers).
Scoop is also great at handling single-file programs and Powershell scripts. These don't even need to be compressed. See the [runat](https://github.com/ScoopInstaller/Main/blob/master/bucket/runat.json) package for an example: it's really just a GitHub gist.
Scoop also supports installer files and their uninstallation methods. Likewise, it can handle single-file apps and PowerShell scripts. These do not even need to be compressed. See the [runat](https://github.com/ScoopInstaller/Main/blob/master/bucket/runat.json) package for an example: it is simply a GitHub gist.
### Contribute to this project
If you'd like to improve Scoop by adding features or fixing bugs, please read our [Contributing Guide](https://github.com/ScoopInstaller/.github/blob/main/.github/CONTRIBUTING.md).
If you would like to improve Scoop by adding features or fixing bugs, please read our [Contributing Guide](https://github.com/ScoopInstaller/.github/blob/main/.github/CONTRIBUTING.md).
### Support this project
If you find Scoop useful and would like to support ongoing development and maintenance, here's how:
If you find Scoop useful and would like to support the ongoing development and maintenance of this project, you can donate here:
- [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DM2SUH9EUXSKJ) (one-time donation)
- [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DM2SUH9EUXSKJ) (one-time donations)
## Known application buckets
The following buckets are known to scoop:
The following buckets are known to Scoop:
- [main](https://github.com/ScoopInstaller/Main) - Default bucket for the most common (mostly CLI) apps
- [extras](https://github.com/ScoopInstaller/Extras) - Apps that don't fit the main bucket's [criteria](https://github.com/ScoopInstaller/Scoop/wiki/Criteria-for-including-apps-in-the-main-bucket)
- [games](https://github.com/Calinou/scoop-games) - Open source/freeware games and game-related tools
- [nerd-fonts](https://github.com/matthewjberger/scoop-nerd-fonts) - Nerd Fonts
- [nirsoft](https://github.com/kodybrown/scoop-nirsoft) - Almost all of the [250+](https://rasa.github.io/scoop-directory/by-apps#kodybrown_scoop-nirsoft) apps from [Nirsoft](https://nirsoft.net)
- [java](https://github.com/ScoopInstaller/Java) - A collection of Java development kits (JDKs), Java runtime engines (JREs), Java's virtual machine debugging tools and Java based runtime engines.
- [nonportable](https://github.com/ScoopInstaller/Nonportable) - Non-portable apps (may require UAC)
- [php](https://github.com/ScoopInstaller/PHP) - Installers for most versions of PHP
- [versions](https://github.com/ScoopInstaller/Versions) - Alternative versions of apps found in other buckets
- [main](https://github.com/ScoopInstaller/Main) - Default bucket which contains popular non-GUI apps.
- [extras](https://github.com/ScoopInstaller/Extras) - Apps that do not fit the main bucket's [criteria](https://github.com/ScoopInstaller/Scoop/wiki/Criteria-for-including-apps-in-the-main-bucket).
- [games](https://github.com/Calinou/scoop-games) - Open-source and freeware video games and game-related tools.
- [nerd-fonts](https://github.com/matthewjberger/scoop-nerd-fonts) - Nerd Fonts.
- [nirsoft](https://github.com/ScoopInstaller/Nirsoft) - A collection of over 250+ apps from [Nirsoft](https://nirsoft.net).
- [sysinternals](https://github.com/niheaven/scoop-sysinternals) - The Sysinternals suite from [Microsoft](https://learn.microsoft.com/sysinternals/).
- [java](https://github.com/ScoopInstaller/Java) - A collection of Java development kits (JDKs) and Java runtime engines (JREs), Java's virtual machine debugging tools and Java based runtime engines.
- [nonportable](https://github.com/ScoopInstaller/Nonportable) - Non-portable apps (may trigger UAC prompts).
- [php](https://github.com/ScoopInstaller/PHP) - Installers for most versions of PHP.
- [versions](https://github.com/ScoopInstaller/Versions) - Alternative versions of apps found in other buckets.
The main bucket is installed by default. To add any of the other buckets, type:
```
scoop bucket add bucketname
```
For example, to add the extras bucket, type:
The `main` bucket is installed by default. You can make use of more buckets by typing:
```console
scoop bucket add <name>
```
For example, to add the `extras` bucket, type:
```console
scoop bucket add extras
```
You would be able to install apps from the `extras` bucket now.
## Other application buckets
Many other application buckets hosted on Github can be found in the [Scoop Directory](https://rasa.github.io/scoop-directory/) or via [other search engines](https://rasa.github.io/scoop-directory/#other-search-engines).
Many other application buckets hosted on GitHub can be found on [ScoopSearch](https://scoop.sh/) or via [other search engines](https://rasa.github.io/scoop-directory/#other-search-engines).

View File

@@ -11,6 +11,10 @@
.PARAMETER App
Manifest name to search.
Placeholders are supported.
.PARAMETER CommitMessageFormat
The format of the commit message.
<app> will be replaced with the file name of manifest.
<version> will be replaced with the version of the latest manifest.
.PARAMETER Dir
The directory where to search for manifests.
.PARAMETER Push
@@ -43,6 +47,7 @@ param(
[String] $Upstream,
[String] $OriginBranch = 'master',
[String] $App = '*',
[String] $CommitMessageFormat = '<app>: Update to version <version>',
[ValidateScript( {
if (!(Test-Path $_ -Type Container)) {
throw "$_ is not a directory!"
@@ -61,7 +66,6 @@ param(
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\unix.ps1"
if ($App -ne '*' -and (Test-Path $App -PathType Leaf)) {
$Dir = Split-Path $App
@@ -87,7 +91,7 @@ Optional options:
exit 0
}
if (is_unix) {
if ($IsLinux -or $IsMacOS) {
if (!(which hub)) {
Write-Host "Please install hub ('brew install hub' or visit: https://hub.github.com/)" -ForegroundColor Yellow
exit 1
@@ -110,7 +114,7 @@ function execute($cmd) {
return $output
}
function pull_requests($json, [String] $app, [String] $upstream, [String] $manifest) {
function pull_requests($json, [String] $app, [String] $upstream, [String] $manifest, [String] $commitMessage) {
$version = $json.version
$homepage = $json.homepage
$branch = "manifest/$app-$version"
@@ -127,7 +131,7 @@ function pull_requests($json, [String] $app, [String] $upstream, [String] $manif
Write-Host "Creating update $app ($version) ..." -ForegroundColor DarkCyan
execute "hub checkout -b $branch"
execute "hub add $manifest"
execute "hub commit -m '${app}: Update to version $version'"
execute "hub commit -m '$commitMessage"
Write-Host "Pushing update $app ($version) ..." -ForegroundColor DarkCyan
execute "hub push origin $branch"
@@ -142,7 +146,7 @@ function pull_requests($json, [String] $app, [String] $upstream, [String] $manif
Write-Host "hub pull-request -m '<msg>' -b '$upstream' -h '$branch'" -ForegroundColor Green
$msg = @"
$app`: Update to version $version
$commitMessage
Hello lovely humans,
a new version of [$app]($homepage) is available.
@@ -155,7 +159,7 @@ a new version of [$app]($homepage) is available.
hub pull-request -m "$msg" -b "$upstream" -h "$branch"
if ($LASTEXITCODE -gt 0) {
execute 'hub reset'
abort "Pull Request failed! (hub pull-request -m '${app}: Update to version $version' -b '$upstream' -h '$branch')"
abort "Pull Request failed! (hub pull-request -m '$commitMessage' -b '$upstream' -h '$branch')"
}
}
@@ -189,7 +193,7 @@ hub diff --name-only | ForEach-Object {
return
}
$version = $json.version
$CommitMessage = $CommitMessageFormat -replace '<app>',$app -replace '<version>',$version
if ($Push) {
Write-Host "Creating update $app ($version) ..." -ForegroundColor DarkCyan
execute "hub add $manifest"
@@ -198,12 +202,12 @@ hub diff --name-only | ForEach-Object {
$status = execute 'hub status --porcelain -uno'
$status = $status | Where-Object { $_ -match "M\s{2}.*$app.json" }
if ($status -and $status.StartsWith('M ') -and $status.EndsWith("$app.json")) {
execute "hub commit -m '${app}: Update to version $version'"
execute "hub commit -m '$commitMessage'"
} else {
Write-Host "Skipping $app because only LF/CRLF changes were detected ..." -ForegroundColor Yellow
}
} else {
pull_requests $json $app $Upstream $manifest
pull_requests $json $app $Upstream $manifest $CommitMessage
}
}

View File

@@ -47,7 +47,6 @@ param(
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\unix.ps1"
$Dir = Convert-Path $Dir
if ($ForceUpdate) { $Update = $true }
@@ -114,13 +113,16 @@ foreach ($current in $MANIFESTS) {
$current.urls | ForEach-Object {
$algorithm, $expected = get_hash $current.hashes[$count]
$version = 'HASH_CHECK'
$tmp = $expected_hash -split ':'
if ($UseCache) {
$version = $current.manifest.version
} else {
$version = 'HASH_CHECK'
}
Invoke-CachedDownload $current.app $version $_ $null $null -use_cache:$UseCache
$to_check = fullpath (cache_path $current.app $version $_)
$actual_hash = compute_hash $to_check $algorithm
$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
if ($algorithm -ne 'sha256') {
@@ -144,13 +146,13 @@ 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[$_])
Write-Host "`tURL:`t`t$($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())"
Write-Host "`tFirst bytes:`t$((get_magic_bytes_pretty $file ' ').ToUpper())"
}
Write-Host "`tExpected:`t$($current.hashes[$_])" -ForegroundColor Green
Write-Host "`tActual:`t`t$($actuals[$_])" -ForegroundColor Red
Write-Host "`tExpected:`t$($current.hashes[$_])" -ForegroundColor Green
Write-Host "`tActual:`t`t$($actuals[$_])" -ForegroundColor Red
}
}

View File

@@ -74,7 +74,6 @@ param(
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
. "$PSScriptRoot\..\lib\install.ps1" # needed for hash generation
. "$PSScriptRoot\..\lib\unix.ps1"
if ($App -ne '*' -and (Test-Path $App -PathType Leaf)) {
$Dir = Split-Path $App
@@ -227,15 +226,21 @@ $Queue | ForEach-Object {
$url = substitute $url $substitutions
$state = New-Object psobject @{
app = $name;
file = $file;
url = $url;
regex = $regex;
json = $json;
jsonpath = $jsonpath;
xpath = $xpath;
reverse = $reverse;
replace = $replace;
app = $name
file = $file
url = $url
regex = $regex
json = $json
jsonpath = $jsonpath
xpath = $xpath
reverse = $reverse
replace = $replace
}
get_config PRIVATE_HOSTS | Where-Object { $_ -ne $null -and $url -match $_.match } | ForEach-Object {
(ConvertFrom-StringData -StringData $_.Headers).GetEnumerator() | ForEach-Object {
$wc.Headers[$_.Key] = $_.Value
}
}
$wc.Headers.Add('Referer', (strip_filename $url))
@@ -255,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
@@ -280,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"))
@@ -310,12 +322,17 @@ while ($in_progress -gt 0) {
# Then add them into the NamespaceManager
$nsmgr = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
$nsList | ForEach-Object {
$nsmgr.AddNamespace($_.LocalName, $_.Value)
if ($_.LocalName -eq 'xmlns') {
$nsmgr.AddNamespace('ns', $_.Value)
$xpath = $xpath -replace '/([^:/]+)((?=/)|(?=$))', '/ns:$1'
} else {
$nsmgr.AddNamespace($_.LocalName, $_.Value)
}
}
# Getting version from XML, using XPath
$ver = $xml.SelectSingleNode($xpath, $nsmgr).'#text'
if (!$ver) {
next "couldn't find '$xpath' in $url"
next "couldn't find '$($xpath -replace 'ns:', '')' in $url"
continue
}
}

View File

@@ -1,78 +1,2 @@
#Requires -Version 5
# remote install:
# Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
$old_erroractionpreference = $erroractionpreference
$erroractionpreference = 'stop' # quit if anything goes wrong
if (($PSVersionTable.PSVersion.Major) -lt 5) {
Write-Output "PowerShell 5 or later is required to run Scoop."
Write-Output "Upgrade PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell"
break
}
# show notification to change execution policy:
$allowedExecutionPolicy = @('Unrestricted', 'RemoteSigned', 'ByPass')
if ((Get-ExecutionPolicy).ToString() -notin $allowedExecutionPolicy) {
Write-Output "PowerShell requires an execution policy in [$($allowedExecutionPolicy -join ", ")] to run Scoop."
Write-Output "For example, to set the execution policy to 'RemoteSigned' please run :"
Write-Output "'Set-ExecutionPolicy RemoteSigned -scope CurrentUser'"
break
}
if ([System.Enum]::GetNames([System.Net.SecurityProtocolType]) -notcontains 'Tls12') {
Write-Output "Scoop requires at least .NET Framework 4.5"
Write-Output "Please download and install it first:"
Write-Output "https://www.microsoft.com/net/download"
break
}
# get core functions
$core_url = 'https://raw.githubusercontent.com/ScoopInstaller/Scoop/master/lib/core.ps1'
Write-Output 'Initializing...'
Invoke-Expression (new-object net.webclient).downloadstring($core_url)
# prep
if (Get-Command -Name 'scoop' -ErrorAction SilentlyContinue) {
write-host "Scoop is already installed. Run 'scoop update' to get the latest version." -f red
# don't abort if invoked with iex that would close the PS session
if ($myinvocation.mycommand.commandtype -eq 'Script') { return } else { exit 1 }
}
$dir = ensure (versiondir 'scoop' 'current')
# download scoop zip
$zipUrl = 'https://github.com/ScoopInstaller/Scoop/archive/master.zip'
$zipFile = "$dir\scoop.zip"
Write-Output 'Downloading scoop...'
Invoke-WebRequest -Uri $zipUrl -OutFile $zipFile
Write-Output 'Extracting...'
Add-Type -Assembly "System.IO.Compression.FileSystem"
[IO.Compression.ZipFile]::ExtractToDirectory($zipfile, "$dir\_tmp")
Copy-Item "$dir\_tmp\*master\*" $dir -Recurse -Force
Remove-Item "$dir\_tmp", $zipfile -Recurse -Force
Write-Output 'Creating shim...'
shim "$dir\bin\scoop.ps1" $false
# download main bucket
$dir = "$scoopdir\buckets\main"
$zipUrl = 'https://github.com/ScoopInstaller/Main/archive/master.zip'
$zipFile = "$dir\main-bucket.zip"
Write-Output 'Downloading main bucket...'
New-Item -Path $dir -Type Directory -Force | Out-Null
Invoke-WebRequest -Uri $zipUrl -OutFile $zipFile
Write-Output 'Extracting...'
[IO.Compression.ZipFile]::ExtractToDirectory($zipfile, "$dir\_tmp")
Copy-Item "$dir\_tmp\*-master\*" $dir -Recurse -Force
Remove-Item "$dir\_tmp", $zipfile -Recurse -Force
ensure_robocopy_in_path
set_config LAST_UPDATE ([System.DateTime]::Now.ToString('o')) | Out-Null
success 'Scoop was installed successfully!'
Write-Output "Type 'scoop help' for instructions."
$erroractionpreference = $old_erroractionpreference # Reset $erroractionpreference to original value
Invoke-RestMethod https://get.scoop.sh | Invoke-Expression

View File

@@ -20,8 +20,8 @@ switch ($subCommand) {
}
({ $subCommand -in @('-v', '--version') }) {
Write-Host 'Current Scoop version:'
if ((Test-CommandAvailable git) -and (Test-Path "$PSScriptRoot\..\.git") -and (get_config SCOOP_BRANCH 'master') -ne 'master') {
git -C "$PSScriptRoot\.." --no-pager log --oneline HEAD -n 1
if (Test-GitAvailable -and (Test-Path "$PSScriptRoot\..\.git") -and (get_config SCOOP_BRANCH 'master') -ne 'master') {
Invoke-Git -Path "$PSScriptRoot\.." -ArgumentList @('log', 'HEAD', '-1', '--oneline')
} else {
$version = Select-String -Pattern '^## \[(v[\d.]+)\].*?([\d-]+)$' -Path "$PSScriptRoot\..\CHANGELOG.md"
Write-Host $version.Matches.Groups[1].Value -ForegroundColor Cyan -NoNewline
@@ -31,9 +31,9 @@ switch ($subCommand) {
Get-LocalBucket | ForEach-Object {
$bucketLoc = Find-BucketDirectory $_ -Root
if ((Test-Path "$bucketLoc\.git") -and (Test-CommandAvailable git)) {
if (Test-GitAvailable -and (Test-Path "$bucketLoc\.git")) {
Write-Host "'$_' bucket:"
git -C "$bucketLoc" --no-pager log --oneline HEAD -n 1
Invoke-Git -Path $bucketLoc -ArgumentList @('log', 'HEAD', '-1', '--oneline')
Write-Host ''
}
}

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

@@ -2,7 +2,8 @@
"main": "https://github.com/ScoopInstaller/Main",
"extras": "https://github.com/ScoopInstaller/Extras",
"versions": "https://github.com/ScoopInstaller/Versions",
"nirsoft": "https://github.com/kodybrown/scoop-nirsoft",
"nirsoft": "https://github.com/ScoopInstaller/Nirsoft",
"sysinternals": "https://github.com/niheaven/scoop-sysinternals",
"php": "https://github.com/ScoopInstaller/PHP",
"nerd-fonts": "https://github.com/matthewjberger/scoop-nerd-fonts",
"nonportable": "https://github.com/ScoopInstaller/Nonportable",

View File

@@ -8,9 +8,9 @@ function find_hash_in_rdf([String] $url, [String] $basename) {
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$data = $wc.DownloadData($url)
[xml]$xml = (Get-Encoding($wc)).GetString($data)
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"
} catch [System.Net.WebException] {
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
return $null
}
@@ -24,12 +24,12 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
$hashfile = $null
$templates = @{
'$md5' = '([a-fA-F0-9]{32})';
'$sha1' = '([a-fA-F0-9]{40})';
'$sha256' = '([a-fA-F0-9]{64})';
'$sha512' = '([a-fA-F0-9]{128})';
'$checksum' = '([a-fA-F0-9]{32,128})';
'$base64' = '([a-zA-Z0-9+\/=]{24,88})';
'$md5' = '([a-fA-F0-9]{32})'
'$sha1' = '([a-fA-F0-9]{40})'
'$sha256' = '([a-fA-F0-9]{64})'
'$sha512' = '([a-fA-F0-9]{128})'
'$checksum' = '([a-fA-F0-9]{32,128})'
'$base64' = '([a-zA-Z0-9+\/=]{24,88})'
}
try {
@@ -37,10 +37,16 @@ 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 -f darkred $_
write-host -f darkred "URL $url is not valid"
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
return
}
@@ -50,15 +56,15 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
$regex = substitute $regex $templates $false
$regex = substitute $regex $substitutions $true
debug $regex
if ($hashfile -match $regex) {
$hash = $matches[1] -replace '\s',''
debug $regex
$hash = $matches[1] -replace '\s', ''
}
# convert base64 encoded hash values
if ($hash -match '^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$') {
$base64 = $matches[0]
if(!($hash -match '^[a-fA-F0-9]+$') -and $hash.length -notin @(32, 40, 64, 128)) {
if (!($hash -match '^[a-fA-F0-9]+$') -and $hash.Length -notin @(32, 40, 64, 128)) {
try {
$hash = ([System.Convert]::FromBase64String($base64) | ForEach-Object { $_.ToString('x2') }) -join ''
} catch {
@@ -69,13 +75,15 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
# find hash with filename in $hashfile
if ($hash.Length -eq 0) {
$filenameRegex = "([a-fA-F0-9]{32,128})[\x20\t]+.*`$basename(?:[\x20\t]+\d+)?"
$filenameRegex = "([a-fA-F0-9]{32,128})[\x20\t]+.*`$basename(?:\s|$)|`$basename[\x20\t]+.*?([a-fA-F0-9]{32,128})"
$filenameRegex = substitute $filenameRegex $substitutions $true
if ($hashfile -match $filenameRegex) {
debug $filenameRegex
$hash = $matches[1]
}
$metalinkRegex = "<hash[^>]+>([a-fA-F0-9]{64})"
$metalinkRegex = '<hash[^>]+>([a-fA-F0-9]{64})'
if ($hashfile -match $metalinkRegex) {
debug $metalinkRegex
$hash = $matches[1]
}
}
@@ -91,14 +99,21 @@ 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)
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"
$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
return
}
debug $jsonpath
$hash = json_path $json $jsonpath $substitutions
if(!$hash) {
if (!$hash) {
$hash = json_path_legacy $json $jsonpath $substitutions
}
return format_hash $hash
@@ -112,10 +127,16 @@ 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 -f darkred $_
write-host -f darkred "URL $url is not valid"
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
return
}
@@ -125,13 +146,15 @@ function find_hash_in_xml([String] $url, [Hashtable] $substitutions, [String] $x
}
# Find all `significant namespace declarations` from the XML file
$nsList = $xml.SelectNodes("//namespace::*[not(. = ../../namespace::*)]")
$nsList = $xml.SelectNodes('//namespace::*[not(. = ../../namespace::*)]')
# Then add them into the NamespaceManager
$nsmgr = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
$nsList | ForEach-Object {
$nsmgr.AddNamespace($_.LocalName, $_.Value)
}
debug $xpath
debug $nsmgr
# Getting hash from XML, using XPath
$hash = $xml.SelectSingleNode($xpath, $nsmgr).'#text'
return format_hash $hash
@@ -148,16 +171,16 @@ function find_hash_in_headers([String] $url) {
$req.Timeout = 2000
$req.Method = 'HEAD'
$res = $req.GetResponse()
if(([int]$res.StatusCode -ge 300) -and ([int]$res.StatusCode -lt 400)) {
if($res.Headers['Digest'] -match 'SHA-256=([^,]+)' -or $res.Headers['Digest'] -match 'SHA=([^,]+)' -or $res.Headers['Digest'] -match 'MD5=([^,]+)') {
if (([int]$res.StatusCode -ge 300) -and ([int]$res.StatusCode -lt 400)) {
if ($res.Headers['Digest'] -match 'SHA-256=([^,]+)' -or $res.Headers['Digest'] -match 'SHA=([^,]+)' -or $res.Headers['Digest'] -match 'MD5=([^,]+)') {
$hash = ([System.Convert]::FromBase64String($matches[1]) | ForEach-Object { $_.ToString('x2') }) -join ''
debug $hash
}
}
$res.Close()
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"
} catch [System.Net.WebException] {
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
return
}
@@ -182,10 +205,10 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
$hashfile_url = substitute $config.url $substitutions
debug $hashfile_url
if ($hashfile_url) {
write-host -f DarkYellow 'Searching hash for ' -NoNewline
write-host -f Green $basename -NoNewline
write-host -f DarkYellow ' in ' -NoNewline
write-host -f Green $hashfile_url
Write-Host 'Searching hash for ' -ForegroundColor DarkYellow -NoNewline
Write-Host $basename -ForegroundColor Green -NoNewline
Write-Host ' in ' -ForegroundColor DarkYellow -NoNewline
Write-Host $hashfile_url -ForegroundColor Green
}
if ($hashmode.Length -eq 0 -and $config.url.Length -ne 0) {
@@ -215,11 +238,11 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
$hashmode = 'xpath'
}
if (!$hashfile_url -and $url -match "^(?:.*fosshub.com\/).*(?:\/|\?dwl=)(?<filename>.*)$") {
if (!$hashfile_url -and $url -match '^(?:.*fosshub.com\/).*(?:\/|\?dwl=)(?<filename>.*)$') {
$hashmode = 'fosshub'
}
if (!$hashfile_url -and $url -match "(?:downloads\.)?sourceforge.net\/projects?\/(?<project>[^\/]+)\/(?:files\/)?(?<file>.*)") {
if (!$hashfile_url -and $url -match '(?:downloads\.)?sourceforge.net\/projects?\/(?<project>[^\/]+)\/(?:files\/)?(?<file>.*)') {
$hashmode = 'sourceforge'
}
@@ -243,40 +266,40 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
}
}
'fosshub' {
$hash = find_hash_in_textfile $url $substitutions ($Matches.filename+'.*?"sha256":"([a-fA-F0-9]{64})"')
$hash = find_hash_in_textfile $url $substitutions ($matches.filename + '.*?"sha256":"([a-fA-F0-9]{64})"')
}
'sourceforge' {
# change the URL because downloads.sourceforge.net doesn't have checksums
$hashfile_url = (strip_filename (strip_fragment "https://sourceforge.net/projects/$($matches['project'])/files/$($matches['file'])")).TrimEnd('/')
$hash = find_hash_in_textfile $hashfile_url $substitutions '"$basename":.*?"sha1":\s"([a-fA-F0-9]{40})"'
$hash = find_hash_in_textfile $hashfile_url $substitutions '"$basename":.*?"sha1":\s*"([a-fA-F0-9]{40})"'
}
}
if ($hash) {
# got one!
write-host -f DarkYellow 'Found: ' -NoNewline
write-host -f Green $hash -NoNewline
write-host -f DarkYellow ' using ' -NoNewline
write-host -f Green "$((Get-Culture).TextInfo.ToTitleCase($hashmode)) Mode"
Write-Host 'Found: ' -ForegroundColor DarkYellow -NoNewline
Write-Host $hash -ForegroundColor Green -NoNewline
Write-Host ' using ' -ForegroundColor DarkYellow -NoNewline
Write-Host "$((Get-Culture).TextInfo.ToTitleCase($hashmode)) Mode" -ForegroundColor Green
return $hash
} elseif ($hashfile_url) {
write-host -f DarkYellow "Could not find hash in $hashfile_url"
Write-Host -f DarkYellow "Could not find hash in $hashfile_url"
}
write-host -f DarkYellow 'Downloading ' -NoNewline
write-host -f Green $basename -NoNewline
write-host -f DarkYellow ' to compute hashes!'
Write-Host 'Downloading ' -ForegroundColor DarkYellow -NoNewline
Write-Host $basename -ForegroundColor Green -NoNewline
Write-Host ' to compute hashes!' -ForegroundColor DarkYellow
try {
Invoke-CachedDownload $app $version $url $null $null $true
} catch [system.net.webexception] {
write-host -f darkred $_
write-host -f darkred "URL $url is not valid"
Write-Host $_ -ForegroundColor DarkRed
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
return $null
}
$file = fullpath (cache_path $app $version $url)
$hash = compute_hash $file 'sha256'
write-host -f DarkYellow 'Computed hash: ' -NoNewline
write-host -f Green $hash
$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
return $hash
}
@@ -346,7 +369,7 @@ function Update-ManifestProperty {
$newValue = substitute $autoupdateProperty $Substitutions
if (($autoupdateProperty.GetType().Name -eq 'Object[]') -and ($autoupdateProperty.Length -eq 1)) {
# Make sure it's an array
$newValue = ,$newValue
$newValue = , $newValue
}
$Manifest.$currentProperty, $hasPropertyChanged = PropertyHelper -Property $Manifest.$currentProperty -Value $newValue
$hasManifestChanged = $hasManifestChanged -or $hasPropertyChanged
@@ -359,7 +382,7 @@ function Update-ManifestProperty {
$newValue = substitute $autoupdateProperty $Substitutions
if (($autoupdateProperty.GetType().Name -eq 'Object[]') -and ($autoupdateProperty.Length -eq 1)) {
# Make sure it's an array
$newValue = ,$newValue
$newValue = , $newValue
}
$Manifest.architecture.$arch.$currentProperty, $hasPropertyChanged = PropertyHelper -Property $Manifest.architecture.$arch.$currentProperty -Value $newValue
$hasManifestChanged = $hasManifestChanged -or $hasPropertyChanged
@@ -388,25 +411,25 @@ function Get-VersionSubstitution {
$firstPart = $Version.Split('-') | Select-Object -First 1
$lastPart = $Version.Split('-') | Select-Object -Last 1
$versionVariables = @{
'$version' = $Version;
'$dotVersion' = ($Version -replace '[._-]', '.');
'$underscoreVersion' = ($Version -replace '[._-]', '_');
'$dashVersion' = ($Version -replace '[._-]', '-');
'$cleanVersion' = ($Version -replace '[._-]', '');
'$majorVersion' = $firstPart.Split('.') | Select-Object -First 1;
'$minorVersion' = $firstPart.Split('.') | Select-Object -Skip 1 -First 1;
'$patchVersion' = $firstPart.Split('.') | Select-Object -Skip 2 -First 1;
'$buildVersion' = $firstPart.Split('.') | Select-Object -Skip 3 -First 1;
'$preReleaseVersion' = $lastPart;
'$version' = $Version
'$dotVersion' = ($Version -replace '[._-]', '.')
'$underscoreVersion' = ($Version -replace '[._-]', '_')
'$dashVersion' = ($Version -replace '[._-]', '-')
'$cleanVersion' = ($Version -replace '[._-]', '')
'$majorVersion' = $firstPart.Split('.') | Select-Object -First 1
'$minorVersion' = $firstPart.Split('.') | Select-Object -Skip 1 -First 1
'$patchVersion' = $firstPart.Split('.') | Select-Object -Skip 2 -First 1
'$buildVersion' = $firstPart.Split('.') | Select-Object -Skip 3 -First 1
'$preReleaseVersion' = $lastPart
}
if($Version -match "(?<head>\d+\.\d+(?:\.\d+)?)(?<tail>.*)") {
$versionVariables.Set_Item('$matchHead', $Matches['head'])
$versionVariables.Set_Item('$matchTail', $Matches['tail'])
if ($Version -match '(?<head>\d+\.\d+(?:\.\d+)?)(?<tail>.*)') {
$versionVariables.Add('$matchHead', $Matches['head'])
$versionVariables.Add('$matchTail', $Matches['tail'])
}
if($CustomMatches) {
if ($CustomMatches) {
$CustomMatches.GetEnumerator() | ForEach-Object {
if($_.Name -ne "0") {
$versionVariables.Set_Item('$match' + (Get-Culture).TextInfo.ToTitleCase($_.Name), $_.Value)
if ($_.Name -ne '0') {
$versionVariables.Add('$match' + (Get-Culture).TextInfo.ToTitleCase($_.Name), $_.Value)
}
}
}

View File

@@ -58,10 +58,18 @@ function Get-LocalBucket {
.SYNOPSIS
List all local buckets.
#>
$bucketNames = (Get-ChildItem -Path $bucketsdir -Directory).Name
$bucketNames = [System.Collections.Generic.List[String]](Get-ChildItem -Path $bucketsdir -Directory).Name
if ($null -eq $bucketNames) {
return @() # Return a zero-length list instead of $null.
} else {
$knownBuckets = known_buckets
for ($i = $knownBuckets.Count - 1; $i -ge 0 ; $i--) {
$name = $knownBuckets[$i]
if ($bucketNames.Contains($name)) {
[void]$bucketNames.Remove($name)
$bucketNames.Insert(0, $name)
}
}
return $bucketNames
}
}
@@ -99,11 +107,11 @@ function list_buckets {
$bucket = [Ordered]@{ Name = $_ }
$path = Find-BucketDirectory $_ -Root
if ((Test-Path (Join-Path $path '.git')) -and (Get-Command git -ErrorAction SilentlyContinue)) {
$bucket.Source = git -C $path config remote.origin.url
$bucket.Updated = git -C $path log --format='%aD' -n 1 | Get-Date
$bucket.Source = Invoke-Git -Path $path -ArgumentList @('config', 'remote.origin.url')
$bucket.Updated = Invoke-Git -Path $path -ArgumentList @('log', '--format=%aD', '-n', '1') | Get-Date
} else {
$bucket.Source = friendly_path $path
$bucket.Updated = (Get-Item "$path\bucket").LastWriteTime
$bucket.Updated = (Get-Item "$path\bucket" -ErrorAction SilentlyContinue).LastWriteTime
}
$bucket.Manifests = Get-ChildItem "$path\bucket" -Force -Recurse -ErrorAction SilentlyContinue |
Measure-Object | Select-Object -ExpandProperty Count
@@ -113,7 +121,7 @@ function list_buckets {
}
function add_bucket($name, $repo) {
if (!(Test-CommandAvailable git)) {
if (!(Test-GitAvailable)) {
error "Git is required for buckets. Run 'scoop install git' and try again."
return 1
}
@@ -130,7 +138,7 @@ function add_bucket($name, $repo) {
}
foreach ($bucket in Get-LocalBucket) {
if (Test-Path -Path "$bucketsdir\$bucket\.git") {
$remote = git -C "$bucketsdir\$bucket" config --get remote.origin.url
$remote = Invoke-Git -Path "$bucketsdir\$bucket" -ArgumentList @('config', '--get', 'remote.origin.url')
if ((Convert-RepositoryUri -Uri $remote) -eq $uni_repo) {
warn "Bucket $bucket already exists for $repo"
return 2
@@ -139,14 +147,14 @@ function add_bucket($name, $repo) {
}
Write-Host 'Checking repo... ' -NoNewline
$out = git_cmd ls-remote $repo 2>&1
$out = Invoke-Git -ArgumentList @('ls-remote', $repo) 2>&1
if ($LASTEXITCODE -ne 0) {
error "'$repo' doesn't look like a valid git repository`n`nError given:`n$out"
return 1
}
ensure $bucketsdir | Out-Null
$dir = ensure $dir
git_cmd clone "$repo" "`"$dir`"" -q
Invoke-Git -ArgumentList @('clone', $repo, $dir, '-q')
Write-Host 'OK'
success "The $name bucket was added successfully."
return 0
@@ -169,7 +177,7 @@ function new_issue_msg($app, $bucket, $title, $body) {
$bucket_path = "$bucketsdir\$bucket"
if (Test-Path $bucket_path) {
$remote = git -C "$bucket_path" config --get remote.origin.url
$remote = Invoke-Git -Path $bucket_path -ArgumentList @('config', '--get', 'remote.origin.url')
# Support ssh and http syntax
# git@PROVIDER:USER/REPO.git
# https://PROVIDER/USER/REPO.git

View File

@@ -1,3 +1,51 @@
function Get-PESubsystem($filePath) {
try {
$fileStream = [System.IO.FileStream]::new($filePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
$binaryReader = [System.IO.BinaryReader]::new($fileStream)
$fileStream.Seek(0x3C, [System.IO.SeekOrigin]::Begin) | Out-Null
$peOffset = $binaryReader.ReadInt32()
$fileStream.Seek($peOffset, [System.IO.SeekOrigin]::Begin) | Out-Null
$fileHeaderOffset = $fileStream.Position
$fileStream.Seek(18, [System.IO.SeekOrigin]::Current) | Out-Null
$fileStream.Seek($fileHeaderOffset + 0x5C, [System.IO.SeekOrigin]::Begin) | Out-Null
return $binaryReader.ReadInt16()
} catch {
return -1
} finally {
$binaryReader.Close()
$fileStream.Close()
}
}
function Set-PESubsystem($filePath, $targetSubsystem) {
try {
$fileStream = [System.IO.FileStream]::new($filePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite)
$binaryReader = [System.IO.BinaryReader]::new($fileStream)
$binaryWriter = [System.IO.BinaryWriter]::new($fileStream)
$fileStream.Seek(0x3C, [System.IO.SeekOrigin]::Begin) | Out-Null
$peOffset = $binaryReader.ReadInt32()
$fileStream.Seek($peOffset, [System.IO.SeekOrigin]::Begin) | Out-Null
$fileHeaderOffset = $fileStream.Position
$fileStream.Seek(18, [System.IO.SeekOrigin]::Current) | Out-Null
$fileStream.Seek($fileHeaderOffset + 0x5C, [System.IO.SeekOrigin]::Begin) | Out-Null
$binaryWriter.Write([System.Int16] $targetSubsystem)
} catch {
return $false
} finally {
$binaryReader.Close()
$fileStream.Close()
}
return $true
}
function Optimize-SecurityProtocol {
# .NET Framework 4.7+ has a default security protocol called 'SystemDefault',
# which allows the operating system to choose the best protocol to use.
@@ -84,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 {
@@ -99,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
@@ -128,13 +247,68 @@ function setup_proxy() {
}
}
function git_cmd {
function Invoke-Git {
[CmdletBinding()]
[OutputType([String])]
param(
[Parameter(Mandatory = $false, Position = 0)]
[Alias('PSPath', 'Path')]
[ValidateNotNullOrEmpty()]
[String]
$WorkingDirectory,
[Parameter(Mandatory = $true, Position = 1)]
[Alias('Args')]
[String[]]
$ArgumentList
)
$proxy = get_config PROXY
$cmd = "git $($args | ForEach-Object { "$_ " })"
if ($proxy -and $proxy -ne 'none') {
$cmd = "SET HTTPS_PROXY=$proxy&&SET HTTP_PROXY=$proxy&&$cmd"
$git = Get-HelperPath -Helper Git
if ($WorkingDirectory) {
$ArgumentList = @('-C', $WorkingDirectory) + $ArgumentList
}
if([String]::IsNullOrEmpty($proxy) -or $proxy -eq 'none') {
return & $git @ArgumentList
}
if($ArgumentList -Match '\b(clone|checkout|pull|fetch|ls-remote)\b') {
$j = Start-Job -ScriptBlock {
# convert proxy setting for git
$proxy = $using:proxy
if ($proxy -and $proxy.StartsWith('currentuser@')) {
$proxy = $proxy.Replace('currentuser@', ':@')
}
$env:HTTPS_PROXY = $proxy
$env:HTTP_PROXY = $proxy
& $using:git @using:ArgumentList
}
$o = $j | Receive-Job -Wait -AutoRemoveJob
return $o
}
return & $git @ArgumentList
}
function Invoke-GitLog {
[CmdletBinding()]
Param (
[Parameter(Mandatory, ValueFromPipeline)]
[String]$Path,
[Parameter(Mandatory, ValueFromPipeline)]
[String]$CommitHash,
[String]$Name = ''
)
Process {
if ($Name) {
if ($Name.Length -gt 12) {
$Name = "$($Name.Substring(0, 10)).."
}
$Name = "%Cgreen$($Name.PadRight(12, ' ').Substring(0, 12))%Creset "
}
Invoke-Git -Path $Path -ArgumentList @('--no-pager', 'log', '--color', '--no-decorate', "--grep='^(chore)'", '--invert-grep', '--abbrev=12', "--format=tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s $Name%C(cyan)%cr%Creset", "$CommitHash..HEAD")
}
cmd.exe /d /c $cmd
}
# helper functions
@@ -200,7 +374,7 @@ function filesize($length) {
} else {
if ($null -eq $length) {
$length = 0
}
}
"$($length) B"
}
}
@@ -209,6 +383,7 @@ function filesize($length) {
function basedir($global) { if($global) { return $globaldir } $scoopdir }
function appsdir($global) { "$(basedir $global)\apps" }
function shimdir($global) { "$(basedir $global)\shims" }
function modulesdir($global) { "$(basedir $global)\modules" }
function appdir($app, $global) { "$(appsdir $global)\$app" }
function versiondir($app, $version, $global) { "$(appdir $app $global)\$version" }
@@ -293,12 +468,16 @@ Function Test-CommandAvailable {
Return [Boolean](Get-Command $Name -ErrorAction Ignore)
}
Function Test-GitAvailable {
return [Boolean](Get-HelperPath -Helper Git)
}
function Get-HelperPath {
[CmdletBinding()]
[OutputType([String])]
param(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[ValidateSet('7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2', 'Zstd')]
[ValidateSet('Git', '7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2', 'Zstd')]
[String]
$Helper
)
@@ -307,18 +486,21 @@ function Get-HelperPath {
}
process {
switch ($Helper) {
'7zip' {
$HelperPath = Get-AppFilePath '7zip' '7z.exe'
if ([String]::IsNullOrEmpty($HelperPath)) {
$HelperPath = Get-AppFilePath '7zip-zstd' '7z.exe'
'Git' {
$internalgit = (Get-AppFilePath 'git' 'mingw64\bin\git.exe'), (Get-AppFilePath 'git' 'mingw32\bin\git.exe') | Where-Object { $_ -ne $null }
if ($internalgit) {
$HelperPath = $internalgit
} else {
$HelperPath = (Get-Command git -ErrorAction Ignore).Source
}
}
'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' }
@@ -339,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 {
@@ -349,7 +531,10 @@ function Get-CommandPath {
} catch {
return $null
}
$commandPath = if ($comm.Path -like "$userShims*" -or $comm.Path -like "$globalShims*") {
$commandPath = if ($comm.Path -like "$userShims\scoop-*.ps1") {
# Scoop aliases
$comm.Source
} elseif ($comm.Path -like "$userShims*" -or $comm.Path -like "$globalShims*") {
Get-ShimTarget ($comm.Path -replace '\.exe$', '.shim')
} elseif ($comm.CommandType -eq 'Application') {
$comm.Source
@@ -457,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
@@ -481,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]
@@ -494,6 +708,9 @@ function Invoke-ExternalCommand {
[Parameter(ParameterSetName = "UseShellExecute")]
[Switch]
$RunAs,
[Parameter(ParameterSetName = "UseShellExecute")]
[Switch]
$Quiet,
[Alias("Msg")]
[String]
$Activity,
@@ -523,29 +740,35 @@ function Invoke-ExternalCommand {
if ($RunAs) {
$Process.StartInfo.UseShellExecute = $true
$Process.StartInfo.Verb = 'RunAs'
} else {
$Process.StartInfo.CreateNoWindow = $true
}
if ($FilePath -match '^((cmd|cscript|wscript|msiexec)(\.exe)?|.*\.(bat|cmd|js|vbs|wsf))$') {
$Process.StartInfo.Arguments = $ArgumentList -join ' '
} elseif ($Process.StartInfo.ArgumentList.Add) {
# 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
$ArgumentList | ForEach-Object { $Process.StartInfo.ArgumentList.Add($_) }
} 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`""
if ($Quiet) {
$Process.StartInfo.UseShellExecute = $true
$Process.StartInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
}
if ($ArgumentList.Length -gt 0) {
$ArgumentList = $ArgumentList | ForEach-Object { [regex]::Split($_.Replace('"', ''), '(?<=(?<![:\w])[/-]\w+) | (?=[/-])') }
if ($FilePath -match '^((cmd|cscript|wscript|msiexec)(\.exe)?|.*\.(bat|cmd|js|vbs|wsf))$') {
$Process.StartInfo.Arguments = $ArgumentList -join ' '
} elseif ($Process.StartInfo.ArgumentList.Add) {
# 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
$ArgumentList | ForEach-Object { $Process.StartInfo.ArgumentList.Add($_) }
} 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 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 ' '
}
$Process.StartInfo.Arguments = $escapedArgs -join ' '
}
try {
[void]$Process.Start()
@@ -588,12 +811,6 @@ function Invoke-ExternalCommand {
return $true
}
function env($name,$global,$val='__get') {
$target = 'User'; if($global) {$target = 'Machine'}
if($val -eq '__get') { [environment]::getEnvironmentVariable($name,$target) }
else { [environment]::setEnvironmentVariable($name,$val,$target) }
}
function isFileLocked([string]$path) {
$file = New-Object System.IO.FileInfo $path
@@ -675,7 +892,7 @@ function Get-ShimTarget($ShimPath) {
if (!$shimTarget) {
$shimTarget = ((Select-String -Path $ShimPath -Pattern '[''"]([^@&]*?)[''"]' -AllMatches).Matches.Groups | Select-Object -Last 1).Value
}
$shimTarget | Convert-Path
$shimTarget | Convert-Path -ErrorAction SilentlyContinue
}
}
@@ -701,16 +918,16 @@ 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())"
# convert to relative path
Push-Location $abs_shimdir
$relative_path = Resolve-Path -Relative $path
Pop-Location
$resolved_path = Convert-Path $path
Push-Location $abs_shimdir
$relative_path = Resolve-Path -Relative $resolved_path
Pop-Location
if ($path -match '\.(exe|com)$') {
# for programs with no awareness of any shell
@@ -720,6 +937,12 @@ function shim($path, $global, $name, $arg) {
if ($arg) {
Write-Output "args = $arg" | Out-UTF8File "$shim.shim" -Append
}
$target_subsystem = Get-PESubsystem $resolved_path
if ($target_subsystem -eq 2) { # we only want to make shims GUI
Write-Output "Making $shim.exe a GUI binary."
Set-PESubsystem "$shim.exe" $target_subsystem | Out-Null
}
} elseif ($path -match '\.(bat|cmd)$') {
# shim .bat, .cmd so they can be used by programs with no awareness of PSH
warn_on_overwrite "$shim.cmd" $path
@@ -781,6 +1004,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"
@@ -788,6 +1012,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$') {
@@ -805,14 +1035,9 @@ function shim($path, $global, $name, $arg) {
) -join "`n" | Out-UTF8File $shim -NoNewLine
} else {
warn_on_overwrite "$shim.cmd" $path
# find path to Git's bash so that batch scripts can run bash scripts
$gitdir = (Get-Item (Get-CommandPath git) -ErrorAction:Stop).Directory.Parent
if ($gitdir.FullName -imatch 'mingw') {
$gitdir = $gitdir.Parent
}
@(
"@rem $resolved_path",
"@`"$(Join-Path (Join-Path $gitdir.FullName 'bin') 'bash.exe')`" `"$resolved_path`" $arg %*"
"@bash `"$resolved_path`" $arg %*"
) -join "`r`n" | Out-UTF8File "$shim.cmd"
warn_on_overwrite $shim $path
@@ -825,38 +1050,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)}) {
@@ -931,45 +1135,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
@@ -1218,50 +1383,50 @@ Optimize-SecurityProtocol
# Load Scoop config
$configHome = $env:XDG_CONFIG_HOME, "$env:USERPROFILE\.config" | Select-Object -First 1
$configFile = "$configHome\scoop\config.json"
$scoopConfig = load_cfg $configFile
# NOTE Scoop config file migration. Remove this after 2023/6/30
if ($scoopConfig) {
$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
if ($_.Key -eq 'lastUpdate') {
$scoopConfigChg = $true
}
}
}
if ($scoopConfigChg) { # Only save config file if there was a change
ConvertTo-Json $scoopConfig | Out-UTF8File -FilePath $configFile
# Check if it's the expected install path for scoop: <root>/apps/scoop/current
$coreRoot = Split-Path $PSScriptRoot
$pathExpected = ($coreRoot -replace '\\','/') -like '*apps/scoop/current*'
if ($pathExpected) {
# Portable config is located in root directory:
# .\current\scoop\apps\<root>\config.json <- a reversed path
# Imagine `<root>/apps/scoop/current/` in a reversed format,
# and the directory tree:
#
# ```
# <root>:
# ├─apps
# ├─buckets
# ├─cache
# ├─persist
# ├─shims
# ├─config.json
# ```
$configPortablePath = Get-AbsolutePath "$coreRoot\..\..\..\config.json"
if (Test-Path $configPortablePath) {
$configFile = $configPortablePath
}
}
# END NOTE
$scoopConfig = load_cfg $configFile
# Scoop root directory
$scoopdir = $env:SCOOP, (get_config ROOT_PATH), "$env: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), "$env:ProgramData\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

@@ -28,7 +28,8 @@ function Expand-7zipArchive {
$7zPath = Get-HelperPath -Helper 7zip
}
$LogPath = "$(Split-Path $Path)\7zip.log"
$ArgList = @('x', $Path, "-o$DestinationPath", '-y')
$DestinationPath = $DestinationPath.TrimEnd('\')
$ArgList = @('x', $Path, "-o$DestinationPath", '-xr!*.nsis', '-y')
$IsTar = ((strip_ext $Path) -match '\.tar$') -or ($Path -match '\.t[abgpx]z2?$')
if (!$IsTar -and $ExtractDir) {
$ArgList += "-ir!$ExtractDir\*"
@@ -45,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
@@ -62,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
@@ -71,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
}
}
@@ -111,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 {
@@ -240,7 +245,14 @@ function Expand-ZipArchive {
$OriDestinationPath = $DestinationPath
$DestinationPath = "$DestinationPath\_tmp"
}
Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force
# Disable progress bar to gain performance
$oldProgressPreference = $ProgressPreference
$global:ProgressPreference = 'SilentlyContinue'
# Compatible with Pscx v3 (https://github.com/Pscx/Pscx) ('Microsoft.PowerShell.Archive' is not needed for Pscx v4)
Microsoft.PowerShell.Archive\Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force
$global:ProgressPreference = $oldProgressPreference
if ($ExtractDir) {
movedir "$DestinationPath\$ExtractDir" $OriDestinationPath | Out-Null
Remove-Item $DestinationPath -Recurse -Force
@@ -267,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

@@ -53,3 +53,17 @@ function check_long_paths {
return $true
}
function Get-WindowsDeveloperModeStatus {
$DevModRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
if (!(Test-Path -Path $DevModRegistryPath) -or (Get-ItemProperty -Path `
$DevModRegistryPath -Name AllowDevelopmentWithoutDevLicense -ErrorAction `
SilentlyContinue).AllowDevelopmentWithoutDevLicense -ne 1) {
warn "Windows Developer Mode is not enabled. Operations relevant to symlinks may fail without proper rights."
Write-Host " You may read more about the symlinks support here:"
Write-Host " https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/"
return $false
}
return $true
}

View File

@@ -13,7 +13,7 @@
# following arguments are treated as non-option arguments, even if
# they begin with a hyphen. The "--" itself will not be included in
# the returned $opts. (POSIX-compatible)
function getopt($argv, $shortopts, $longopts) {
function getopt([String[]]$argv, [String]$shortopts, [String[]]$longopts) {
$opts = @{}; $rem = @()
function err($msg) {
@@ -24,10 +24,6 @@ function getopt($argv, $shortopts, $longopts) {
return [Regex]::Escape($str)
}
# ensure these are arrays
$argv = @($argv)
$longopts = @($longopts)
for ($i = 0; $i -lt $argv.Length; $i++) {
$arg = $argv[$i]
if ($null -eq $arg) { continue }
@@ -81,6 +77,5 @@ function getopt($argv, $shortopts, $longopts) {
$rem += $arg
}
}
$opts, $rem
}

File diff suppressed because it is too large Load Diff

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

@@ -1,22 +1,17 @@
$modulesdir = "$scoopdir\modules"
function install_psmodule($manifest, $dir, $global) {
$psmodule = $manifest.psmodule
if (!$psmodule) { return }
if ($global) {
abort 'Installing PowerShell modules globally is not implemented!'
}
$targetdir = ensure (modulesdir $global)
$modulesdir = ensure $modulesdir
ensure_in_psmodulepath $modulesdir $global
ensure_in_psmodulepath $targetdir $global
$module_name = $psmodule.name
if (!$module_name) {
abort "Invalid manifest: The 'name' property is missing from 'psmodule'."
}
$linkfrom = "$modulesdir\$module_name"
$linkfrom = "$targetdir\$module_name"
Write-Host "Installing PowerShell module '$module_name'"
Write-Host "Linking $(friendly_path $linkfrom) => $(friendly_path $dir)"
@@ -36,7 +31,9 @@ function uninstall_psmodule($manifest, $dir, $global) {
$module_name = $psmodule.name
Write-Host "Uninstalling PowerShell module '$module_name'."
$linkfrom = "$modulesdir\$module_name"
$targetdir = modulesdir $global
$linkfrom = "$targetdir\$module_name"
if (Test-Path $linkfrom) {
Write-Host "Removing $(friendly_path $linkfrom)"
$linkfrom = Convert-Path $linkfrom
@@ -45,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
}
}

View File

@@ -5,16 +5,16 @@ function create_startmenu_shortcuts($manifest, $dir, $global, $arch) {
$target = [System.IO.Path]::Combine($dir, $_.item(0))
$target = New-Object System.IO.FileInfo($target)
$name = $_.item(1)
$arguments = ""
$arguments = ''
$icon = $null
if($_.length -ge 3) {
if ($_.length -ge 3) {
$arguments = $_.item(2)
}
if($_.length -ge 4) {
if ($_.length -ge 4) {
$icon = [System.IO.Path]::Combine($dir, $_.item(3))
$icon = New-Object System.IO.FileInfo($icon)
}
$arguments = (substitute $arguments @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir})
$arguments = (substitute $arguments @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir })
startmenu_shortcut $target $name $arguments $icon $global
}
}
@@ -29,11 +29,11 @@ function shortcut_folder($global) {
}
function startmenu_shortcut([System.IO.FileInfo] $target, $shortcutName, $arguments, [System.IO.FileInfo]$icon, $global) {
if(!$target.Exists) {
if (!$target.Exists) {
Write-Host -f DarkRed "Creating shortcut for $shortcutName ($(fname $target)) failed: Couldn't find $target"
return
}
if($icon -and !$icon.Exists) {
if ($icon -and !$icon.Exists) {
Write-Host -f DarkRed "Creating shortcut for $shortcutName ($(fname $target)) failed: Couldn't find icon $icon"
return
}
@@ -51,11 +51,11 @@ function startmenu_shortcut([System.IO.FileInfo] $target, $shortcutName, $argume
if ($arguments) {
$wsShell.Arguments = $arguments
}
if($icon -and $icon.Exists) {
if ($icon -and $icon.Exists) {
$wsShell.IconLocation = $icon.FullName
}
$wsShell.Save()
write-host "Creating shortcut for $shortcutName ($(fname $target))"
Write-Host "Creating shortcut for $shortcutName ($(fname $target))"
}
# Removes the Startmenu shortcut if it exists
@@ -63,10 +63,10 @@ function rm_startmenu_shortcuts($manifest, $global, $arch) {
$shortcuts = @(arch_specific 'shortcuts' $manifest $arch)
$shortcuts | Where-Object { $_ -ne $null } | ForEach-Object {
$name = $_.item(1)
$shortcut = "$(shortcut_folder $global)\$name.lnk"
write-host "Removing shortcut $(friendly_path $shortcut)"
if(Test-Path -Path $shortcut) {
Remove-Item $shortcut
$shortcut = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$(shortcut_folder $global)\$name.lnk")
Write-Host "Removing shortcut $(friendly_path $shortcut)"
if (Test-Path -Path $shortcut) {
Remove-Item $shortcut
}
}
}

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

@@ -1,45 +0,0 @@
# Note: This file is for overwriting global variables and functions to make
# them unix compatible. It has to be imported after everything else!
function is_unix() { $PSVersionTable.Platform -eq 'Unix' }
function is_mac() { $PSVersionTable.OS.ToLower().StartsWith('darwin') }
function is_linux() { $PSVersionTable.OS.ToLower().StartsWith('linux') }
if(!(is_unix)) {
return # get the hell outta here
}
# core.ps1
$scoopdir = $env:SCOOP, (get_config ROOT_PATH), (Join-Path $env:HOME 'scoop') | Select-Object -First 1
$globaldir = $env:SCOOP_GLOBAL, (get_config 'GLOBAL_PATH'), '/usr/local/scoop' | Select-Object -First 1
$cachedir = $env:SCOOP_CACHE, (get_config 'CACHE_PATH'), (Join-Path $scoopdir 'cache') | Select-Object -First 1
# core.ps1
function ensure($dir) {
mkdir -p $dir > $null
return Convert-Path $dir
}
# install.ps1
function compute_hash($file, $algname) {
if(is_mac) {
switch ($algname)
{
"md5" { $result = (md5 -q $file) }
"sha1" { $result = (shasum -ba 1 $file) }
"sha256" { $result = (shasum -ba 256 $file) }
"sha512" { $result = (shasum -ba 512 $file) }
default { $result = (shasum -ba 256 $file) }
}
} else {
switch ($algname)
{
"md5" { $result = (md5sum -b $file) }
"sha1" { $result = (sha1sum -b $file) }
"sha256" { $result = (sha256sum -b $file) }
"sha512" { $result = (sha512sum -b $file) }
default { $result = (sha256sum -b $file) }
}
}
return $result.split(' ') | Select-Object -first 1
}

View File

@@ -147,9 +147,20 @@ function Compare-Version {
$splitReferenceVersion = @(SplitVersion -Version $ReferenceVersion -Delimiter $Delimiter)
$splitDifferenceVersion = @(SplitVersion -Version $DifferenceVersion -Delimiter $Delimiter)
# Nightly versions are always equal
# Nightly versions are always equal unless UPDATE_NIGHTLY is $true
if ($splitReferenceVersion[0] -eq 'nightly' -and $splitDifferenceVersion[0] -eq 'nightly') {
return 0
if (get_config UPDATE_NIGHTLY) {
# nightly versions will be compared by date if UPDATE_NIGHTLY is $true
if ($null -eq $splitReferenceVersion[1]) {
$splitReferenceVersion += Get-Date -Format 'yyyyMMdd'
}
if ($null -eq $splitDifferenceVersion[1]) {
$splitDifferenceVersion += Get-Date -Format 'yyyyMMdd'
}
return [Math]::Sign($splitDifferenceVersion[1] - $splitReferenceVersion[1])
} else {
return 0
}
}
for ($i = 0; $i -lt [Math]::Max($splitReferenceVersion.Length, $splitDifferenceVersion.Length); $i++) {

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

@@ -1,5 +1,8 @@
# Usage: scoop cat <app>
# Summary: Show content of specified manifest. If available, `bat` will be used to pretty-print the JSON.
# Summary: Show content of specified manifest.
# Help: Show content of specified manifest.
# If configured, `bat` will be used to pretty-print the JSON.
# See `cat_style` in `scoop help config` for further information.
param($app)
@@ -11,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

@@ -10,26 +10,27 @@ $defenderIssues = 0
$adminPrivileges = ([System.Security.Principal.WindowsPrincipal] [System.Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
if ($adminPrivileges) {
if ($adminPrivileges -and $env:USERNAME -ne 'WDAGUtilityAccount') {
$defenderIssues += !(check_windows_defender $false)
$defenderIssues += !(check_windows_defender $true)
}
$issues += !(check_main_bucket)
$issues += !(check_long_paths)
$issues += !(Get-WindowsDeveloperModeStatus)
if (!(Test-HelperInstalled -Helper 7zip)) {
error "'7-Zip' is not installed! It's required for unpacking most programs. Please Run 'scoop install 7zip' or 'scoop install 7zip-zstd'."
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'."
$issues++
}
if (!(Test-HelperInstalled -Helper Innounp)) {
error "'Inno Setup Unpacker' is not installed! It's required for unpacking InnoSetup files. Please run 'scoop install innounp'."
warn "'Inno Setup Unpacker' is not installed! It's required for unpacking InnoSetup files. Please run 'scoop install innounp'."
$issues++
}
if (!(Test-HelperInstalled -Helper Dark)) {
error "'dark' is not installed! It's required for unpacking installers created with the WiX Toolset. Please run 'scoop install dark' or 'scoop install wixtoolset'."
warn "'dark' is not installed! It's required for unpacking installers created with the WiX Toolset. Please run 'scoop install dark' or 'scoop install wixtoolset'."
$issues++
}

View File

@@ -53,7 +53,7 @@
#
# default_architecture: 64bit|32bit|arm64
# Allow to configure preferred architecture for application installation.
# If not specified, architecture is determined be system.
# If not specified, architecture is determined by system.
#
# debug: $true|$false
# Additional and detailed output will be shown.
@@ -111,6 +111,15 @@
# Should be in the format 'YYYY-MM-DD', 'YYYY/MM/DD' or any other forms that accepted by '[System.DateTime]::Parse()'.
# Ref: https://docs.microsoft.com/dotnet/api/system.datetime.parse?view=netframework-4.5#StringToParse
#
# update_nightly: $true|$false
# 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
# -------------------
#
@@ -147,35 +156,21 @@ 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"
} else {
$value
if ($value -is [System.DateTime]) {
$value.ToString('o')
} else {
$value
}
}
}

View File

@@ -59,7 +59,7 @@ function choose_item($list, $query) {
}
if (!$url) {
scoop help create
& "$PSScriptRoot\scoop-help.ps1" create
} else {
create_manifest $url
}

View File

@@ -43,7 +43,7 @@ if (is_scoop_outdated) {
if ($opt.u -or $opt.'no-update-scoop') {
warn "Scoop is out of date."
} else {
scoop update
& "$PSScriptRoot\scoop-update.ps1"
}
}
@@ -57,7 +57,7 @@ foreach ($curr_app in $apps) {
$app, $bucket, $version = parse_app $curr_app
$app, $manifest, $bucket, $url = Get-Manifest "$bucket/$app"
info "Starting download for $app..."
info "Downloading '$app'$(if ($version) { " ($version)" }) [$architecture]$(if ($bucket) { " from $bucket bucket" })"
# Generate manifest if there is different version in manifest
if (($null -ne $version) -and ($manifest.version -ne $version)) {
@@ -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
@@ -85,7 +85,7 @@ foreach ($curr_app in $apps) {
$curr_check_hash = $check_hash
if ($version -eq 'nightly') {
$version = nightly_version $(get-date)
$version = nightly_version
$curr_check_hash = $false
}

View File

@@ -47,15 +47,23 @@ $apps | ForEach-Object {
return
}
if (get_config NO_JUNCTION){
if (get_config NO_JUNCTION) {
$version = Select-CurrentVersion -App $app -Global:$global
} else {
$version = 'current'
}
$dir = versiondir $app $version $global
$json = install_info $app $version $global
if (!$json) {
error "Failed to hold '$app'."
continue
}
$install = @{}
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }
if ($install.hold) {
info "'$app' is already held."
continue
}
$install.hold = $true
save_install_info $install $dir
success "$app is now held and can not be updated anymore."

View File

@@ -34,18 +34,19 @@ foreach ($item in $import.buckets) {
}
foreach ($item in $import.apps) {
$instArgs = @()
$holdArgs = @()
$info = $item.Info -Split ', '
$global = if ('Global install' -in $info) {
' --global'
} else {
''
if ('Global install' -in $info) {
$instArgs += '--global'
$holdArgs += '--global'
}
$arch = if ('64bit' -in $info) {
' --arch 64bit'
} elseif ('32bit' -in $info) {
' --arch 32bit'
} else {
' --arch arm64'
if ('64bit' -in $info -and '64bit' -ne $def_arch) {
$instArgs += '--arch', '64bit'
} elseif ('32bit' -in $info -and '32bit' -ne $def_arch) {
$instArgs += '--arch', '32bit'
} elseif ('arm64' -in $info -and 'arm64' -ne $def_arch) {
$instArgs += '--arch', 'arm64'
}
$app = if ($item.Source -in $bucket_names) {
@@ -56,9 +57,9 @@ foreach ($item in $import.apps) {
$item.Source
}
& "$PSScriptRoot\scoop-install.ps1" $app$global$arch
& "$PSScriptRoot\scoop-install.ps1" $app @instArgs
if ('Held package' -in $info) {
& "$PSScriptRoot\scoop-hold.ps1" $($item.Name)$global
& "$PSScriptRoot\scoop-hold.ps1" $item.Name @holdArgs
}
}

View File

@@ -1,7 +1,7 @@
# Usage: scoop info <app> [--verbose]
# Usage: scoop info <app> [options]
# Summary: Display information about an app
# Options:
# -v, --verbose Show full paths and URLs
# Help: Options:
# -v, --verbose Show full paths and URLs
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
@@ -84,7 +84,7 @@ if ($manifest.depends) {
if (Test-Path $manifest_file) {
if (Get-Command git -ErrorAction Ignore) {
$gitinfo = (git -C (Split-Path $manifest_file) log -1 -s --format='%aD#%an' $manifest_file 2> $null) -Split '#'
$gitinfo = (Invoke-Git -Path (Split-Path $manifest_file) -ArgumentList @('log', '-1', '-s', '--format=%aD#%an', $manifest_file) 2> $null) -Split '#'
}
if ($gitinfo) {
$item.'Updated at' = $gitinfo[0] | Get-Date
@@ -112,7 +112,7 @@ if ($status.installed) {
# Collect file list from each location
$appFiles = Get-ChildItem $appsdir -Filter $app
$currentFiles = Get-ChildItem $appFiles -Filter (Select-CurrentVersion $app $global)
$currentFiles = Get-ChildItem $appFiles.FullName -Filter (Select-CurrentVersion $app $global)
$persistFiles = Get-ChildItem $persist_dir -ErrorAction Ignore # Will fail if app does not persist data
$cacheFiles = Get-ChildItem $cachedir -Filter "$app#*"
@@ -120,7 +120,7 @@ if ($status.installed) {
$fileTotals = @()
foreach ($fileType in ($appFiles, $currentFiles, $persistFiles, $cacheFiles)) {
if ($null -ne $fileType) {
$fileSum = (Get-ChildItem $fileType -Recurse | Measure-Object -Property Length -Sum).Sum
$fileSum = (Get-ChildItem $fileType.FullName -Recurse -File | Measure-Object -Property Length -Sum).Sum
$fileTotals += coalesce $fileSum 0
} else {
$fileTotals += 0
@@ -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"
@@ -56,7 +57,7 @@ if (is_scoop_outdated) {
if ($opt.u -or $opt.'no-update-scoop') {
warn "Scoop is out of date."
} else {
scoop update
& "$PSScriptRoot\scoop-update.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"
@@ -69,7 +70,7 @@ $apps | ForEach-Object {
#region Workaround for #2952
if (test_running_process $app $global) {
continue
return
}
#endregion Workaround for #2952
@@ -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

@@ -9,7 +9,7 @@ param($query)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'manifest'
. "$PSScriptRoot\..\lib\versions.ps1" # 'Get-LatestVersion'
$list = @()
$list = [System.Collections.Generic.List[PSCustomObject]]::new()
try {
$query = New-Object Regex $query, 'IgnoreCase'
@@ -32,24 +32,90 @@ function bin_match($manifest, $query) {
if ((strip_ext $fname) -match $query) { $fname }
elseif ($alias -match $query) { $alias }
}
if ($bins) { return $bins }
else { return $false }
}
function bin_match_json($json, $query) {
[System.Text.Json.JsonElement]$bin = [System.Text.Json.JsonElement]::new()
if (!$json.RootElement.TryGetProperty("bin", [ref] $bin)) { return $false }
$bins = @()
if($bin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($bin) -match $query) {
$bins += [System.IO.Path]::GetFileName($bin)
} elseif ($bin.ValueKind -eq [System.Text.Json.JsonValueKind]::Array) {
foreach($subbin in $bin.EnumerateArray()) {
if($subbin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($subbin) -match $query) {
$bins += [System.IO.Path]::GetFileName($subbin)
} elseif ($subbin.ValueKind -eq [System.Text.Json.JsonValueKind]::Array) {
if([System.IO.Path]::GetFileNameWithoutExtension($subbin[0]) -match $query) {
$bins += [System.IO.Path]::GetFileName($subbin[0])
} elseif ($subbin.GetArrayLength() -ge 2 -and $subbin[1] -match $query) {
$bins += $subbin[1]
}
}
}
}
if ($bins) { return $bins }
else { return $false }
}
function search_bucket($bucket, $query) {
$apps = apps_in_bucket (Find-BucketDirectory $bucket) | ForEach-Object { @{ name = $_ } }
$apps = Get-ChildItem (Find-BucketDirectory $bucket) -Filter '*.json' -Recurse
if ($query) {
$apps = $apps | Where-Object {
if ($_.name -match $query) { return $true }
$bin = bin_match (manifest $_.name $bucket) $query
$apps | ForEach-Object {
$json = [System.Text.Json.JsonDocument]::Parse([System.IO.File]::ReadAllText($_.FullName))
$name = $_.BaseName
if ($name -match $query) {
$list.Add([PSCustomObject]@{
Name = $name
Version = $json.RootElement.GetProperty("version")
Source = $bucket
Binaries = ""
})
} else {
$bin = bin_match_json $json $query
if ($bin) {
$_.bin = $bin
return $true
$list.Add([PSCustomObject]@{
Name = $name
Version = $json.RootElement.GetProperty("version")
Source = $bucket
Binaries = $bin -join ' | '
})
}
}
}
}
# fallback function for PowerShell 5
function search_bucket_legacy($bucket, $query) {
$apps = Get-ChildItem (Find-BucketDirectory $bucket) -Filter '*.json' -Recurse
$apps | ForEach-Object {
$manifest = [System.IO.File]::ReadAllText($_.FullName) | ConvertFrom-Json -ErrorAction Continue
$name = $_.BaseName
if ($name -match $query) {
$list.Add([PSCustomObject]@{
Name = $name
Version = $manifest.Version
Source = $bucket
Binaries = ""
})
} else {
$bin = bin_match $manifest $query
if ($bin) {
$list.Add([PSCustomObject]@{
Name = $name
Version = $manifest.Version
Source = $bucket
Binaries = $bin -join ' | '
})
}
}
}
$apps | ForEach-Object { $_.version = (Get-LatestVersion -AppName $_.name -Bucket $bucket); $_ }
}
function download_json($url) {
@@ -96,43 +162,35 @@ function search_remotes($query) {
(add them using 'scoop bucket add <bucket name>')"
}
$remote_list = @()
$results | ForEach-Object {
$name = $_.bucket
$bucket = $_.bucket
$_.results | ForEach-Object {
$item = [ordered]@{}
$item.Name = $_
$item.Source = $name
$list += [PSCustomObject]$item
$item.Source = $bucket
$remote_list += [PSCustomObject]$item
}
}
$list
$remote_list
}
$jsonTextAvailable = [System.AppDomain]::CurrentDomain.GetAssemblies() | Where-object { [System.IO.Path]::GetFileNameWithoutExtension($_.Location) -eq "System.Text.Json" }
Get-LocalBucket | ForEach-Object {
$res = search_bucket $_ $query
$local_results = $local_results -or $res
if ($res) {
$name = "$_"
$res | ForEach-Object {
$item = [ordered]@{}
$item.Name = $_.name
$item.Version = $_.version
$item.Source = $name
$item.Binaries = ""
if ($_.bin) { $item.Binaries = $_.bin -join ' | ' }
$list += [PSCustomObject]$item
}
if ($jsonTextAvailable) {
search_bucket $_ $query
} else {
search_bucket_legacy $_ $query
}
}
if ($list.Length -gt 0) {
if ($list.Count -gt 0) {
Write-Host "Results from local buckets..."
$list
}
if (!$local_results -and !(github_ratelimit_reached)) {
if ($list.Count -eq 0 -and !(github_ratelimit_reached)) {
$remote_results = search_remotes $query
if (!$remote_results) {
warn "No matches found."

View File

@@ -12,7 +12,7 @@
#
# To list all shims or matching shims, use the 'list' subcommand:
#
# scoop shim list [<shim_name>/<pattern>...]
# scoop shim list [<regex_pattern>...]
#
# To show a shim's information, use the 'info' subcommand:
#
@@ -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) {
@@ -82,7 +83,7 @@ function Get-ShimInfo($ShimPath) {
function Get-ShimPath($ShimName, $Global) {
'.shim', '.ps1' | ForEach-Object {
$shimPath = Join-Path (shimdir $Global) "$ShimName$_"
if (Test-Path $shimPath) {
if (Test-Path -LiteralPath $shimPath) {
return $shimPath
}
}
@@ -144,7 +145,7 @@ switch ($SubCommand) {
$other | ForEach-Object {
try {
$pattern = $_
[Regex]::New($pattern)
[void][Regex]::New($pattern)
} catch {
Write-Host "ERROR: Invalid pattern: " -ForegroundColor Red -NoNewline
Write-Host $pattern -ForegroundColor Magenta

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
@@ -21,10 +21,10 @@ if (!(Get-FormatData ScoopStatus)) {
function Test-UpdateStatus($repopath) {
if (Test-Path "$repopath\.git") {
git_cmd -C "`"$repopath`"" fetch -q origin
Invoke-Git -Path $repopath -ArgumentList @('fetch', '-q', 'origin')
$script:network_failure = 128 -eq $LASTEXITCODE
$branch = git -C $repopath branch --show-current
$commits = git -C $repopath log "HEAD..origin/$branch" --oneline
$branch = Invoke-Git -Path $repopath -ArgumentList @('branch', '--show-current')
$commits = Invoke-Git -Path $repopath -ArgumentList @('log', "HEAD..origin/$branch", '--oneline')
if ($commits) { return $true }
else { return $false }
} else {

View File

@@ -53,8 +53,16 @@ $apps | ForEach-Object {
}
$dir = versiondir $app $version $global
$json = install_info $app $version $global
if (!$json) {
error "Failed to unhold '$app'"
continue
}
$install = @{}
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }
if (!$install.hold) {
info "'$app' is not held."
continue
}
$install.hold = $null
save_install_info $install $dir
success "$app is no longer held and can be updated again."

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"
@@ -56,23 +57,27 @@ if(($PSVersionTable.PSVersion.Major) -lt 5) {
}
$show_update_log = get_config SHOW_UPDATE_LOG $true
function update_scoop($show_update_log) {
function Sync-Scoop {
[CmdletBinding()]
Param (
[Switch]$Log
)
# Test if Scoop Core is hold
if(Test-ScoopCoreOnHold) {
return
}
# check for git
if (!(Test-CommandAvailable git)) { abort "Scoop uses Git to update itself. Run 'scoop install git' and try again." }
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"
# get git scoop
git_cmd clone -q $configRepo --branch $configBranch --single-branch "`"$newdir`""
Invoke-Git -ArgumentList @('clone', '-q', $configRepo, '--branch', $configBranch, '--single-branch', $newdir)
# check if scoop was successful downloaded
if (!(Test-Path "$newdir\bin\scoop.ps1")) {
@@ -85,7 +90,7 @@ function update_scoop($show_update_log) {
Rename-Item $newdir 'current' -ErrorAction Stop
} catch {
Write-Warning $_
abort "Scoop update failed. Folder in use. Paste $newdir into $currentdir."
abort "Scoop update failed. Folder in use. Please rename folders $currentdir to ``old`` and $newdir to ``current``."
}
}
} else {
@@ -93,18 +98,18 @@ function update_scoop($show_update_log) {
Remove-Item "$currentdir\..\old" -Recurse -Force -ErrorAction SilentlyContinue
}
$previousCommit = git -C "$currentdir" rev-parse HEAD
$currentRepo = git -C "$currentdir" config remote.origin.url
$currentBranch = git -C "$currentdir" branch
$previousCommit = Invoke-Git -Path $currentdir -ArgumentList @('rev-parse', 'HEAD')
$currentRepo = Invoke-Git -Path $currentdir -ArgumentList @('config', 'remote.origin.url')
$currentBranch = Invoke-Git -Path $currentdir -ArgumentList @('branch')
$isRepoChanged = !($currentRepo -match $configRepo)
$isBranchChanged = !($currentBranch -match "\*\s+$configBranch")
# Stash uncommitted changes
if (git -C "$currentdir" diff HEAD --name-only) {
if (Invoke-Git -Path $currentdir -ArgumentList @('diff', 'HEAD', '--name-only')) {
if (get_config AUTOSTASH_ON_CONFLICT) {
warn "Uncommitted changes detected. Stashing..."
git -C "$currentdir" stash push -m "WIP at $([System.DateTime]::Now.ToString('o'))" -u -q
Invoke-Git -Path $currentdir -ArgumentList @('stash', 'push', '-m', "WIP at $([System.DateTime]::Now.ToString('o'))", '-u', '-q')
} else {
warn "Uncommitted changes detected. Update aborted."
return
@@ -113,26 +118,26 @@ function update_scoop($show_update_log) {
# Change remote url if the repo is changed
if ($isRepoChanged) {
git -C "$currentdir" config remote.origin.url "$configRepo"
Invoke-Git -Path $currentdir -ArgumentList @('config', 'remote.origin.url', $configRepo)
}
# Fetch and reset local repo if the repo or the branch is changed
if ($isRepoChanged -or $isBranchChanged) {
# Reset git fetch refs, so that it can fetch all branches (GH-3368)
git -C "$currentdir" config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
Invoke-Git -Path $currentdir -ArgumentList @('config', 'remote.origin.fetch', '+refs/heads/*:refs/remotes/origin/*')
# fetch remote branch
git_cmd -C "`"$currentdir`"" fetch --force origin "refs/heads/`"$configBranch`":refs/remotes/origin/$configBranch" -q
Invoke-Git -Path $currentdir -ArgumentList @('fetch', '--force', 'origin', "refs/heads/$configBranch`:refs/remotes/origin/$configBranch", '-q')
# checkout and track the branch
git_cmd -C "`"$currentdir`"" checkout -B $configBranch -t origin/$configBranch -q
Invoke-Git -Path $currentdir -ArgumentList @('checkout', '-B', $configBranch, '-t', "origin/$configBranch", '-q')
# reset branch HEAD
git -C "$currentdir" reset --hard origin/$configBranch -q
Invoke-Git -Path $currentdir -ArgumentList @('reset', '--hard', "origin/$configBranch", '-q')
} else {
git_cmd -C "`"$currentdir`"" pull -q
Invoke-Git -Path $currentdir -ArgumentList @('pull', '-q')
}
$res = $lastexitcode
if ($show_update_log) {
git -C "$currentdir" --no-pager log --no-decorate --grep='^(chore)' --invert-grep --format='tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset' "$previousCommit..HEAD"
if ($Log) {
Invoke-GitLog -Path $currentdir -CommitHash $previousCommit
}
if ($res -ne 0) {
@@ -140,47 +145,63 @@ function update_scoop($show_update_log) {
}
}
# This should have been deprecated after 2019-05-12
# if ((Get-LocalBucket) -notcontains 'main') {
# info "The main bucket of Scoop has been separated to 'https://github.com/ScoopInstaller/Main'"
# info "Adding main bucket..."
# add_bucket 'main'
# }
shim "$currentdir\bin\scoop.ps1" $false
}
function update_bucket($show_update_log) {
# check for git
if (!(Test-CommandAvailable git)) { abort "Scoop uses Git to update main bucket and others. Run 'scoop install git' and try again." }
function Sync-Bucket {
Param (
[Switch]$Log
)
Write-Host "Updating Buckets..."
foreach ($bucket in Get-LocalBucket) {
Write-Host "Updating '$bucket' bucket..."
$bucketLoc = Find-BucketDirectory $bucket -Root
if (!(Test-Path (Join-Path $bucketLoc '.git'))) {
if ($bucket -eq 'main') {
# Make sure main bucket, which was downloaded as zip, will be properly "converted" into git
Write-Host " Converting 'main' bucket to git repo..."
$status = rm_bucket 'main'
if ($status -ne 0) {
abort "Failed to remove local 'main' bucket."
}
$status = add_bucket 'main' (known_bucket_repo 'main')
if ($status -ne 0) {
abort "Failed to add remote 'main' bucket."
}
} else {
Write-Host "'$bucket' is not a git repository. Skipped."
}
continue
if (!(Test-Path (Join-Path (Find-BucketDirectory 'main' -Root) '.git'))) {
info "Converting 'main' bucket to git repo..."
$status = rm_bucket 'main'
if ($status -ne 0) {
abort "Failed to remove local 'main' bucket."
}
$status = add_bucket 'main' (known_bucket_repo 'main')
if ($status -ne 0) {
abort "Failed to add remote 'main' bucket."
}
}
$previousCommit = git -C "$bucketLoc" rev-parse HEAD
git_cmd -C "`"$bucketLoc`"" pull -q
if ($show_update_log) {
git -C "$bucketLoc" --no-pager log --no-decorate --grep='^(chore)' --invert-grep --format='tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset' "$previousCommit..HEAD"
$buckets = Get-LocalBucket | ForEach-Object {
$path = Find-BucketDirectory $_ -Root
return @{
name = $_
valid = Test-Path (Join-Path $path '.git')
path = $path
}
}
$buckets | Where-Object { !$_.valid } | ForEach-Object { Write-Host "'$($_.name)' is not a git repository. Skipped." }
if ($PSVersionTable.PSVersion.Major -ge 7) {
# Parallel parameter is available since PowerShell 7
$buckets | Where-Object { $_.valid } | ForEach-Object -ThrottleLimit 5 -Parallel {
. "$using:PSScriptRoot\..\lib\core.ps1"
$bucketLoc = $_.path
$name = $_.name
$previousCommit = Invoke-Git -Path $bucketLoc -ArgumentList @('rev-parse', 'HEAD')
Invoke-Git -Path $bucketLoc -ArgumentList @('pull', '-q')
if ($using:Log) {
Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit
}
}
} else {
$buckets | Where-Object { $_.valid } | ForEach-Object {
$bucketLoc = $_.path
$name = $_.name
$previousCommit = Invoke-Git -Path $bucketLoc -ArgumentList @('rev-parse', 'HEAD')
Invoke-Git -Path $bucketLoc -ArgumentList @('pull', '-q')
if ($Log) {
Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit
}
}
}
}
@@ -202,7 +223,7 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
$version = $manifest.version
$is_nightly = $version -eq 'nightly'
if ($is_nightly) {
$version = nightly_version $(get-date) $quiet
$version = nightly_version $quiet
$check_hash = $false
}
@@ -220,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
@@ -234,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) {
@@ -260,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")) {
@@ -321,8 +342,8 @@ if (-not ($apps -or $all)) {
error 'scoop update: --no-cache is invalid when <app> is not specified.'
exit 1
}
update_scoop $show_update_log
update_bucket $show_update_log
Sync-Scoop -Log:$show_update_log
Sync-Bucket -Log:$show_update_log
set_config LAST_UPDATE ([System.DateTime]::Now.ToString('o')) | Out-Null
success 'Scoop was updated successfully!'
} else {
@@ -336,8 +357,8 @@ if (-not ($apps -or $all)) {
$apps_param = $apps
if ($updateScoop) {
update_scoop $show_update_log
update_bucket $show_update_log
Sync-Scoop -Log:$show_update_log
Sync-Bucket -Log:$show_update_log
set_config LAST_UPDATE ([System.DateTime]::Now.ToString('o')) | Out-Null
success 'Scoop was updated successfully!'
}

View File

@@ -36,14 +36,14 @@
$opt, $apps, $err = getopt $args 'asnup' @('all', 'scan', 'no-depends', 'no-update-scoop', 'passthru')
if ($err) { "scoop virustotal: $err"; exit 1 }
if (!$apps) { my_usage; exit 1 }
if (!$apps -and -$all) { my_usage; exit 1 }
$architecture = Format-ArchitectureString
if (is_scoop_outdated) {
if ($opt.u -or $opt.'no-update-scoop') {
warn 'Scoop is out of date.'
} else {
scoop update
& "$PSScriptRoot\scoop-update.ps1"
}
}
@@ -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

@@ -4,7 +4,7 @@
param($command)
if (!$command) {
'ERROR: <command> missing'
error '<command> missing'
my_usage
exit 1
}
@@ -12,7 +12,7 @@ if (!$command) {
$path = Get-CommandPath $command
if ($null -eq $path) {
Write-Host "'$command' not found / not a scoop shim."
warn "'$command' not found, not a scoop shim, or a broken shim."
exit 2
} else {
friendly_path $path

View File

@@ -127,10 +127,6 @@
"installer": {
"$ref": "#/definitions/installer"
},
"msi": {
"$ref": "#/definitions/stringOrArrayOfStrings",
"description": "Deprecated"
},
"post_install": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
@@ -248,6 +244,15 @@
"hash": {
"$ref": "#/definitions/hashExtractionOrArrayOfHashExtractions"
},
"installer": {
"type": "object",
"additionalProperties": false,
"properties": {
"file": {
"type": "string"
}
}
},
"license": {
"$ref": "#/definitions/license"
},
@@ -266,6 +271,9 @@
}
}
},
"shortcuts": {
"$ref": "#/definitions/shortcutsArray"
},
"url": {
"$ref": "#/definitions/autoupdateUriOrArrayOfAutoupdateUris"
}
@@ -591,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 {
"$(compute_hash $_ 'sha256') *$($_.Name)" | Out-File "$PSScriptRoot\bin\checksum.sha256" -Append -Encoding oem
"$(compute_hash $_ 'sha512') *$($_.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"
}
@@ -21,7 +21,7 @@ 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 {
"$(compute_hash $_ 'sha256') *$($_.Name)" | Out-File "$PSScriptRoot\bin\checksum.sha256" -Append -Encoding oem
"$(compute_hash $_ 'sha512') *$($_.Name)" | Out-File "$PSScriptRoot\bin\checksum.sha512" -Append -Encoding oem
"$((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,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

@@ -1,74 +0,0 @@
$repo_dir = (Get-Item $MyInvocation.MyCommand.Path).Directory.Parent.FullName
$repo_files = @( Get-ChildItem $repo_dir -File -Recurse -Force )
$project_file_exclusions = @(
'[\\/]\.git[\\/]',
'\.sublime-workspace$',
'\.DS_Store$',
'supporting(\\|/)validator(\\|/)packages(\\|/)*',
'supporting(\\|/)shimexe(\\|/)packages(\\|/)*'
)
Describe 'Project code' {
$files = @(
$repo_files |
Where-Object { $_.fullname -inotmatch $($project_file_exclusions -join '|') } |
Where-Object { $_.fullname -imatch '.(ps1|psm1)$' }
)
$files_exist = ($files.Count -gt 0)
It $('PowerShell code files exist ({0} found)' -f $files.Count) -Skip:$(-not $files_exist) {
if (-not ($files.Count -gt 0)) {
throw 'No PowerShell code files were found'
}
}
function Test-PowerShellSyntax {
# ref: http://powershell.org/wp/forums/topic/how-to-check-syntax-of-scripts-automatically @@ https://archive.is/xtSv6
# originally created by Alexander Petrovskiy & Dave Wyatt
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string[]]
$Path
)
process {
foreach ($scriptPath in $Path) {
$contents = Get-Content -Path $scriptPath
if ($null -eq $contents) {
continue
}
$errors = $null
$null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors)
New-Object psobject -Property @{
Path = $scriptPath
SyntaxErrorsFound = ($errors.Count -gt 0)
}
}
}
}
It 'PowerShell code files do not contain syntax errors' -Skip:$(-not $files_exist) {
$badFiles = @(
foreach ($file in $files) {
if ( (Test-PowerShellSyntax $file.FullName).SyntaxErrorsFound ) {
$file.FullName
}
}
)
if ($badFiles.Count -gt 0) {
throw "The following files have syntax errors: `r`n`r`n$($badFiles -join "`r`n")"
}
}
}
. "$PSScriptRoot\Import-File-Tests.ps1"

View File

@@ -1,127 +1,52 @@
#Requires -Version 5.0
#Requires -Modules @{ ModuleName = 'Pester'; RequiredVersion = '4.10.1' }
#Requires -Version 5.1
#Requires -Modules @{ ModuleName = 'BuildHelpers'; ModuleVersion = '2.0.1' }
#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.2.0' }
param(
[ValidateNotNullOrEmpty()]
[String]
$repo_dir = (Split-Path -Path $MyInvocation.PSCommandPath -Parent)
[String] $BucketPath = $MyInvocation.PSScriptRoot
)
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\unix.ps1"
. "$PSScriptRoot\Scoop-00File.Tests.ps1" -TestPath $BucketPath
$bucketdir = $repo_dir
if (Test-Path("$repo_dir\..\bucket")) {
$bucketdir = "$repo_dir\..\bucket"
} elseif (Test-Path("$repo_dir\bucket")) {
$bucketdir = "$repo_dir\bucket"
}
# Tests for non manifest files
$repo_files = @(Get-ChildItem -Path $repo_dir -File -Recurse)
$project_file_exclusions = @(
'[\\/]\.git[\\/]',
'.sublime-workspace$',
'.DS_Store$'
)
. "$PSScriptRoot\Import-File-Tests.ps1"
# Tests for manifest files
Describe 'Manifest Validator' -Tag 'Validator' {
BeforeAll {
$schema = "$PSScriptRoot\..\schema.json"
$working_dir = setup_working 'manifest'
Add-Type -Path "$PSScriptRoot\..\supporting\validator\bin\Newtonsoft.Json.dll"
Add-Type -Path "$PSScriptRoot\..\supporting\validator\bin\Newtonsoft.Json.Schema.dll"
Add-Type -Path "$PSScriptRoot\..\supporting\validator\bin\Scoop.Validator.dll"
}
It 'Scoop.Validator is available' {
([System.Management.Automation.PSTypeName]'Scoop.Validator').Type | Should -Be 'Scoop.Validator'
}
Context 'parse_json function' {
It 'fails with invalid json' {
{ parse_json "$working_dir\broken_wget.json" } | Should -Throw
Describe 'Manifest validates against the schema' {
BeforeDiscovery {
$bucketDir = if (Test-Path "$BucketPath\bucket") {
"$BucketPath\bucket"
} else {
$BucketPath
}
}
Context 'schema validation' {
It 'fails with broken schema' {
$validator = New-Object Scoop.Validator("$working_dir\broken_schema.json", $true)
$validator.Validate("$working_dir\wget.json") | Should -BeFalse
$validator.Errors.Count | Should -Be 1
$validator.Errors | Select-Object -First 1 | Should -Match 'broken_schema.*(line 6).*(position 4)'
}
It 'fails with broken manifest' {
$validator = New-Object Scoop.Validator($schema, $true)
$validator.Validate("$working_dir\broken_wget.json") | Should -BeFalse
$validator.Errors.Count | Should -Be 1
$validator.Errors | Select-Object -First 1 | Should -Match 'broken_wget.*(line 5).*(position 4)'
}
It 'fails with invalid manifest' {
$validator = New-Object Scoop.Validator($schema, $true)
$validator.Validate("$working_dir\invalid_wget.json") | Should -BeFalse
$validator.Errors.Count | Should -Be 16
$validator.Errors | Select-Object -First 1 | Should -Match "Property 'randomproperty' has not been defined and the schema does not allow additional properties\."
$validator.Errors | Select-Object -Last 1 | Should -Match 'Required properties are missing from object: version\.'
}
}
}
Describe 'manifest validates against the schema' -Tag 'Manifests' {
BeforeAll {
$schema = "$PSScriptRoot\..\schema.json"
$changed_manifests = @()
if ($env:CI -eq $true) {
# AppVeyor
$commit = if ($env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT) { $env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT } else { $env:APPVEYOR_REPO_COMMIT }
# GitHub Actions
if ($env:GITHUB_SHA) {
$commit = $env:GITHUB_SHA
}
$changed_manifests = (Get-GitChangedFile -Path $repo_dir -Include '*.json' -Commit $commit)
Set-BuildEnvironment -Force
$manifestFiles = @(Get-GitChangedFile -Path $bucketDir -Include '*.json' -Commit $env:BHCommitHash)
} else {
$manifestFiles = (Get-ChildItem $bucketDir -Filter '*.json' -Recurse).FullName
}
$manifest_files = Get-ChildItem $bucketdir -Filter '*.json' -Recurse
$validator = New-Object Scoop.Validator($schema, $true)
}
$quota_exceeded = $false
$manifest_files | ForEach-Object {
$skip_manifest = ($changed_manifests -inotcontains $_.FullName)
if ($env:CI -ne $true -or $changed_manifests -imatch 'schema.json') {
$skip_manifest = $false
}
It "$_" -Skip:$skip_manifest {
BeforeAll {
Add-Type -Path "$PSScriptRoot\..\supporting\validator\bin\Scoop.Validator.dll"
# Could not use backslash '\' in Linux/macOS for .NET object 'Scoop.Validator'
$validator = New-Object Scoop.Validator("$PSScriptRoot/../schema.json", $true)
$global:quotaExceeded = $false
}
It '<_>' -TestCases $manifestFiles {
if ($global:quotaExceeded) {
Set-ItResult -Skipped -Because 'Schema validation limit exceeded.'
} else {
$file = $_ # exception handling may overwrite $_
if (!($quota_exceeded)) {
try {
$validator.Validate($file.fullname)
if ($validator.Errors.Count -gt 0) {
Write-Host -f red " [-] $_ has $($validator.Errors.Count) Error$(If($validator.Errors.Count -gt 1) { 's' })!"
Write-Host -f yellow $validator.ErrorsAsString
}
$validator.Errors.Count | Should -Be 0
} catch {
if ($_.exception.message -like '*The free-quota limit of 1000 schema validations per hour has been reached.*') {
$quota_exceeded = $true
Write-Host -f darkyellow 'Schema validation limit exceeded. Will skip further validations.'
} else {
throw
}
try {
$validator.Validate($file)
if ($validator.Errors.Count -gt 0) {
Write-Host " [-] $_ has $($validator.Errors.Count) Error$(If($validator.Errors.Count -gt 1) { 's' })!" -ForegroundColor Red
Write-Host $validator.ErrorsAsString -ForegroundColor Yellow
}
$validator.Errors.Count | Should -Be 0
} catch {
if ($_.Exception.Message -like '*The free-quota limit of 1000 schema validations per hour has been reached.*') {
$global:quotaExceeded = $true
Set-ItResult -Skipped -Because 'Schema validation limit exceeded.'
} else {
throw
}
}
$manifest = parse_json $file.fullname
$url = arch_specific 'url' $manifest '32bit'
$url64 = arch_specific 'url' $manifest '64bit'
if (!$url) {
$url = $url64
}
$url | Should -Not -BeNullOrEmpty
}
}
}

View File

@@ -1,134 +0,0 @@
if ([String]::IsNullOrEmpty($MyInvocation.PSScriptRoot)) {
Write-Error 'This script should not be called directly! It has to be imported from a buckets test file!'
exit 1
}
Describe 'Style constraints for non-binary project files' {
$files = @(
# gather all files except '*.exe', '*.zip', or any .git repository files
$repo_files |
Where-Object { $_.fullname -inotmatch $($project_file_exclusions -join '|') } |
Where-Object { $_.fullname -inotmatch '(.exe|.zip|.dll)$' } |
Where-Object { $_.fullname -inotmatch '(unformatted)' }
)
$files_exist = ($files.Count -gt 0)
It $('non-binary project files exist ({0} found)' -f $files.Count) -Skip:$(-not $files_exist) {
if (-not ($files.Count -gt 0)) {
throw 'No non-binary project were found'
}
}
It 'files do not contain leading UTF-8 BOM' -Skip:$(-not $files_exist) {
# UTF-8 BOM == 0xEF 0xBB 0xBF
# see http://www.powershellmagazine.com/2012/12/17/pscxtip-how-to-determine-the-byte-order-mark-of-a-text-file @@ https://archive.is/RgT42
# ref: http://poshcode.org/2153 @@ https://archive.is/sGnnu
$badFiles = @(
foreach ($file in $files) {
if ((Get-Command Get-Content).parameters.ContainsKey('AsByteStream')) {
# PowerShell Core (6.0+) '-Encoding byte' is replaced by '-AsByteStream'
$content = ([char[]](Get-Content $file.FullName -AsByteStream -TotalCount 3) -join '')
} else {
$content = ([char[]](Get-Content $file.FullName -Encoding byte -TotalCount 3) -join '')
}
if ([regex]::match($content, '(?ms)^\xEF\xBB\xBF').success) {
$file.FullName
}
}
)
if ($badFiles.Count -gt 0) {
throw "The following files have utf-8 BOM: `r`n`r`n$($badFiles -join "`r`n")"
}
}
It 'files end with a newline' -Skip:$(-not $files_exist) {
$badFiles = @(
foreach ($file in $files) {
# Ignore previous TestResults.xml
if ($file -match 'TestResults.xml') {
continue
}
$string = [System.IO.File]::ReadAllText($file.FullName)
if ($string.Length -gt 0 -and $string[-1] -ne "`n") {
$file.FullName
}
}
)
if ($badFiles.Count -gt 0) {
throw "The following files do not end with a newline: `r`n`r`n$($badFiles -join "`r`n")"
}
}
It 'file newlines are CRLF' -Skip:$(-not $files_exist) {
$badFiles = @(
foreach ($file in $files) {
$content = [System.IO.File]::ReadAllText($file.FullName)
if (!$content) {
throw "File contents are null: $($file.FullName)"
}
$lines = [regex]::split($content, '\r\n')
$lineCount = $lines.Count
for ($i = 0; $i -lt $lineCount; $i++) {
if ( [regex]::match($lines[$i], '\r|\n').success ) {
$file.FullName
break
}
}
}
)
if ($badFiles.Count -gt 0) {
throw "The following files have non-CRLF line endings: `r`n`r`n$($badFiles -join "`r`n")"
}
}
It 'files have no lines containing trailing whitespace' -Skip:$(-not $files_exist) {
$badLines = @(
foreach ($file in $files) {
# Ignore previous TestResults.xml
if ($file -match 'TestResults.xml') {
continue
}
$lines = [System.IO.File]::ReadAllLines($file.FullName)
$lineCount = $lines.Count
for ($i = 0; $i -lt $lineCount; $i++) {
if ($lines[$i] -match '\s+$') {
'File: {0}, Line: {1}' -f $file.FullName, ($i + 1)
}
}
}
)
if ($badLines.Count -gt 0) {
throw "The following $($badLines.Count) lines contain trailing whitespace: `r`n`r`n$($badLines -join "`r`n")"
}
}
It 'any leading whitespace consists only of spaces (excepting makefiles)' -Skip:$(-not $files_exist) {
$badLines = @(
foreach ($file in $files) {
if ($file.fullname -inotmatch '(^|.)makefile$') {
$lines = [System.IO.File]::ReadAllLines($file.FullName)
$lineCount = $lines.Count
for ($i = 0; $i -lt $lineCount; $i++) {
if ($lines[$i] -notmatch '^[ ]*(\S|$)') {
'File: {0}, Line: {1}' -f $file.FullName, ($i + 1)
}
}
}
}
)
if ($badLines.Count -gt 0) {
throw "The following $($badLines.Count) lines contain TABs within leading whitespace: `r`n`r`n$($badLines -join "`r`n")"
}
}
}

188
test/Scoop-00File.Tests.ps1 Normal file
View File

@@ -0,0 +1,188 @@
param(
[String] $TestPath = "$PSScriptRoot\.."
)
BeforeDiscovery {
$project_file_exclusions = @(
'[\\/]\.git[\\/]',
'\.sublime-workspace$',
'\.DS_Store$',
'supporting(\\|/)validator(\\|/)packages(\\|/)*'
)
$repo_files = (Get-ChildItem $TestPath -File -Recurse).FullName |
Where-Object { $_ -inotmatch $($project_file_exclusions -join '|') }
}
Describe 'Code Syntax' -ForEach @(, $repo_files) -Tag 'File' {
BeforeAll {
$files = @(
$_ | Where-Object { $_ -imatch '.(ps1|psm1)$' }
)
function Test-PowerShellSyntax {
# ref: http://powershell.org/wp/forums/topic/how-to-check-syntax-of-scripts-automatically @@ https://archive.is/xtSv6
# originally created by Alexander Petrovskiy & Dave Wyatt
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string[]]
$Path
)
process {
foreach ($scriptPath in $Path) {
$contents = Get-Content -Path $scriptPath
if ($null -eq $contents) {
continue
}
$errors = $null
$null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors)
New-Object psobject -Property @{
Path = $scriptPath
SyntaxErrorsFound = ($errors.Count -gt 0)
}
}
}
}
}
It 'PowerShell code files do not contain syntax errors' {
$badFiles = @(
foreach ($file in $files) {
if ( (Test-PowerShellSyntax $file).SyntaxErrorsFound ) {
$file
}
}
)
if ($badFiles.Count -gt 0) {
throw "The following files have syntax errors: `r`n`r`n$($badFiles -join "`r`n")"
}
}
}
Describe 'Style constraints for non-binary project files' -ForEach @(, $repo_files) -Tag 'File' {
BeforeAll {
$files = @(
# gather all files except '*.exe', '*.zip', or any .git repository files
$_ |
Where-Object { $_ -inotmatch '(.exe|.zip|.dll)$' } |
Where-Object { $_ -inotmatch '(unformatted)' }
)
}
It 'files do not contain leading UTF-8 BOM' {
# UTF-8 BOM == 0xEF 0xBB 0xBF
# see http://www.powershellmagazine.com/2012/12/17/pscxtip-how-to-determine-the-byte-order-mark-of-a-text-file @@ https://archive.is/RgT42
# ref: http://poshcode.org/2153 @@ https://archive.is/sGnnu
$badFiles = @(
foreach ($file in $files) {
if ((Get-Command Get-Content).parameters.ContainsKey('AsByteStream')) {
# PowerShell Core (6.0+) '-Encoding byte' is replaced by '-AsByteStream'
$content = ([char[]](Get-Content $file -AsByteStream -TotalCount 3) -join '')
} else {
$content = ([char[]](Get-Content $file -Encoding byte -TotalCount 3) -join '')
}
if ([regex]::match($content, '(?ms)^\xEF\xBB\xBF').success) {
$file
}
}
)
if ($badFiles.Count -gt 0) {
throw "The following files have utf-8 BOM: `r`n`r`n$($badFiles -join "`r`n")"
}
}
It 'files end with a newline' {
$badFiles = @(
foreach ($file in $files) {
# Ignore previous TestResults.xml
if ($file -match 'TestResults.xml') {
continue
}
$string = [System.IO.File]::ReadAllText($file)
if ($string.Length -gt 0 -and $string[-1] -ne "`n") {
$file
}
}
)
if ($badFiles.Count -gt 0) {
throw "The following files do not end with a newline: `r`n`r`n$($badFiles -join "`r`n")"
}
}
It 'file newlines are CRLF' {
$badFiles = @(
foreach ($file in $files) {
$content = [System.IO.File]::ReadAllText($file)
if (!$content) {
throw "File contents are null: $($file)"
}
$lines = [regex]::split($content, '\r\n')
$lineCount = $lines.Count
for ($i = 0; $i -lt $lineCount; $i++) {
if ( [regex]::match($lines[$i], '\r|\n').success ) {
$file
break
}
}
}
)
if ($badFiles.Count -gt 0) {
throw "The following files have non-CRLF line endings: `r`n`r`n$($badFiles -join "`r`n")"
}
}
It 'files have no lines containing trailing whitespace' {
$badLines = @(
foreach ($file in $files) {
# Ignore previous TestResults.xml
if ($file -match 'TestResults.xml') {
continue
}
$lines = [System.IO.File]::ReadAllLines($file)
$lineCount = $lines.Count
for ($i = 0; $i -lt $lineCount; $i++) {
if ($lines[$i] -match '\s+$') {
'File: {0}, Line: {1}' -f $file, ($i + 1)
}
}
}
)
if ($badLines.Count -gt 0) {
throw "The following $($badLines.Count) lines contain trailing whitespace: `r`n`r`n$($badLines -join "`r`n")"
}
}
It 'any leading whitespace consists only of spaces (excepting makefiles)' {
$badLines = @(
foreach ($file in $files) {
if ($file -inotmatch '(^|.)makefile$') {
$lines = [System.IO.File]::ReadAllLines($file)
$lineCount = $lines.Count
for ($i = 0; $i -lt $lineCount; $i++) {
if ($lines[$i] -notmatch '^[ ]*(\S|$)') {
'File: {0}, Line: {1}' -f $file, ($i + 1)
}
}
}
}
)
if ($badLines.Count -gt 0) {
throw "The following $($badLines.Count) lines contain TABs within leading whitespace: `r`n`r`n$($badLines -join "`r`n")"
}
}
}

View File

@@ -0,0 +1,33 @@
Describe 'PSScriptAnalyzer' -Tag 'Linter' {
BeforeDiscovery {
$scriptDir = @('.', 'bin', 'lib', 'libexec', 'test')
}
BeforeAll {
$lintSettings = "$PSScriptRoot\..\PSScriptAnalyzerSettings.psd1"
}
It 'PSScriptAnalyzerSettings.ps1 should exist' {
$lintSettings | Should -Exist
}
Context 'Linting all *.psd1, *.psm1 and *.ps1 files' {
BeforeEach {
$analysis = Invoke-ScriptAnalyzer -Path "$PSScriptRoot\..\$_" -Settings $lintSettings
}
It 'Should pass: <_>' -TestCases $scriptDir {
$analysis | Should -HaveCount 0
if ($analysis) {
foreach ($result in $analysis) {
switch -wildCard ($result.ScriptName) {
'*.psm1' { $type = 'Module' }
'*.ps1' { $type = 'Script' }
'*.psd1' { $type = 'Manifest' }
}
Write-Warning " [*] $($result.Severity): $($result.Message)"
Write-Warning " $($result.RuleName) in $type`: $directory\$($result.ScriptName):$($result.Line)"
}
}
}
}
}

View File

@@ -1,56 +1,46 @@
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\help.ps1"
. "$PSScriptRoot\..\libexec\scoop-alias.ps1" | Out-Null
Describe 'add_alias' -Tag 'Scoop' {
Mock shimdir { "$env:TEMP\shims" }
Mock set_config { }
Mock get_config { @{} }
$shimdir = shimdir
ensure $shimdir
Context "alias doesn't exist" {
It 'creates a new alias' {
$alias_file = "$shimdir\scoop-rm.ps1"
$alias_file | Should -Not -Exist
add_alias 'rm' '"hello, world!"'
& $alias_file | Should -Be 'hello, world!'
}
}
Context 'alias exists' {
It 'does not change existing alias' {
$alias_file = "$shimdir\scoop-rm.ps1"
New-Item $alias_file -Type File -Force
$alias_file | Should -Exist
add_alias 'rm' 'test'
$alias_file | Should -FileContentMatch ''
}
}
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\help.ps1"
. "$PSScriptRoot\..\libexec\scoop-alias.ps1" | Out-Null
}
Describe 'rm_alias' -Tag 'Scoop' {
Mock shimdir { "$env:TEMP\shims" }
Mock set_config { }
Mock get_config { @{} }
Describe 'Manipulate Alias' -Tag 'Scoop' {
BeforeAll {
Mock shimdir { "$TestDrive\shims" }
Mock set_config {}
Mock get_config { @{} }
$shimdir = shimdir
ensure $shimdir
$shimdir = shimdir
ensure $shimdir
}
Context 'alias exists' {
It 'removes an existing alias' {
$alias_file = "$shimdir\scoop-rm.ps1"
add_alias 'rm' '"hello, world!"'
It 'Creates a new alias if alias doesn''t exist' {
$alias_file = "$shimdir\scoop-rm.ps1"
$alias_file | Should -Not -Exist
$alias_file | Should -Exist
Mock get_config { @(@{'rm' = 'scoop-rm' }) }
add_alias 'rm' '"hello, world!"'
& $alias_file | Should -Be 'hello, world!'
}
rm_alias 'rm'
$alias_file | Should -Not -Exist
}
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"'
Should -Invoke -CommandName abort -Times 1 -ParameterFilter { $msg -eq "File 'scoop-rm.ps1' already exists in shims directory." }
}
It 'Removes an existing alias' {
$alias_file = "$shimdir\scoop-rm.ps1"
$alias_file | Should -Exist
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,17 +1,16 @@
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
}
Describe 'config' -Tag 'Scoop' {
BeforeAll {
$configFile = "$env:TEMP\ScoopTestFixtures\config.json"
if (Test-Path $configFile) {
Remove-Item -Path $configFile -Force
}
$configFile = [IO.Path]::GetTempFileName()
$unicode = [Regex]::Unescape('\u4f60\u597d\u3053\u3093\u306b\u3061\u306f') # 你好こんにちは
}
BeforeEach {
$scoopConfig = $null
AfterAll {
Remove-Item -Path $configFile -Force
}
It 'load_cfg should return null if config file does not exist' {

View File

@@ -1,10 +1,9 @@
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\unix.ps1"
$repo_dir = (Get-Item $MyInvocation.MyCommand.Path).directory.parent.FullName
$isUnix = is_unix
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\system.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
}
Describe 'Get-AppFilePath' -Tag 'Scoop' {
BeforeAll {
@@ -128,15 +127,14 @@ Describe 'is_directory' -Tag 'Scoop' {
}
}
Describe 'movedir' -Tag 'Scoop' {
$extract_dir = 'subdir'
$extract_to = $null
Describe 'movedir' -Tag 'Scoop', 'Windows' {
BeforeAll {
$working_dir = setup_working 'movedir'
$extract_dir = 'subdir'
$extract_to = $null
}
It 'moves directories with no spaces in path' -Skip:$isUnix {
It 'moves directories with no spaces in path' {
$dir = "$working_dir\user"
movedir "$dir\_tmp\$extract_dir" "$dir\$extract_to"
@@ -144,7 +142,7 @@ Describe 'movedir' -Tag 'Scoop' {
"$dir\_tmp\$extract_dir" | Should -Not -Exist
}
It 'moves directories with spaces in path' -Skip:$isUnix {
It 'moves directories with spaces in path' {
$dir = "$working_dir\user with space"
movedir "$dir\_tmp\$extract_dir" "$dir\$extract_to"
@@ -157,7 +155,7 @@ Describe 'movedir' -Tag 'Scoop' {
"$dir\_tmp" | Should -Not -Exist
}
It 'moves directories with quotes in path' -Skip:$isUnix {
It 'moves directories with quotes in path' {
$dir = "$working_dir\user with 'quote"
movedir "$dir\_tmp\$extract_dir" "$dir\$extract_to"
@@ -166,14 +164,14 @@ Describe 'movedir' -Tag 'Scoop' {
}
}
Describe 'shim' -Tag 'Scoop' {
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" -Skip:$isUnix {
It "links a file onto the user's path" {
{ Get-Command 'shim-test' -ea stop } | Should -Throw
{ Get-Command 'shim-test.ps1' -ea stop } | Should -Throw
{ Get-Command 'shim-test.cmd' -ea stop } | Should -Throw
@@ -186,15 +184,13 @@ Describe 'shim' -Tag 'Scoop' {
shim-test | Should -Be 'Hello, world!'
}
Context 'user with quote' {
It 'shims a file with quote in path' -Skip:$isUnix {
{ Get-Command 'shim-test' -ea stop } | Should -Throw
{ shim-test } | Should -Throw
It 'shims a file with quote in path' {
{ Get-Command 'shim-test' -ea stop } | Should -Throw
{ shim-test } | Should -Throw
shim "$working_dir\user with 'quote\shim-test.ps1" $false 'shim-test'
{ Get-Command 'shim-test' -ea stop } | Should -Not -Throw
shim-test | Should -Be 'Hello, world!'
}
shim "$working_dir\user with 'quote\shim-test.ps1" $false 'shim-test'
{ Get-Command 'shim-test' -ea stop } | Should -Not -Throw
shim-test | Should -Be 'Hello, world!'
}
AfterEach {
@@ -202,14 +198,14 @@ Describe 'shim' -Tag 'Scoop' {
}
}
Describe 'rm_shim' -Tag 'Scoop' {
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' -Skip:$isUnix {
It 'removes shim from path' {
shim "$working_dir\shim-test.ps1" $false 'shim-test'
rm_shim 'shim-test' $shimdir
@@ -221,19 +217,19 @@ Describe 'rm_shim' -Tag 'Scoop' {
}
}
Describe 'get_app_name_from_shim' -Tag 'Scoop' {
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 }
}
It 'returns empty string if file does not exist' -Skip:$isUnix {
It 'returns empty string if file does not exist' {
get_app_name_from_shim 'non-existent-file' | Should -Be ''
}
It 'returns app name if file exists and is a shim to an app' -Skip:$isUnix {
It 'returns app name if file exists and is a shim to an app' {
ensure "$working_dir/mockapp/current/"
Write-Output '' | Out-File "$working_dir/mockapp/current/mockapp1.ps1"
shim "$working_dir/mockapp/current/mockapp1.ps1" $false 'shim-test1'
@@ -246,7 +242,7 @@ Describe 'get_app_name_from_shim' -Tag 'Scoop' {
get_app_name_from_shim "$shim_path2" | Should -Be 'mockapp'
}
It 'returns empty string if file exists and is not a shim' -Skip:$isUnix {
It 'returns empty string if file exists and is not a shim' {
Write-Output 'lorem ipsum' | Out-File -Encoding ascii "$working_dir/mock-shim.ps1"
get_app_name_from_shim "$working_dir/mock-shim.ps1" | Should -Be ''
}
@@ -263,38 +259,6 @@ Describe 'get_app_name_from_shim' -Tag 'Scoop' {
}
}
Describe 'ensure_robocopy_in_path' -Tag 'Scoop' {
$shimdir = shimdir $false
Mock versiondir { $repo_dir }
Context 'robocopy is not in path' {
It 'shims robocopy when not on path' -Skip:$isUnix {
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
}
}
Context 'robocopy is in path' {
It 'does not shim robocopy when it is in path' -Skip:$isUnix {
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

@@ -1,14 +1,13 @@
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\decompress.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
. "$PSScriptRoot\..\lib\unix.ps1"
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\decompress.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
}
$isUnix = is_unix
Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' {
BeforeAll {
$working_dir = setup_working 'decompress'
@@ -19,13 +18,17 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
return $to
}
It 'Decompression test cases should exist' {
}
Context 'Decompression test cases should exist' {
BeforeAll {
$testcases = "$working_dir\TestCases.zip"
}
It 'Test cases should exist and hash should match' {
$testcases | Should -Exist
compute_hash $testcases 'sha256' | Should -Be '791bfce192917a2ff225dcdd87d23ae5f720b20178d85e68e4b1b56139cf8e6a'
if (!$isUnix) {
Microsoft.PowerShell.Archive\Expand-Archive $testcases $working_dir
}
(Get-FileHash -Path $testcases -Algorithm SHA256).Hash.ToLower() | Should -Be '791bfce192917a2ff225dcdd87d23ae5f720b20178d85e68e4b1b56139cf8e6a'
}
It 'Test cases should be extracted correctly' {
{ Microsoft.PowerShell.Archive\Expand-Archive -Path $testcases -DestinationPath $working_dir } | Should -Not -Throw
}
}
@@ -49,14 +52,14 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
$test6_3 = "$working_dir\7ZipTest6.part03.rar"
}
It 'extract normal compressed file' -Skip:$isUnix {
It 'extract normal compressed file' {
$to = test_extract 'Expand-7zipArchive' $test1
$to | Should -Exist
"$to\empty" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'extract nested compressed file' -Skip:$isUnix {
It 'extract nested compressed file' {
# file ext: tgz
$to = test_extract 'Expand-7zipArchive' $test2
$to | Should -Exist
@@ -70,28 +73,28 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
(Get-ChildItem $to).Count | Should -Be 1
}
It 'extract nested compressed file with different inner name' -Skip:$isUnix {
It 'extract nested compressed file with different inner name' {
$to = test_extract 'Expand-7zipArchive' $test4
$to | Should -Exist
"$to\empty" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'extract splited 7z archives (.001, .002, ...)' -Skip:$isUnix {
It 'extract splited 7z archives (.001, .002, ...)' {
$to = test_extract 'Expand-7zipArchive' $test5_1
$to | Should -Exist
"$to\empty" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'extract splited RAR archives (.part01.rar, .part02.rar, ...)' -Skip:$isUnix {
It 'extract splited RAR archives (.part01.rar, .part02.rar, ...)' {
$to = test_extract 'Expand-7zipArchive' $test6_1
$to | Should -Exist
"$to\dummy" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix {
It 'works with "-Removal" switch ($removal param)' {
$test1 | Should -Exist
test_extract 'Expand-7zipArchive' $test1 $true
$test1 | Should -Not -Exist
@@ -126,21 +129,21 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
$test2 = "$working_dir\ZstdTest.tar.zst"
}
It 'extract normal compressed file' -Skip:$isUnix {
It 'extract normal compressed file' {
$to = test_extract 'Expand-ZstdArchive' $test1
$to | Should -Exist
"$to\ZstdTest" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'extract nested compressed file' -Skip:$isUnix {
It 'extract nested compressed file' {
$to = test_extract 'Expand-ZstdArchive' $test2
$to | Should -Exist
"$to\ZstdTest" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix {
It 'works with "-Removal" switch ($removal param)' {
$test1 | Should -Exist
test_extract 'Expand-ZstdArchive' $test1 $true
$test1 | Should -Not -Exist
@@ -159,7 +162,7 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
$test2 = "$working_dir\MSITestNull.msi"
}
It 'extract normal MSI file' -Skip:$isUnix {
It 'extract normal MSI file' {
Mock get_config { $false }
$to = test_extract 'Expand-MsiArchive' $test1
$to | Should -Exist
@@ -167,13 +170,13 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
(Get-ChildItem "$to\MSITest").Count | Should -Be 1
}
It 'extract empty MSI file using lessmsi' -Skip:$isUnix {
It 'extract empty MSI file using lessmsi' {
Mock get_config { $true }
$to = test_extract 'Expand-MsiArchive' $test2
$to | Should -Exist
}
It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix {
It 'works with "-Removal" switch ($removal param)' {
Mock get_config { $false }
$test1 | Should -Exist
test_extract 'Expand-MsiArchive' $test1 $true
@@ -192,14 +195,14 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
$test = "$working_dir\InnoTest.exe"
}
It 'extract Inno Setup file' -Skip:$isUnix {
It 'extract Inno Setup file' {
$to = test_extract 'Expand-InnoArchive' $test
$to | Should -Exist
"$to\empty" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix {
It 'works with "-Removal" switch ($removal param)' {
$test | Should -Exist
test_extract 'Expand-InnoArchive' $test $true
$test | Should -Not -Exist
@@ -212,14 +215,14 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
$test = "$working_dir\ZipTest.zip"
}
It 'extract compressed file' -Skip:$isUnix {
It 'extract compressed file' {
$to = test_extract 'Expand-ZipArchive' $test
$to | Should -Exist
"$to\empty" | Should -Exist
(Get-ChildItem $to).Count | Should -Be 1
}
It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix {
It 'works with "-Removal" switch ($removal param)' {
$test | Should -Exist
test_extract 'Expand-ZipArchive' $test $true
$test | Should -Not -Exist

View File

@@ -1,9 +1,11 @@
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\depends.ps1"
. "$PSScriptRoot\..\lib\buckets.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\depends.ps1"
. "$PSScriptRoot\..\lib\buckets.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
}
Describe 'Package Dependencies' -Tag 'Scoop' {
Context 'Requirement function' {

View File

@@ -1,5 +1,7 @@
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\getopt.ps1"
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\getopt.ps1"
}
Describe 'getopt' -Tag 'Scoop' {
It 'handle short option with required argument missing' {
@@ -15,6 +17,12 @@ Describe 'getopt' -Tag 'Scoop' {
$err | Should -Be 'Option --arb requires an argument.'
}
It 'handle space in quote' {
$opt, $rem, $err = getopt '-x', 'space arg' 'x:' ''
$err | Should -BeNullOrEmpty
$opt.x | Should -Be 'space arg'
}
It 'handle unrecognized short option' {
$null, $null, $err = getopt '-az' 'a' ''
$err | Should -Be 'Option -z not recognized.'

View File

@@ -1,10 +1,10 @@
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\unix.ps1"
$isUnix = is_unix
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\system.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
}
Describe 'appname_from_url' -Tag 'Scoop' {
It 'should extract the correct name' {
@@ -34,39 +34,40 @@ Describe 'url_remote_filename' -Tag 'Scoop' {
}
}
Describe 'is_in_dir' -Tag 'Scoop' {
It 'should work correctly' -Skip:$isUnix {
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
}
}
Describe 'env add and remove path' -Tag 'Scoop' {
# test data
$manifest = @{
'env_add_path' = @('foo', 'bar')
Describe 'env add and remove path' -Tag 'Scoop', 'Windows' {
BeforeAll {
# test data
$manifest = @{
'env_add_path' = @('foo', 'bar', '.', '..')
}
$testdir = Join-Path $PSScriptRoot 'path-test-directory'
$global = $false
}
$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' -Skip:$isUnix {
Mock add_first_in_path {}
Mock remove_from_path {}
It 'should concat the correct 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 }
}
}
@@ -121,37 +122,3 @@ Describe 'persist_def' -Tag 'Scoop' {
$target | Should -Be 'foo'
}
}
Describe 'compute_hash' -Tag 'Scoop' {
BeforeAll {
$working_dir = setup_working 'manifest'
}
It 'computes MD5 correctly' {
compute_hash (Join-Path "$working_dir" 'invalid_wget.json') 'md5' | Should -Be 'cf229eecc201063e32b436e73b71deba'
compute_hash (Join-Path "$working_dir" 'wget.json') 'md5' | Should -Be '57c397fd5092cbd6a8b4df56be2551ab'
compute_hash (Join-Path "$working_dir" 'broken_schema.json') 'md5' | Should -Be '0427c7f4edc33d6d336db98fc160beb0'
compute_hash (Join-Path "$working_dir" 'broken_wget.json') 'md5' | Should -Be '30a7d4d3f64cb7a800d96c0f2ccec87f'
}
It 'computes SHA-1 correctly' {
compute_hash (Join-Path "$working_dir" 'invalid_wget.json') 'sha1' | Should -Be '33ae44df8feed86cdc8f544234029fb28280c3c5'
compute_hash (Join-Path "$working_dir" 'wget.json') 'sha1' | Should -Be '98bfacb887da8cd05d3a1162f89d90173294be55'
compute_hash (Join-Path "$working_dir" 'broken_schema.json') 'sha1' | Should -Be '6dcd64f8ce7a3ae6bbc3dc2288b7cb202dbfa3c8'
compute_hash (Join-Path "$working_dir" 'broken_wget.json') 'sha1' | Should -Be '60b5b1d5bcb4193d19aeab265eab0bb9b0c46c8f'
}
It 'computes SHA-256 correctly' {
compute_hash (Join-Path "$working_dir" 'invalid_wget.json') 'sha256' | Should -Be '1a92ef57c5f3cecba74015ae8e92fc3f2dbe141f9d171c3a06f98645a522d58c'
compute_hash (Join-Path "$working_dir" 'wget.json') 'sha256' | Should -Be '31d6d0953d4e95f0a42080acd61a8c2f92bc90cae324c0d6d2301a974c15f62f'
compute_hash (Join-Path "$working_dir" 'broken_schema.json') 'sha256' | Should -Be 'f3e5082e366006c317d9426e590623254cb1ce23d4f70165afed340b03ce333b'
compute_hash (Join-Path "$working_dir" 'broken_wget.json') 'sha256' | Should -Be 'da658987c3902658c6e754bfa6546dfd084aaa2c3ae25f1fd8aa4645bc9cae24'
}
It 'computes SHA-512 correctly' {
compute_hash (Join-Path "$working_dir" 'invalid_wget.json') 'sha512' | Should -Be '7a7b82ec17547f5ec13dc614a8cec919e897e6c344a6ce7d71205d6f1c3aed276c7b15cbc69acac8207f72417993299cef36884e1915d56758ea09efa2259870'
compute_hash (Join-Path "$working_dir" 'wget.json') 'sha512' | Should -Be '216ebf07bb77062b51420f0f5eb6b7a94d9623d1d41d36c833436058f41e39898f2aa48d7020711c0d8765d02b87ac2e6810f3f502636a6e6f47dc4b9aa02d17'
compute_hash (Join-Path "$working_dir" 'broken_schema.json') 'sha512' | Should -Be '8d3f5617517e61c33275eafea4b166f0a245ec229c40dea436173c354786bad72e4fd9d662f6ac2b9f3dd375c00815a07f10e12975eec1b12da7ba7db10f9c14'
compute_hash (Join-Path "$working_dir" 'broken_wget.json') 'sha512' | Should -Be '7b16a714491e91cc6daa5f90e700547fac4d62e1fcec8c4b78f5a2386e04e68a8ed68f27503ece9555904a047df8050b3f12b4f779c05b1e4d0156e6e2d8fdbb'
}
}

View File

@@ -1,41 +0,0 @@
$repo_dir = (Get-Item $MyInvocation.MyCommand.Path).directory.parent.FullName
Describe 'PSScriptAnalyzer' -Tag 'Linter' {
$scoop_modules = Get-ChildItem $repo_dir -Recurse -Include *.psd1, *.psm1, *.ps1
$scoop_modules = $scoop_modules | Where-Object { $_.DirectoryName -notlike '*\supporting*' -and $_.DirectoryName -notlike '*\test*' }
$scoop_modules = $scoop_modules | Select-Object -ExpandProperty Directory -Unique
Context 'Checking ScriptAnalyzer' {
It 'Invoke-ScriptAnalyzer Cmdlet should exist' {
{ Get-Command Invoke-ScriptAnalyzer -ErrorAction Stop } | Should -Not -Throw
}
It 'PSScriptAnalyzerSettings.ps1 should exist' {
Test-Path "$repo_dir\PSScriptAnalyzerSettings.psd1" | Should -BeTrue
}
It 'There should be files to test' {
$scoop_modules.Count | Should -Not -Be 0
}
}
$linting_settings = Get-Item -Path "$repo_dir\PSScriptAnalyzerSettings.psd1"
Context 'Linting all *.psd1, *.psm1 and *.ps1 files' {
foreach ($directory in $scoop_modules) {
$analysis = Invoke-ScriptAnalyzer -Path $directory.FullName -Settings $linting_settings.FullName
It "Should pass: $directory" {
$analysis.Count | Should -Be 0
}
if ($analysis) {
foreach ($result in $analysis) {
switch -wildCard ($result.ScriptName) {
'*.psm1' { $type = 'Module' }
'*.ps1' { $type = 'Script' }
'*.psd1' { $type = 'Manifest' }
}
Write-Host -f Yellow " [*] $($result.Severity): $($result.Message)"
Write-Host -f Yellow " $($result.RuleName) in $type`: $directory\$($result.ScriptName):$($result.Line)"
}
}
}
}
}

View File

@@ -1,22 +1,28 @@
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
BeforeAll {
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
}
Describe 'Pretty json formating' -Tag 'Scoop' {
BeforeAll {
$format = "$PSScriptRoot\fixtures\format"
$manifests = Get-ChildItem "$format\formatted" -File -Filter '*.json'
Describe 'JSON parse and beautify' -Tag 'Scoop' {
Context 'Parse JSON' {
It 'success with valid json' {
{ parse_json "$PSScriptRoot\fixtures\manifest\wget.json" } | Should -Not -Throw
}
It 'fails with invalid json' {
{ parse_json "$PSScriptRoot\fixtures\manifest\broken_wget.json" } | Should -Throw
}
}
Context 'Beautify manifest' {
$manifests | ForEach-Object {
if ($PSVersionTable.PSVersion.Major -gt 5) { $_ = $_.Name } # Fix for pwsh
It "$_" {
$pretty_json = (parse_json "$format\unformatted\$_") | ConvertToPrettyJson
$correct = (Get-Content "$format\formatted\$_") -join "`r`n"
$correct.CompareTo($pretty_json) | Should -Be 0
}
Context 'Beautify JSON' {
BeforeDiscovery {
$manifests = (Get-ChildItem "$PSScriptRoot\fixtures\format\formatted" -File -Filter '*.json').Name
}
BeforeAll {
$format = "$PSScriptRoot\fixtures\format"
}
It '<_>' -ForEach $manifests {
$pretty_json = (parse_json "$format\unformatted\$_") | ConvertToPrettyJson
$correct = (Get-Content "$format\formatted\$_") -join "`r`n"
$correct.CompareTo($pretty_json) | Should -Be 0
}
}
}
@@ -47,3 +53,34 @@ Describe 'Handle ARM64 and correctly fallback' -Tag 'Scoop' {
Get-SupportedArchitecture $manifest3 'arm64' | Should -BeNullOrEmpty
}
}
Describe 'Manifest Validator' -Tag 'Validator' {
# Could not use backslash '\' in Linux/macOS for .NET object 'Scoop.Validator'
BeforeAll {
Add-Type -Path "$PSScriptRoot\..\supporting\validator\bin\Scoop.Validator.dll"
$schema = "$PSScriptRoot/../schema.json"
}
It 'Scoop.Validator is available' {
([System.Management.Automation.PSTypeName]'Scoop.Validator').Type | Should -Be 'Scoop.Validator'
}
It 'fails with broken schema' {
$validator = New-Object Scoop.Validator("$PSScriptRoot/fixtures/manifest/broken_schema.json", $true)
$validator.Validate("$PSScriptRoot/fixtures/manifest/wget.json") | Should -BeFalse
$validator.Errors.Count | Should -Be 1
$validator.Errors | Select-Object -First 1 | Should -Match 'broken_schema.*(line 6).*(position 4)'
}
It 'fails with broken manifest' {
$validator = New-Object Scoop.Validator($schema, $true)
$validator.Validate("$PSScriptRoot/fixtures/manifest/broken_wget.json") | Should -BeFalse
$validator.Errors.Count | Should -Be 1
$validator.Errors | Select-Object -First 1 | Should -Match 'broken_wget.*(line 5).*(position 4)'
}
It 'fails with invalid manifest' {
$validator = New-Object Scoop.Validator($schema, $true)
$validator.Validate("$PSScriptRoot/fixtures/manifest/invalid_wget.json") | Should -BeFalse
$validator.Errors.Count | Should -Be 16
$validator.Errors | Select-Object -First 1 | Should -Match "Property 'randomproperty' has not been defined and the schema does not allow additional properties\."
$validator.Errors | Select-Object -Last 1 | Should -Match 'Required properties are missing from object: version\.'
}
}

View File

@@ -1,69 +1,3 @@
if (!$script:run) { $script:run = 0 }
if (!$script:failed) { $script:failed = 0 }
function filter_tests($arg) {
if (!$arg) { return }
$script:filter = $arg -join ' '
Write-Host "filtering by '$filter'"
}
function test($desc, $assertions) {
if ($filter -and $desc -notlike "*$filter*") { return }
$script:test = $desc
$script:run++
try {
$assertions.invoke()
} catch {
script:fail $_.exception.innerexception.message
}
$script:test = $null
}
function assert($x, $eq = '__undefined', $ne = '__undefined') {
if ($args.length -gt 0) {
fail "unexpected arguments: $args"
}
if ($eq -ne '__undefined') {
if ($x -ne $eq) { fail "$(fmt $x) != $(fmt $eq)" }
} elseif ($ne -ne '__undefined') {
if ($x -eq $ne) { fail "$(fmt $x) == $(fmt $ne)" }
} else {
if (!$x) { fail "$x" }
}
}
function test_results {
$col = 'darkgreen'
$res = 'all passed'
if ($script:failed -gt 0) {
$col = 'darkred'
$res = "$script:failed failed"
}
Write-Host "ran $script:run tests, " -NoNewline
Write-Host $res -f $col
}
function script:fail($msg) {
$script:failed++
$invoked = (Get-Variable -Scope 1 myinvocation).value
$script = Split-Path $invoked.scriptname -Leaf
$line = $invoked.scriptlinenumber
if ($script:test) { $msg = "$script:test`r`n -> $msg" }
Write-Host "FAIL: $msg" -f darkred
Write-Host "$script line $line`:"
Write-Host (($invoked.positionmessage -split "`r`n")[1..2] -join "`r`n")
}
function script:fmt($var) {
if ($null -eq $var) { return "`$null" }
if ($var -is [string]) { return "'$var'" }
return $var
}
# copies fixtures to a working directory
function setup_working($name) {
$fixtures = "$PSScriptRoot/fixtures/$name"
@@ -73,11 +7,7 @@ function setup_working($name) {
}
# reset working dir
if ($PSVersionTable.Platform -eq 'Unix') {
$working_dir = "/tmp/ScoopTestFixtures/$name"
} else {
$working_dir = "$env:TEMP/ScoopTestFixtures/$name"
}
$working_dir = "$([IO.Path]::GetTempPath())ScoopTestFixtures/$name"
if (Test-Path $working_dir) {
Remove-Item -Recurse -Force $working_dir

View File

@@ -1,5 +1,7 @@
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
}
Describe 'versions comparison' -Tag 'Scoop' {
Context 'semver compliant versions' {
@@ -90,10 +92,21 @@ Describe 'versions comparison' -Tag 'Scoop' {
}
It 'handles equal versions' {
function get_config { $null }
Compare-Version '12.0' '12.0' | Should -Be 0
Compare-Version '7.0.4-9' '7.0.4-9' | Should -Be 0
Compare-Version 'nightly-20190801' 'nightly' | Should -Be 0
Compare-Version 'nightly-20190801' 'nightly-20200801' | Should -Be 0
}
It "handles nightly versions with 'update_nightly'" {
function get_config { $true }
Mock Get-Date { '20200801' }
Compare-Version 'nightly-20200801' 'nightly' | Should -Be 0
Compare-Version 'nightly-20200730' 'nightly' | Should -Be 1
Compare-Version 'nightly-20200730' 'nightly-20200801' | Should -Be 1
Compare-Version 'nightly-20200802' 'nightly' | Should -Be -1
Compare-Version 'nightly-20200802' 'nightly-20200801' | Should -Be -1
}
}
}

View File

@@ -1,22 +1,21 @@
#Requires -Version 5.1
Write-Host "PowerShell: $($PSVersionTable.PSVersion)"
Write-Host (7z.exe | Select-String -Pattern '7-Zip').ToString()
Write-Host 'Check and install testsuite dependencies ...'
if (Get-InstalledModule -Name Pester -MinimumVersion 4.0 -MaximumVersion 4.99 -ErrorAction SilentlyContinue) {
Write-Host 'Pester 4 is already installed.'
Write-Output "PowerShell: $($PSVersionTable.PSVersion)"
Write-Output 'Check and install testsuite dependencies ...'
if (Get-InstalledModule -Name Pester -MinimumVersion 5.2 -MaximumVersion 5.99 -ErrorAction SilentlyContinue) {
Write-Output 'Pester 5 is already installed.'
} else {
Write-Host 'Installing Pester 4 ...'
Install-Module -Repository PSGallery -Scope CurrentUser -Force -Name Pester -MinimumVersion 4.0 -MaximumVersion 4.99 -SkipPublisherCheck
Write-Output 'Installing Pester 5 ...'
Install-Module -Repository PSGallery -Scope CurrentUser -Force -Name Pester -MinimumVersion 5.2 -MaximumVersion 5.99 -SkipPublisherCheck
}
if (Get-InstalledModule -Name PSScriptAnalyzer -MinimumVersion 1.17 -ErrorAction SilentlyContinue) {
Write-Host 'PSScriptAnalyzer is already installed.'
Write-Output 'PSScriptAnalyzer is already installed.'
} else {
Write-Host 'Installing PSScriptAnalyzer ...'
Write-Output 'Installing PSScriptAnalyzer ...'
Install-Module -Repository PSGallery -Scope CurrentUser -Force -Name PSScriptAnalyzer -SkipPublisherCheck
}
if (Get-InstalledModule -Name BuildHelpers -MinimumVersion 2.0 -ErrorAction SilentlyContinue) {
Write-Host 'BuildHelpers is already installed.'
Write-Output 'BuildHelpers is already installed.'
} else {
Write-Host 'Installing BuildHelpers ...'
Write-Output 'Installing BuildHelpers ...'
Install-Module -Repository PSGallery -Scope CurrentUser -Force -Name BuildHelpers -SkipPublisherCheck
}

View File

@@ -1,40 +1,41 @@
#Requires -Version 5.1
#Requires -Modules @{ ModuleName = 'BuildHelpers'; ModuleVersion = '2.0.1' }
#Requires -Modules @{ ModuleName = 'Pester'; MaximumVersion = '4.99' }
#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.2.0' }
#Requires -Modules @{ ModuleName = 'PSScriptAnalyzer'; ModuleVersion = '1.17.1' }
param(
[String] $TestPath = $(Convert-Path "$PSScriptRoot\..\")
[String] $TestPath = (Convert-Path "$PSScriptRoot\..")
)
$splat = @{
Path = $TestPath
PassThru = $true
$pesterConfig = New-PesterConfiguration -Hashtable @{
Run = @{
Path = $TestPath
PassThru = $true
}
Output = @{
Verbosity = 'Detailed'
}
}
$excludes = @()
if ($IsLinux -or $IsMacOS) {
Write-Warning 'Skipping Windows-only tests on Linux/macOS'
$excludes += 'Windows'
}
if ($env:CI -eq $true) {
Write-Host "Load 'BuildHelpers' environment variables ..."
Set-BuildEnvironment -Force
$CI_WIN = (($env:RUNNER_OS -eq 'Windows') -or ($env:CI_WINDOWS -eq $true))
$excludes = @()
$commit = $env:BHCommitHash
$commitMessage = $env:BHCommitMessage
# Check if tests are called from the Core itself, if so, adding excludes
if ($TestPath -eq $(Convert-Path "$PSScriptRoot\..\")) {
if ($commitMessage -match '!linter') {
if ($TestPath -eq (Convert-Path "$PSScriptRoot\..")) {
if ($env:BHCommitMessage -match '!linter') {
Write-Warning "Skipping code linting per commit flag '!linter'"
$excludes += 'Linter'
}
if (!$CI_WIN) {
Write-Warning 'Skipping tests and code linting for decompress.ps1 because they only work on Windows'
$excludes += 'Decompress'
}
$changedScripts = (Get-GitChangedFile -Include '*.ps1' -Commit $commit)
$changedScripts = (Get-GitChangedFile -Include '*.ps1', '*.psd1', '*.psm1' -Commit $env:BHCommitHash)
if (!$changedScripts) {
Write-Warning "Skipping tests and code linting for *.ps1 files because they didn't change"
Write-Warning "Skipping tests and code linting for PowerShell scripts because they didn't change"
$excludes += 'Linter'
$excludes += 'Scoop'
}
@@ -44,12 +45,14 @@ if ($env:CI -eq $true) {
$excludes += 'Decompress'
}
if ('Decompress' -notin $excludes) {
if ('Decompress' -notin $excludes -and 'Windows' -notin $excludes) {
Write-Host 'Install decompress dependencies ...'
Write-Host (7z.exe | Select-String -Pattern '7-Zip').ToString()
$env:SCOOP_HELPERS_PATH = 'C:\projects\helpers'
if (!(Test-Path $env:SCOOP_HELPERS_PATH)) {
New-Item -ItemType Directory -Path $env:SCOOP_HELPERS_PATH
New-Item -ItemType Directory -Path $env:SCOOP_HELPERS_PATH | Out-Null
}
$env:SCOOP_LESSMSI_PATH = "$env:SCOOP_HELPERS_PATH\lessmsi\lessmsi.exe"
@@ -83,13 +86,8 @@ if ($env:CI -eq $true) {
}
}
if ($excludes.Length -gt 0) {
$splat.ExcludeTag = $excludes
}
# Display CI environment variables
$buildVariables = ( Get-ChildItem -Path 'Env:' ).Where( { $_.Name -match '^(?:BH|CI(?:_|$)|APPVEYOR|GITHUB_|RUNNER_|SCOOP_)' } )
$buildVariables += ( Get-Variable -Name 'CI_*' -Scope 'Script' )
$buildVariables = (Get-ChildItem -Path 'Env:').Where({ $_.Name -match '^(?:BH|CI(?:_|$)|APPVEYOR|GITHUB_|RUNNER_|SCOOP_)' })
$details = $buildVariables |
Where-Object -FilterScript { $_.Name -notmatch 'EMAIL' } |
Sort-Object -Property 'Name' |
@@ -97,24 +95,22 @@ if ($env:CI -eq $true) {
Out-String
Write-Host 'CI variables:'
Write-Host $details -ForegroundColor DarkGray
# AppVeyor
if ($env:BHBuildSystem -eq "AppVeyor") {
$resultsXml = "$PSScriptRoot\TestResults.xml"
$splat += @{
OutputFile = $resultsXml
OutputFormat = 'NUnitXML'
}
Write-Host 'Invoke-Pester' @splat
$result = Invoke-Pester @splat
(New-Object Net.WebClient).UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", $resultsXml)
exit $result.FailedCount
}
}
# GitHub Actions / Local
Write-Host 'Invoke-Pester' @splat
$result = Invoke-Pester @splat
if ($excludes.Length -gt 0) {
$pesterConfig.Filter.ExcludeTag = $excludes
}
if ($env:BHBuildSystem -eq 'AppVeyor') {
# AppVeyor
$resultsXml = "$PSScriptRoot\TestResults.xml"
$pesterConfig.TestResult.Enabled = $true
$pesterConfig.TestResult.OutputPath = $resultsXml
$result = Invoke-Pester -Configuration $pesterConfig
Add-TestResultToAppveyor -TestFile $resultsXml
} else {
# GitHub Actions / Local
$result = Invoke-Pester -Configuration $pesterConfig
}
exit $result.FailedCount