78 Commits

Author SHA1 Message Date
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
Hsiao-nan Cheung
8aee6f9980 chore(release): Bump to version 0.3.0
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
Co-authored-by: Richard Kuhnt <r15ch13+git@gmail.com>
Co-authored-by: Mathias Hermansson <mathias.hermansson@se.ibm.com>
Co-authored-by: yi_Xu <yi_Xu@yixuju.cn>
Co-authored-by: Chawye Hsu <chawyehsu@hotmail.com>
Co-authored-by: L. Yeung <lewis_yeung-ly@outlook.com>
Co-authored-by: AkariiinMKII <6019344+AkariiinMKII@users.noreply.github.com>
Co-authored-by: KOGA Mitsuhiro <shiena.jp+github@gmail.com>
Co-authored-by: Jules <jules+dev@simplelogin.com>
Co-authored-by: César Román <thecesrom@gmail.com>
2022-10-13 19:09:34 +08:00
Hsiao-nan Cheung
9baf293ab6 docs(changelog): Prepare for version 0.3.0 (#5167) 2022-10-07 00:26:31 +08:00
Hsiao-nan Cheung
7f47f662e2 feat(subdir): Allow subdir in 'bucket' (#5119) 2022-09-28 11:22:33 +08:00
Rashil Gandhi
7a599f062f feat(install): Add support for ARM64 architecture (#5154)
* Initial support for ARMv8

* Add fallback mechanism

* Update changelog

* Update useragent

* Some typo and format changes

* Use `env:ProgramFiles(Arm)` to detect ARM64

- Move `default_architecture()` to `core.ps1`

* Rename 'ensure_architecture()' and 'default_architecture()'

* Refactor 'supports_architecture()' to 'Get-SupportedArchitecture()'

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-09-28 04:19:03 +05:30
Hsiao-nan Cheung
146dab60d1 builds(schema): Remove 'object' from 'anyOf' under 'sourceforge' (#5163) 2022-09-26 20:14:31 +08:00
Hsiao-nan Cheung
04595b417a builds(checkver): Implement SourceForge checkver functionality (#5113)
Co-authored-by: Mathias Hermansson <mathias.hermansson@se.ibm.com>
2022-09-26 19:56:40 +08:00
César Román
782f3f1aa6 feat(checkurls): Allow checking URLs from private_hosts (#5152) 2022-09-16 13:30:08 +08:00
Jules
122fdc1a1c refactor(download): Rename dl() to Invoke-Download() (#5143) 2022-09-16 10:36:40 +08:00
Hsiao-nan Cheung
373007870c builds(vscode): Tweak VSCode setting (#5149) 2022-09-16 10:28:22 +08:00
L. Yeung
6fc65ed864 refactor(scoop-shim): Use getopt to parse arguments (#5125)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-09-11 22:51:49 +08:00
AkariiinMKII
bfef3d8298 fix(scoop-update): Add uninstall_psmodule to update process (#5136) 2022-09-09 14:16:15 +08:00
KOGA Mitsuhiro
5ad35d6054 fix(jsonpath): Prevent converting date string to DateTime in JSONPath (#5130) 2022-09-09 13:40:44 +08:00
AkariiinMKII
740322f74f fix(psmodules): Remove folder recursively when unlinking previous module path (#5127)
* Update psmodules.ps1

* Update CHANGELOG.md

* Update CHANGELOG.md [skip ci]

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-08-29 21:50:27 +05:30
L. Yeung
e06c7f0c81 feat(getopt): Support option terminator (--) (#5121)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-08-25 11:08:31 +08:00
Hsiao-nan Cheung
1985a05b59 refactor(path): Use 'Convert-Path()' instead of 'Resolve-Path()' (#5109) 2022-08-22 15:12:46 +08:00
Chawye Hsu
dea9ebb01a builds(schema): Set manifest schema to be stricter (#5093) 2022-08-21 19:58:27 +08:00
Hsiao-nan Cheung
08ecdd16a8 fix(config): Change config option to snake_case in file and SCREAMING_CASE in code (#5116) 2022-08-21 19:51:31 +08:00
Hsiao-nan Cheung
a9e5a974dd feat(scoop-config): Allow 'hold_update_until' be set manually (#5100)
Co-authored-by: Richard Kuhnt <r15ch13+git@gmail.com>
2022-08-16 10:03:25 +08:00
yi_Xu
8619ee7603 feat(uninstall): Show the running processes (#5102)
* feat(install): show the running process

* docs(CHANGELOG): Update changelog

* perf(install): Show process-name in table format

* Update lib/install.ps1

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

* Update lib/install.ps1

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

Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2022-08-15 15:02:47 +05:30
Hsiao-nan Cheung
bd123939dc feat(scoop-update): Stash uncommitted changes before update (#5091) 2022-08-11 18:10:42 +08:00
yi_Xu
ec04dd07bc feat(scoop-(un)hold): Support scoop (un)hold scoop (#5089)
* feat(scoop-config): Add new configuration of `SCOOP_HOLD`

Allow to disable Scoop itself updates.
This configuration have the same function with 'scoop (un)hold scoop'.

* perf(scoop-update): Separate `update_bucket` from `update_scoop`

* perf(scoop-(un)hold): remove big overarching if-statement

* perf(scoop-config): use `SCOOP_HOLD_DAYS` instead of `SCOOP_HOLD`

* perf(scoop-config): Update forward Compatible code

* Update libexec/scoop-config.ps1

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

* Update libexec/scoop-hold.ps1

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

* Update libexec/scoop-unhold.ps1

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

* Update libexec/scoop-update.ps1

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

* perf(scoop-update): Update last_scoop_update

* docs(changelog): update changelog to add feature

* fix(changelog): fix changelog typo

* Update libexec/scoop-update.ps1

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

* Update libexec/scoop-config.ps1

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

* Update libexec/scoop-update.ps1

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

* refactor: Use `lastUpdate` instead of `lastupdate`

Consistent with scoop-export.

* fix(install): make config lastUpdate silent

* refactor(scoop-update): Remove `SCOOP_HOLD`

* fix: update changelog

remove none used code.

* Update lib/core.ps1

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

* Update libexec/scoop-update.ps1

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

* Update libexec/scoop-update.ps1

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

* Update libexec/scoop-update.ps1

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

* Update libexec/scoop-update.ps1

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

* Update libexec/scoop-update.ps1

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

* Update libexec/scoop-update.ps1

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

* Update libexec/scoop-update.ps1

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

* Update libexec/scoop-update.ps1

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

* Update libexec/scoop-update.ps1

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

* perf(scoop-update): Handle the judgment in try-catch

* fix(scoop-update): Remove 'update_until' when update scoop itself

* Update lib/core.ps1

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

* docs(CHANGELOG): Update changelog

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2022-08-11 14:50:40 +05:30
Hsiao-nan Cheung
4a31bd3302 chore(release): Bump to version 0.2.4 2022-08-10 13:14:12 +08:00
Hsiao-nan Cheung
5b5daa5ee9 fix(installed): Use 'System.Nullable<bool>' for param 'global' (#5088) 2022-08-07 02:57:12 +08:00
Hsiao-nan Cheung
9b4ee8795d docs(changelog): Change version date [skip ci] (#5087) 2022-08-06 21:02:11 +08:00
yi_Xu
7bfef4912c feat(scoop-update): Add support for pre_uninstall and post_uninstall (#5085) 2022-08-06 20:59:39 +08:00
Hsiao-nan Cheung
ca19d7b856 docs(changelog): Prepare for version 0.2.4 (#5078) 2022-08-05 14:42:00 +08:00
Hsiao-nan Cheung
f945e20167 builds(checkver): Load page content before running 'script' (#5080) 2022-08-05 14:41:30 +08:00
Rashil Gandhi
4d261e7349 feat(scoop-status): Add flag to disable remote checking (#5073)
* feat(scoop-status): Add flag to disable remote checking

* changelog

* Add `break` to stop checking rest of the buckets

* change flag name to `--local`

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-08-04 08:35:28 +05:30
Rashil Gandhi
c3b43625fa feat(install): Show bucket name while installing an app (#5075)
* Update install.ps1

* Update CHANGELOG.md
2022-08-01 22:08:06 +05:30
L. Yeung
34da8507a8 feat(core): Create no window by default in Invoke-ExternalCommand (#5066) 2022-07-26 00:46:08 +08:00
L. Yeung
288aee9ee9 feat(core): Improve argument concatenation in Invoke-ExternalCommand (#5065) 2022-07-25 23:44:26 +08:00
L. Yeung
eac520a4d8 fix(core): Avoid deadlock in Invoke-ExternalCommand (#5064) 2022-07-25 23:43:08 +08:00
Hsiao-nan Cheung
6ae0d5eb8f builds(json): Update Newtonsoft.Json.Schema to 3.0.15-beta2 (#5053) 2022-07-19 15:01:28 +08:00
Rashil Gandhi
9e2e2526fb fix(install): Move from cache when --no-cache (#5039)
* fix(install): Move from cache when `--no-cache`

* Update CHANGELOG.md
2022-07-16 09:59:43 +05:30
L. Yeung
664e667bed fix(scoop-status): Correct formatting of Info output (#5047)
* fix(scoop-status): Correct formatting of `Info` output

* Update CHANGELOG.md
2022-07-15 11:38:30 +05:30
Rashil Gandhi
80b52e32a1 chore(release): Bump to version 0.2.3
Merge pull request #5031 from ScoopInstaller/develop
2022-07-07 15:25:40 +05:30
zStruCat
b4e0ff16a6 fix(scoop-import): Use foreach instead of ForEach-Object for nullity check (#5034)
* Add nullity check and alread_in_local_bucket check

* update CHANGELOG

* Use foreach instead of ForEach-Object

* changelog

* update help

* refine the info and warning when adding an already-added bucket

* Update lib/buckets.ps1

Make warning clearer

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

Co-authored-by: Rashil Gandhi <rashil2000@gmail.com>
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
2022-07-07 10:49:53 +05:30
Hsiao-nan Cheung
c5702ddd19 docs(changelog): Prepare for v0.2.3 (#5036) 2022-07-05 18:11:10 +08:00
Rashil Gandhi
f992f049cc fix(chore): Fix help output of scoop-export (#5029) 2022-07-02 23:21:10 +08:00
Hsiao-nan Cheung
76c3bcd70c builds(json): Update Newton.Json to 13.0.1 (#5026) 2022-07-01 22:29:48 +08:00
Hsiao-nan Cheung
794f8a51aa builds(checkver): Exit routine earlier if error (#5025) 2022-07-01 22:22:49 +08:00
Rashil Gandhi
53f0e9a18c feat(checkver,auto-pr): Allow passing file path (#5019)
* feat(checkver,auto-pr): Allow passing file path

* changelog

* reuse $App parameter

* Update CHANGELOG.md
2022-07-01 12:12:52 +05:30
Issac Lin
1b5ee6f090 fix(decompress): Handle split RAR archives (#4994) 2022-06-24 22:15:54 +08:00
Rashil Gandhi
c4d1b9c22f feat(scoop-import): Import a Scoop installation from JSON (#5014)
* feat(scoop-export): Export Scoop installation in JSON

* optimize

* yesss done

* complete

* Update CHANGELOG.md

* Add suggestions from code review

* fix CI
2022-06-24 11:12:01 +05:30
Rashil Gandhi
9811a5f853 feat(chore): Add missing -a/--all param to all commands (#5004)
* feat(scoop-reset): Add -a/--all switch to reset all apps

* feat(scoop-cache): Add -a/--all switch to delete whole cache

* feat(scoop-virustotal): Add -e/--every switch to check every installed app

* Update CHANGELOG.md

* use 'all' instead of 'every'
2022-06-22 15:47:31 +05:30
Rashil Gandhi
6629331799 feat(scoop-status): Check bucket status, improve output (#5011)
* feat(scoop-status): Check bucket status, improve output

* Update CHANGELOG.md
2022-06-22 15:40:24 +05:30
tech189
4fec4d7089 feat(scoop-info): Show app installed/download size (#4886)
Co-authored-by: Rashil Gandhi <46838874+rashil2000@users.noreply.github.com>
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-06-21 23:22:01 +08:00
Rashil Gandhi
83d0fef02f fix(shortcuts): Fix missing parentheses (#5006)
* fix(shortcuts): Fix missing parentheses

* Update CHANGELOG.md
2022-06-21 18:31:20 +05:30
Francois Botha
0fd6657572 tests(typo): Fix typo ('formated' -> 'formatted') (#4217)
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-06-21 14:46:35 +08:00
Rashil Gandhi
9723725402 fix(chore): Update help documentation (#5002)
* tweaks to help system

* update help text

* update changelog

* fix order of printing deps

* Apply suggestions from code review

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

* Update scoop-help.ps1

* Update scoop.ps1

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-06-21 12:08:05 +05:30
Rashil Gandhi
847756bd1e refactor(scoop-search): Output PSObject, use API token (#4997)
* refactor(scoop-search): Output PSObject, use API token

* warn about parsing error

* Update CHANGELOG.md

* Update scoop-search.ps1

* Apply suggestions from code review

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

* separate lines

Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-06-21 11:48:58 +05:30
Ying Fan Chong
86e3efbaf0 fix(shortcuts): Fix network drive shortcut creation (#4410)
Co-authored-by: Rashil Gandhi <rashil2000@gmail.com>
Co-authored-by: Hsiao-nan Cheung <niheaven@gmail.com>
2022-06-21 14:12:08 +08:00
93 changed files with 2081 additions and 1656 deletions

View File

@@ -31,9 +31,9 @@ labels: "bug"
### System details
**Windows version:** [e.g. 7, 8, 10]
**Windows version:** [e.g. 7, 8, 10, 11]
**OS architecture:** [e.g. 32bit, 64bit]
**OS architecture:** [e.g. 32bit, 64bit, arm64]
**PowerShell version:** [output of `"$($PSVersionTable.PSVersion)"`]

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@v5.1
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@v5.1
with:
modules-to-cache: PSScriptAnalyzer, BuildHelpers, Pester:4.10.1
modules-to-cache: BuildHelpers
shell: pwsh
- name: Test Scoop Core
shell: pwsh

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
*.log
.DS_Store
._.DS_Store
scoop.sublime-workspace

View File

@@ -4,6 +4,9 @@
"powershell.codeFormatting.preset": "OTBS",
"powershell.codeFormatting.alignPropertyValuePairs": true,
"powershell.codeFormatting.ignoreOneLineBlock": true,
"powershell.codeFormatting.useConstantStrings": true,
"powershell.codeFormatting.useCorrectCasing": true,
"powershell.codeFormatting.whitespaceBetweenParameters": true,
"files.exclude": {
"**/.git": true,
"**/.svn": true,

View File

@@ -1,3 +1,131 @@
## [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))
### 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
- **install:** Add support for ARM64 architecture ([#5154](https://github.com/ScoopInstaller/Scoop/issues/5154))
- **install:** Show the running process ([#5102](https://github.com/ScoopInstaller/Scoop/issues/5102))
- **getopt:** Support option terminator (`--`) ([#5121](https://github.com/ScoopInstaller/Scoop/issues/5121))
- **subdir:** Allow subdir in 'bucket' ([#5119](https://github.com/ScoopInstaller/Scoop/issues/5119))
- **scoop-config:** Allow 'hold_update_until' be set manually ([#5100](https://github.com/ScoopInstaller/Scoop/issues/5100))
- **scoop-(un)hold:** Support `scoop (un)hold scoop` ([#5089](https://github.com/ScoopInstaller/Scoop/issues/5089))
- **scoop-update:** Stash uncommitted changes before update ([#5091](https://github.com/ScoopInstaller/Scoop/issues/5091))
### Bug Fixes
- **config:** Change config option to snake_case in file and SCREAMING_CASE in code ([#5116](https://github.com/ScoopInstaller/Scoop/issues/5116))
- **jsonpath:** Prevent converting date string to DateTime in JSONPath ([#5130](https://github.com/ScoopInstaller/Scoop/issues/5130))
- **psmodule:** Remove folder recursively when unlinking previous module path ([#5127](https://github.com/ScoopInstaller/Scoop/issues/5127))
- **scoop-update:** Add `uninstall_psmodule` to update process ([#5136](https://github.com/ScoopInstaller/Scoop/issues/5136))
### Code Refactoring
- **download:** Rename `dl()` to `Invoke-Download()` ([#5143](https://github.com/ScoopInstaller/Scoop/issues/5143))
- **path:** Use 'Convert-Path()' instead of 'Resolve-Path()' ([#5109](https://github.com/ScoopInstaller/Scoop/issues/5109))
- **scoop-shim:** Use `getopt` to parse arguments ([#5125](https://github.com/ScoopInstaller/Scoop/issues/5125))
### Builds
- **checkver:** Implement SourceForge checkver functionality ([#5113](https://github.com/ScoopInstaller/Scoop/issues/5113), [#5163](https://github.com/ScoopInstaller/Scoop/issues/5163))
- **checkurls:** Allow checking URLs from private_hosts ([#5152](https://github.com/ScoopInstaller/Scoop/issues/5152))
- **schema:** Set manifest schema to be stricter ([#5093](https://github.com/ScoopInstaller/Scoop/issues/5093))
- **vscode:** Tweak VSCode setting ([#5149](https://github.com/ScoopInstaller/Scoop/issues/5149))
## [v0.2.4](https://github.com/ScoopInstaller/Scoop/compare/v0.2.3...v0.2.4) - 2022-08-08
### Features
- **core:** Create no window by default in `Invoke-ExternalCommand` ([#5066](https://github.com/ScoopInstaller/Scoop/issues/5066))
- **core:** Improve argument concatenation in `Invoke-ExternalCommand` ([#5065](https://github.com/ScoopInstaller/Scoop/issues/5065))
- **install:** Show bucket name while installing an app ([#5075](https://github.com/ScoopInstaller/Scoop/issues/5075))
- **scoop-status:** Add flag to disable remote checking ([#5073](https://github.com/ScoopInstaller/Scoop/issues/5073))
- **scoop-update:** Add support for `pre_uninstall` and `post_uninstall` ([#5085](https://github.com/ScoopInstaller/Scoop/issues/5085))
### Bug Fixes
- **core:** Avoid deadlock in `Invoke-ExternalCommand` ([#5064](https://github.com/ScoopInstaller/Scoop/issues/5064))
- **core:** Use 'System.Nullable<bool>' for param 'global' ([#5088](https://github.com/ScoopInstaller/Scoop/issues/5088))
- **install:** Move from cache when `--no-cache` is specified ([#5039](https://github.com/ScoopInstaller/Scoop/issues/5039))
- **scoop-status:** Correct formatting of `Info` output ([#5047](https://github.com/ScoopInstaller/Scoop/issues/5047))
### Builds
- **checkver:** Load page content before running 'script' ([#5080](https://github.com/ScoopInstaller/Scoop/issues/5080))
- **json:** Update Newtonsoft.Json.Schema to 3.0.15-beta2 ([#5053](https://github.com/ScoopInstaller/Scoop/issues/5053))
## [v0.2.3](https://github.com/ScoopInstaller/Scoop/compare/v0.2.2...v0.2.3) - 2022-07-07
### Features
- **chore:** Add missing -a/--all param to all commands ([#5004](https://github.com/ScoopInstaller/Scoop/issues/5004))
- **scoop-status:** Check bucket status, improve output ([#5011](https://github.com/ScoopInstaller/Scoop/issues/5011))
- **scoop-info:** Show app installed/download size ([#4886](https://github.com/ScoopInstaller/Scoop/issues/4886))
- **scoop-import:** Import a Scoop installation from JSON ([#5014](https://github.com/ScoopInstaller/Scoop/issues/5014), [#5034](https://github.com/ScoopInstaller/Scoop/issues/5034))
### Bug Fixes
- **chore:** Update help documentation ([#5002](https://github.com/ScoopInstaller/Scoop/issues/5002), [#5029](https://github.com/ScoopInstaller/Scoop/issues/5029))
- **decompress:** Handle split RAR archives ([#4994](https://github.com/ScoopInstaller/Scoop/issues/4994))
- **shortcuts:** Fix network drive shortcut creation ([#4410](https://github.com/ScoopInstaller/Scoop/issues/4410), [#5006](https://github.com/ScoopInstaller/Scoop/issues/5006))
### Code Refactoring
- **scoop-search:** Output PSObject, use API token ([#4997](https://github.com/ScoopInstaller/Scoop/issues/4997))
### Builds
- **checkver,auto-pr:** Allow passing file path ([#5019](https://github.com/ScoopInstaller/Scoop/issues/5019))
- **checkver:** Exit routine earlier if error ([#5025](https://github.com/ScoopInstaller/Scoop/issues/5025))
- **json:** Update Newton.Json to 13.0.1 ([#5026](https://github.com/ScoopInstaller/Scoop/issues/5026))
### Tests
- **typo:** Fix typo ('formated' -> 'formatted') ([#4217](https://github.com/ScoopInstaller/Scoop/issues/4217))
## [v0.2.2](https://github.com/ScoopInstaller/Scoop/compare/v0.2.1...v0.2.2) - 2022-06-21
### 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'
)
}

View File

@@ -120,17 +120,21 @@ The following buckets are known to scoop:
- [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)
- [sysinternals](https://github.com/niheaven/scoop-sysinternals) - Sysinternals Suite and all individual application from [Microsoft](https://learn.microsoft.com/sysinternals/)
- [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
The main bucket is installed by default. To add any of the other buckets, type:
```
```console
scoop bucket add bucketname
```
For example, to add the extras bucket, type:
```
```console
scoop bucket add extras
```

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,7 +47,7 @@ param(
[String] $Upstream,
[String] $OriginBranch = 'master',
[String] $App = '*',
[Parameter(Mandatory = $true)]
[String] $CommitMessageFormat = '<app>: Update to version <version>',
[ValidateScript( {
if (!(Test-Path $_ -Type Container)) {
throw "$_ is not a directory!"
@@ -62,9 +66,14 @@ param(
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\unix.ps1"
$Dir = Resolve-Path $Dir
if ($App -ne '*' -and (Test-Path $App -PathType Leaf)) {
$Dir = Split-Path $App
} elseif ($Dir) {
$Dir = Convert-Path $Dir
} else {
throw "'-Dir' parameter required if '-App' is not a filepath!"
}
if ((!$Push -and !$Request) -or $Help) {
Write-Host @'
@@ -82,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
@@ -105,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"
@@ -122,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"
@@ -137,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.
@@ -150,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')"
}
}
@@ -184,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"
@@ -193,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,9 +47,8 @@ param(
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\unix.ps1"
$Dir = Resolve-Path $Dir
$Dir = Convert-Path $Dir
if ($ForceUpdate) { $Update = $true }
# Cleanup
if (!$UseCache) { Remove-Item "$cachedir\*HASH_CHECK*" -Force }
@@ -60,9 +59,10 @@ function err ([String] $name, [String[]] $message) {
}
$MANIFESTS = @()
foreach ($single in Get-ChildItem $Dir "$App.json") {
$name = (strip_ext $single.Name)
$manifest = parse_json "$Dir\$($single.Name)"
foreach ($single in Get-ChildItem $Dir -Filter "$App.json" -Recurse) {
$name = $single.BaseName
$file = $single.FullName
$manifest = parse_json $file
# Skip nighly manifests, since their hash validation is skipped
if ($manifest.version -eq 'nightly') { continue }
@@ -79,6 +79,8 @@ foreach ($single in Get-ChildItem $Dir "$App.json") {
hash $manifest '64bit' | ForEach-Object { $hashes += $_ }
script:url $manifest '32bit' | ForEach-Object { $urls += $_ }
hash $manifest '32bit' | ForEach-Object { $hashes += $_ }
script:url $manifest 'arm64' | ForEach-Object { $urls += $_ }
hash $manifest 'arm64' | ForEach-Object { $hashes += $_ }
} else {
err $name 'Manifest does not contain URL property.'
continue
@@ -92,6 +94,7 @@ foreach ($single in Get-ChildItem $Dir "$App.json") {
$MANIFESTS += @{
app = $name
file = $file
manifest = $manifest
urls = $urls
hashes = $hashes
@@ -110,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'
}
dl_with_cache $current.app $version $_ $null $null -use_cache:$UseCache
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
$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') {
@@ -141,12 +147,12 @@ foreach ($current in $MANIFESTS) {
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[$_])"
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
}
}
@@ -158,23 +164,27 @@ foreach ($current in $MANIFESTS) {
# Defaults to zero, don't know, which architecture is available
$64bit_count = 0
$32bit_count = 0
$arm64_count = 0
# 64bit is get, donwloaded and added first
if ($platforms.Contains('64bit')) {
$64bit_count = $current.manifest.architecture.'64bit'.hash.Count
# 64bit is get, donwloaded and added first
$current.manifest.architecture.'64bit'.hash = $actuals[0..($64bit_count - 1)]
}
if ($platforms.Contains('32bit')) {
$32bit_count = $current.manifest.architecture.'32bit'.hash.Count
$max = $64bit_count + $32bit_count - 1 # Edge case if manifest contains 64bit and 32bit.
$current.manifest.architecture.'32bit'.hash = $actuals[($64bit_count)..$max]
$current.manifest.architecture.'32bit'.hash = $actuals[($64bit_count)..($64bit_count + $32bit_count - 1)]
}
if ($platforms.Contains('arm64')) {
$arm64_count = $current.manifest.architecture.'arm64'.hash.Count
$current.manifest.architecture.'arm64'.hash = $actuals[($64bit_count + $32bit_count)..($64bit_count + $32bit_count + $arm64_count - 1)]
}
}
Write-Host "Writing updated $($current.app) manifest" -ForegroundColor DarkGreen
$current.manifest = $current.manifest | ConvertToPrettyJson
$path = Resolve-Path "$Dir\$($current.app).json"
$path = Convert-Path $current.file
[System.IO.File]::WriteAllLines($path, $current.manifest)
}
}

View File

@@ -30,12 +30,12 @@ param(
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
$Dir = Resolve-Path $Dir
$Dir = Convert-Path $Dir
$Queue = @()
Get-ChildItem $Dir "$App.json" | ForEach-Object {
$manifest = parse_json "$Dir\$($_.Name)"
$Queue += , @($_.Name, $manifest)
Get-ChildItem $Dir -Filter "$App.json" -Recurse | ForEach-Object {
$manifest = parse_json $_.FullName
$Queue += , @($_.BaseName, $manifest)
}
Write-Host '[' -NoNewLine
@@ -62,6 +62,13 @@ function test_dl([String] $url, $cookies) {
$wreq.Headers.Add('Cookie', (cookie_header $cookies))
}
}
get_config PRIVATE_HOSTS | Where-Object { $_ -ne $null -and $url -match $_.match } | ForEach-Object {
(ConvertFrom-StringData -StringData $_.Headers).GetEnumerator() | ForEach-Object {
$wreq.Headers[$_.Key] = $_.Value
}
}
$wres = $null
try {
$wres = $wreq.GetResponse()
@@ -91,6 +98,7 @@ foreach ($man in $Queue) {
} else {
script:url $manifest '64bit' | ForEach-Object { $urls += $_ }
script:url $manifest '32bit' | ForEach-Object { $urls += $_ }
script:url $manifest 'arm64' | ForEach-Object { $urls += $_ }
}
$urls | ForEach-Object {
@@ -125,7 +133,7 @@ foreach ($man in $Queue) {
Write-Host $failed -NoNewLine -ForegroundColor Red
}
Write-Host '] ' -NoNewLine
Write-Host (strip_ext $name)
Write-Host $name
$errors | ForEach-Object {
Write-Host " > $_" -ForegroundColor DarkRed

View File

@@ -52,7 +52,6 @@
#>
param(
[String] $App = '*',
[Parameter(Mandatory = $true)]
[ValidateScript( {
if (!(Test-Path $_ -Type Container)) {
throw "$_ is not a directory!"
@@ -75,10 +74,17 @@ param(
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\versions.ps1"
. "$PSScriptRoot\..\lib\install.ps1" # needed for hash generation
. "$PSScriptRoot\..\lib\unix.ps1"
$Dir = Resolve-Path $Dir
$Search = $App
if ($App -ne '*' -and (Test-Path $App -PathType Leaf)) {
$Dir = Split-Path $App
$files = Get-ChildItem $Dir -Filter (Split-Path $App -Leaf)
} elseif ($Dir) {
$Dir = Convert-Path $Dir
$files = Get-ChildItem $Dir -Filter "$App.json" -Recurse
} else {
throw "'-Dir' parameter required if '-App' is not a filepath!"
}
$GitHubToken = Get-GitHubToken
# don't use $Version with $App = '*'
@@ -89,21 +95,21 @@ if ($App -eq '*' -and $Version -ne '') {
# get apps to check
$Queue = @()
$json = ''
Get-ChildItem $Dir "$App.json" | ForEach-Object {
$json = parse_json "$Dir\$($_.Name)"
$files | ForEach-Object {
$file = $_.FullName
$json = parse_json $file
if ($json.checkver) {
$Queue += , @($_.Name, $json)
$Queue += , @($_.BaseName, $json, $file)
}
}
# clear any existing events
Get-Event | ForEach-Object {
Remove-Event $_.SourceIdentifier
}
Get-Event | Remove-Event
Get-EventSubscriber | Unregister-Event
# start all downloads
$Queue | ForEach-Object {
$name, $json = $_
$name, $json, $file = $_
$substitutions = Get-VersionSubstitution $json.version # 'autoupdate.ps1'
@@ -115,18 +121,32 @@ $Queue | ForEach-Object {
}
Register-ObjectEvent $wc downloadDataCompleted -ErrorAction Stop | Out-Null
$githubRegex = '\/releases\/tag\/(?:v|V)?([\d.]+)'
$url = $json.homepage
# Not Specified
if ($json.checkver.url) {
$url = $json.checkver.url
} else {
$url = $json.homepage
}
$regex = ''
if ($json.checkver.re) {
$regex = $json.checkver.re
} elseif ($json.checkver.regex) {
$regex = $json.checkver.regex
} else {
$regex = ''
}
$jsonpath = ''
$xpath = ''
$replace = ''
$useGithubAPI = $false
# GitHub
if ($regex) {
$githubRegex = $regex
} else {
$githubRegex = '/releases/tag/(?:v|V)?([\d.]+)'
}
if ($json.checkver -eq 'github') {
if (!$json.homepage.StartsWith('https://github.com/')) {
error "$name checkver expects the homepage to be a github repository"
@@ -142,11 +162,38 @@ $Queue | ForEach-Object {
if ($json.checkver.PSObject.Properties.Count -eq 1) { $useGithubAPI = $true }
}
if ($json.checkver.re) {
$regex = $json.checkver.re
# SourceForge
if ($regex) {
$sourceforgeRegex = $regex
} else {
$sourceforgeRegex = '(?!\.)([\d.]+)(?<=\d)'
}
if ($json.checkver.regex) {
$regex = $json.checkver.regex
if ($json.checkver -eq 'sourceforge') {
if ($json.homepage -match '//(sourceforge|sf)\.net/projects/(?<project>[^/]+)(/files/(?<path>[^/]+))?|//(?<project>[^.]+)\.(sourceforge\.(net|io)|sf\.net)') {
$project = $Matches['project']
$path = $Matches['path']
} else {
$project = strip_ext $name
}
$url = "https://sourceforge.net/projects/$project/rss"
if ($path) {
$url = $url + '?path=/' + $path.TrimStart('/')
}
$regex = "CDATA\[/$path/.*?$sourceforgeRegex.*?\]".Replace('//', '/')
}
if ($json.checkver.sourceforge) {
if ($json.checkver.sourceforge -is [System.String] -and $json.checkver.sourceforge -match '(?<project>[\w-]*)(/(?<path>.*))?') {
$project = $Matches['project']
$path = $Matches['path']
} else {
$project = $json.checkver.sourceforge.project
$path = $json.checkver.sourceforge.path
}
$url = "https://sourceforge.net/projects/$project/rss"
if ($path) {
$url = $url + '?path=/' + $path.TrimStart('/')
}
$regex = "CDATA\[/$path/.*?$sourceforgeRegex.*?\]".Replace('//', '/')
}
if ($json.checkver.jp) {
@@ -159,7 +206,7 @@ $Queue | ForEach-Object {
$xpath = $json.checkver.xpath
}
if ($json.checkver.replace -and $json.checkver.replace.GetType() -eq [System.String]) {
if ($json.checkver.replace -is [System.String]) { # If `checkver` is [System.String], it has a method called `Replace`
$replace = $json.checkver.replace
}
@@ -179,7 +226,8 @@ $Queue | ForEach-Object {
$url = substitute $url $substitutions
$state = New-Object psobject @{
app = (strip_ext $name);
app = $name;
file = $file;
url = $url;
regex = $regex;
json = $json;
@@ -207,31 +255,34 @@ while ($in_progress -gt 0) {
$state = $ev.SourceEventArgs.UserState
$app = $state.app
$file = $state.file
$json = $state.json
$url = $state.url
$regexp = $state.regex
$jsonpath = $state.jsonpath
$xpath = $state.xpath
$script = $json.checkver.script
$reverse = $state.reverse
$replace = $state.replace
$expected_ver = $json.version
$ver = $Version
if (!$ver) {
$page = (Get-Encoding($wc)).GetString($ev.SourceEventArgs.Result)
$err = $ev.SourceEventArgs.Error
if ($json.checkver.script) {
$page = Invoke-Command ([scriptblock]::Create($json.checkver.script -join "`r`n"))
if (!$regex -and $replace) {
next "'replace' requires 're' or 'regex'"
continue
}
$err = $ev.SourceEventArgs.Error
if ($err) {
next "$($err.message)`r`nURL $url is not valid"
continue
}
if (!$regex -and $replace) {
next "'replace' requires 're' or 'regex'"
continue
if ($url) {
$page = (Get-Encoding($wc)).GetString($ev.SourceEventArgs.Result)
}
if ($script) {
$page = Invoke-Command ([scriptblock]::Create($script -join "`r`n"))
}
if ($jsonpath) {
@@ -258,12 +309,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
}
}
@@ -311,7 +367,7 @@ while ($in_progress -gt 0) {
# Skip actual only if versions are same and there is no -f
if (($ver -eq $expected_ver) -and !$ForceUpdate -and $SkipUpdated) { continue }
Write-Host "$App`: " -NoNewline
Write-Host "$app`: " -NoNewline
# version hasn't changed (step over if forced update)
if ($ver -eq $expected_ver -and !$ForceUpdate) {
@@ -337,7 +393,7 @@ while ($in_progress -gt 0) {
Write-Host 'Forcing autoupdate!' -ForegroundColor DarkMagenta
}
try {
Invoke-AutoUpdate $App $Dir $json $ver $matchesHashtable # 'autoupdate.ps1'
Invoke-AutoUpdate $app $file $json $ver $matchesHashtable # 'autoupdate.ps1'
} catch {
if ($ThrowError) {
throw $_

View File

@@ -24,12 +24,12 @@ param(
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\description.ps1"
$Dir = Resolve-Path $Dir
$Dir = Convert-Path $Dir
$Queue = @()
Get-ChildItem $Dir "$App.json" | ForEach-Object {
$manifest = parse_json "$Dir\$($_.Name)"
$Queue += , @(($_.Name -replace '\.json$', ''), $manifest)
Get-ChildItem $Dir -Filter "$App.json" -Recurse | ForEach-Object {
$manifest = parse_json $_.FullName
$Queue += , @($_.BaseName, $manifest)
}
$Queue | ForEach-Object {

View File

@@ -31,15 +31,14 @@ param(
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\json.ps1"
$Dir = Resolve-Path $Dir
Get-ChildItem $Dir "$App.json" | ForEach-Object {
if ($PSVersionTable.PSVersion.Major -gt 5) { $_ = $_.Name } # Fix for pwsh
$Dir = Convert-Path $Dir
Get-ChildItem $Dir -Filter "$App.json" -Recurse | ForEach-Object {
$file = $_.FullName
# beautify
$json = parse_json "$Dir\$_" | ConvertToPrettyJson
$json = parse_json $file | ConvertToPrettyJson
# convert to 4 spaces
$json = $json -replace "`t", ' '
[System.IO.File]::WriteAllLines("$Dir\$_", $json)
[System.IO.File]::WriteAllLines($file, $json)
}

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...'
dl $zipurl $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 $dir -Type Directory -Force | Out-Null
dl $zipurl $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
scoop config lastupdate ([System.DateTime]::Now.ToString('o'))
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

@@ -26,7 +26,7 @@ param(
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
$Dir = Resolve-Path $Dir
$Dir = Convert-Path $Dir
Write-Host '[' -NoNewLine
Write-Host 'C' -NoNewLine -ForegroundColor Green
@@ -36,8 +36,8 @@ Write-Host 'A' -NoNewLine -ForegroundColor Cyan
Write-Host ']utoupdate'
Write-Host ' | |'
Get-ChildItem $Dir "$App.json" | ForEach-Object {
$json = parse_json "$Dir\$($_.Name)"
Get-ChildItem $Dir -Filter "$App.json" -Recurse | ForEach-Object {
$json = parse_json $_.FullName
if ($SkipSupported -and $json.checkver -and $json.autoupdate) { return }
@@ -48,5 +48,5 @@ Get-ChildItem $Dir "$App.json" | ForEach-Object {
Write-Host '[' -NoNewLine
Write-Host $(if ($json.autoupdate) { 'A' } else { ' ' }) -NoNewLine -ForegroundColor Cyan
Write-Host '] ' -NoNewLine
Write-Host (strip_ext $_.Name)
Write-Host $_.BaseName
}

View File

@@ -47,7 +47,7 @@ switch ($subCommand) {
}
}
default {
"scoop: '$subCommand' isn't a scoop command. See 'scoop help'."
warn "scoop: '$subCommand' isn't a scoop command. See 'scoop help'."
exit 1
}
}

View File

@@ -3,6 +3,7 @@
"extras": "https://github.com/ScoopInstaller/Extras",
"versions": "https://github.com/ScoopInstaller/Versions",
"nirsoft": "https://github.com/kodybrown/scoop-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

@@ -248,7 +248,7 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
'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})"'
}
}
@@ -267,14 +267,14 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
write-host -f Green $basename -NoNewline
write-host -f DarkYellow ' to compute hashes!'
try {
dl_with_cache $app $version $url $null $null $true
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"
return $null
}
$file = fullpath (cache_path $app $version $url)
$hash = compute_hash $file 'sha256'
$hash = (Get-FileHash -Path $file -Algorithm SHA256).Hash.ToLower()
write-host -f DarkYellow 'Computed hash: ' -NoNewline
write-host -f Green $hash
return $hash
@@ -449,7 +449,7 @@ function Invoke-AutoUpdate {
# 'Set-Content -Encoding ASCII' don't works in PowerShell 5
# Wait for 'UTF8NoBOM' Encoding in PowerShell 7
# $Manifest | ConvertToPrettyJson | Set-Content -Path (Join-Path $Path "$AppName.json") -Encoding UTF8NoBOM
[System.IO.File]::WriteAllLines((Join-Path $Path "$AppName.json"), (ConvertToPrettyJson $Manifest))
[System.IO.File]::WriteAllLines($Path, (ConvertToPrettyJson $Manifest))
# notes
$note = "`nUpdating note:"
if ($Manifest.autoupdate.note) {
@@ -457,7 +457,7 @@ function Invoke-AutoUpdate {
$hasNote = $true
}
if ($Manifest.autoupdate.architecture) {
'64bit', '32bit' | ForEach-Object {
'64bit', '32bit', 'arm64' | ForEach-Object {
if ($Manifest.autoupdate.architecture.$_.note) {
$note += "`n$_-arch: $($Manifest.autoupdate.architecture.$_.note)"
$hasNote = $true

View File

@@ -50,7 +50,7 @@ function known_buckets {
}
function apps_in_bucket($dir) {
return Get-ChildItem $dir | Where-Object { $_.Name.EndsWith('.json') } | ForEach-Object { $_.Name -replace '.json$', '' }
return (Get-ChildItem $dir -Filter '*.json' -Recurse).BaseName
}
function Get-LocalBucket {
@@ -120,7 +120,7 @@ function add_bucket($name, $repo) {
$dir = Find-BucketDirectory $name -Root
if (Test-Path $dir) {
warn "The '$name' bucket already exists. Use 'scoop bucket rm $name' to remove it."
warn "The '$name' bucket already exists. To add this bucket again, first remove it by running 'scoop bucket rm $name'."
return 2
}

View File

@@ -24,7 +24,7 @@ function Get-Encoding($wc) {
}
function Get-UserAgent() {
return "Scoop/1.0 (+http://scoop.sh/) PowerShell/$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor) (Windows NT $([System.Environment]::OSVersion.Version.Major).$([System.Environment]::OSVersion.Version.Minor); $(if($env:PROCESSOR_ARCHITECTURE -eq 'AMD64'){'Win64; x64; '})$(if($env:PROCESSOR_ARCHITEW6432 -eq 'AMD64'){'WOW64; '})$PSEdition)"
return "Scoop/1.0 (+http://scoop.sh/) PowerShell/$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor) (Windows NT $([System.Environment]::OSVersion.Version.Major).$([System.Environment]::OSVersion.Version.Minor); $(if(${env:ProgramFiles(Arm)}){'ARM64; '}elseif($env:PROCESSOR_ARCHITECTURE -eq 'AMD64'){'Win64; x64; '})$(if($env:PROCESSOR_ARCHITEW6432 -in 'AMD64','ARM64'){'WOW64; '})$PSEdition)"
}
function Show-DeprecatedWarning {
@@ -59,6 +59,7 @@ function load_cfg($file) {
}
function get_config($name, $default) {
$name = $name.ToLowerInvariant()
if($null -eq $scoopConfig.$name -and $null -ne $default) {
return $default
}
@@ -72,6 +73,8 @@ function set_config {
$value
)
$name = $name.ToLowerInvariant()
if ($null -eq $scoopConfig -or $scoopConfig.Count -eq 0) {
ensure (Split-Path -Path $configFile) | Out-Null
$scoopConfig = New-Object -TypeName PSObject
@@ -98,7 +101,7 @@ function set_config {
function setup_proxy() {
# note: '@' and ':' in password must be escaped, e.g. 'p@ssword' -> p\@ssword'
$proxy = get_config 'proxy'
$proxy = get_config PROXY
if(!$proxy) {
return
}
@@ -126,7 +129,7 @@ function setup_proxy() {
}
function git_cmd {
$proxy = get_config 'proxy'
$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"
@@ -153,7 +156,7 @@ function error($msg) { write-host "ERROR $msg" -f darkred }
function warn($msg) { write-host "WARN $msg" -f darkyellow }
function info($msg) { write-host "INFO $msg" -f darkgray }
function debug($obj) {
if((get_config 'debug' $false) -ine 'true' -and $env:SCOOP_DEBUG -ine 'true') {
if ((get_config DEBUG $false) -ine 'true' -and $env:SCOOP_DEBUG -ine 'true') {
return
}
@@ -210,7 +213,7 @@ function appdir($app, $global) { "$(appsdir $global)\$app" }
function versiondir($app, $version, $global) { "$(appdir $app $global)\$version" }
function currentdir($app, $global) {
if (get_config NO_JUNCTIONS) {
if (get_config NO_JUNCTION) {
$version = Select-CurrentVersion -App $app -Global:$global
} else {
$version = 'current'
@@ -225,8 +228,8 @@ function cache_path($app, $version, $url) { "$cachedir\$app#$version#$($url -rep
# apps
function sanitary_path($path) { return [regex]::replace($path, "[/\\?:*<>|]", "") }
function installed($app, $global) {
if (-not $PSBoundParameters.ContainsKey('global')) {
function installed($app, [Nullable[bool]]$global) {
if ($null -eq $global) {
return (installed $app $false) -or (installed $app $true)
}
# Dependencies of the format "bucket/dependency" install in a directory of form
@@ -246,7 +249,7 @@ function installed_apps($global) {
function failed($app, $global) {
$app = ($app -split '/|\\')[-1]
$appPath = appdir $app $global
$hasCurrent = (get_config NO_JUNCTIONS) -or (Test-Path "$appPath\current")
$hasCurrent = (get_config NO_JUNCTION) -or (Test-Path "$appPath\current")
return (Test-Path $appPath) -and !($hasCurrent -and (installed $app $global))
}
@@ -394,7 +397,7 @@ function app_status($app, $global) {
$status.outdated = $false
if ($status.version -and $status.latest_version) {
if (get_config 'force_update' $false) {
if (get_config FORCE_UPDATE $false) {
$status.outdated = ((Compare-Version -ReferenceVersion $status.version -DifferenceVersion $status.latest_version) -ne 0)
} else {
$status.outdated = ((Compare-Version -ReferenceVersion $status.version -DifferenceVersion $status.latest_version) -gt 0)
@@ -507,12 +510,12 @@ function Invoke-ExternalCommand {
}
$Process = New-Object System.Diagnostics.Process
$Process.StartInfo.FileName = $FilePath
$Process.StartInfo.Arguments = ($ArgumentList | Select-Object -Unique) -join ' '
$Process.StartInfo.UseShellExecute = $false
if ($LogPath) {
if ($FilePath -match '(^|\W)msiexec($|\W)') {
$Process.StartInfo.Arguments += " /lwe `"$LogPath`""
if ($FilePath -match '^msiexec(.exe)?$') {
$ArgumentList += "/lwe `"$LogPath`""
} else {
$redirectToLogFile = $true
$Process.StartInfo.RedirectStandardOutput = $true
$Process.StartInfo.RedirectStandardError = $true
}
@@ -520,9 +523,32 @@ 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`""
}
$Process.StartInfo.Arguments = $escapedArgs -join ' '
}
try {
$Process.Start() | Out-Null
[void]$Process.Start()
} catch {
if ($Activity) {
Write-Host "error." -ForegroundColor DarkRed
@@ -530,11 +556,17 @@ function Invoke-ExternalCommand {
error $_.Exception.Message
return $false
}
if ($LogPath -and ($FilePath -notmatch '(^|\W)msiexec($|\W)')) {
Out-UTF8File -FilePath $LogPath -Append -InputObject $Process.StandardOutput.ReadToEnd()
Out-UTF8File -FilePath $LogPath -Append -InputObject $Process.StandardError.ReadToEnd()
if ($redirectToLogFile) {
# we do this to remove a deadlock potential
# ref: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.standardoutput?view=netframework-4.5#remarks
$stdoutTask = $Process.StandardOutput.ReadToEndAsync()
$stderrTask = $Process.StandardError.ReadToEndAsync()
}
$Process.WaitForExit()
if ($redirectToLogFile) {
Out-UTF8File -FilePath $LogPath -Append -InputObject $stdoutTask.Result
Out-UTF8File -FilePath $LogPath -Append -InputObject $stderrTask.Result
}
if ($Process.ExitCode -ne 0) {
if ($ContinueExitCodes -and ($ContinueExitCodes.ContainsKey($Process.ExitCode))) {
if ($Activity) {
@@ -556,13 +588,6 @@ function Invoke-ExternalCommand {
return $true
}
function dl($url,$to) {
$wc = New-Object Net.Webclient
$wc.headers.add('Referer', (strip_filename $url))
$wc.Headers.Add('User-Agent', (Get-UserAgent))
$wc.downloadFile($url,$to)
}
function env($name,$global,$val='__get') {
$target = 'User'; if($global) {$target = 'Machine'}
if($val -eq '__get') { [environment]::getEnvironmentVariable($name,$target) }
@@ -604,12 +629,12 @@ function movedir($from, $to) {
$proc.StartInfo.RedirectStandardError = $true
$proc.StartInfo.UseShellExecute = $false
$proc.StartInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
$proc.Start()
$out = $proc.StandardOutput.ReadToEnd()
[void]$proc.Start()
$stdoutTask = $proc.StandardOutput.ReadToEndAsync()
$proc.WaitForExit()
if($proc.ExitCode -ge 8) {
debug $out
debug $stdoutTask.Result
throw "Could not find '$(fname $from)'! (error $($proc.ExitCode))"
}
@@ -685,7 +710,7 @@ function shim($path, $global, $name, $arg) {
Push-Location $abs_shimdir
$relative_path = Resolve-Path -Relative $path
Pop-Location
$resolved_path = Resolve-Path $path
$resolved_path = Convert-Path $path
if ($path -match '\.(exe|com)$') {
# for programs with no awareness of any shell
@@ -781,6 +806,11 @@ function shim($path, $global, $name, $arg) {
} else {
warn_on_overwrite "$shim.cmd" $path
# find path to Git's bash so that batch scripts can run bash scripts
if (!(Get-CommandPath git)) {
error "Can't shim '$shim': 'git' is needed but not installed."
error "Please install git ('scoop install git') and try again."
exit 1
}
$gitdir = (Get-Item (Get-CommandPath git) -ErrorAction:Stop).Directory.Parent
if ($gitdir.FullName -imatch 'mingw') {
$gitdir = $gitdir.Parent
@@ -801,7 +831,7 @@ 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'
$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 }
@@ -832,15 +862,38 @@ function ensure_in_path($dir, $global) {
}
}
function ensure_architecture($architecture_opt) {
if(!$architecture_opt) {
return default_architecture
function Get-DefaultArchitecture {
$arch = get_config DEFAULT_ARCHITECTURE
$system = if (${env:ProgramFiles(Arm)}) {
'arm64'
} elseif ([System.Environment]::Is64BitOperatingSystem) {
'64bit'
} else {
'32bit'
}
$architecture_opt = $architecture_opt.ToString().ToLower()
switch($architecture_opt) {
{ @('64bit', '64', 'x64', 'amd64', 'x86_64', 'x86-64') -contains $_ } { return '64bit' }
{ @('32bit', '32', 'x86', 'i386', '386', 'i686') -contains $_ } { return '32bit' }
default { throw [System.ArgumentException] "Invalid architecture: '$architecture_opt'"}
if ($null -eq $arch) {
$arch = $system
} else {
try {
$arch = Format-ArchitectureString $arch
} catch {
warn 'Invalid default architecture configured. Determining default system architecture'
$arch = $system
}
}
return $arch
}
function Format-ArchitectureString($Architecture) {
if (!$Architecture) {
return Get-DefaultArchitecture
}
$Architecture = $Architecture.ToString().ToLower()
switch ($Architecture) {
{ @('64bit', '64', 'x64', 'amd64', 'x86_64', 'x86-64') -contains $_ } { return '64bit' }
{ @('32bit', '32', 'x86', 'i386', '386', 'i686') -contains $_ } { return '32bit' }
{ @('arm64', 'arm', 'aarch64') -contains $_ } { return 'arm64' }
default { throw [System.ArgumentException] "Invalid architecture: '$Architecture'" }
}
}
@@ -967,29 +1020,39 @@ function show_app($app, $bucket, $version) {
return $app
}
function last_scoop_update() {
# PowerShell 6 returns an DateTime Object
$last_update = (get_config lastupdate)
if ($null -ne $last_update -and $last_update.GetType() -eq [System.String]) {
try {
$last_update = [System.DateTime]::Parse($last_update)
} catch {
$last_update = $null
}
}
return $last_update
}
function is_scoop_outdated() {
$last_update = $(last_scoop_update)
$now = [System.DateTime]::Now
if($null -eq $last_update) {
set_config lastupdate $now.ToString('o')
# enforce an update for the first time
try {
$expireHour = (New-TimeSpan (get_config LAST_UPDATE) $now).TotalHours
return ($expireHour -ge 3)
} catch {
# If not System.DateTime
set_config LAST_UPDATE ($now.ToString('o')) | Out-Null
return $true
}
return $last_update.AddHours(3) -lt $now.ToLocalTime()
}
function Test-ScoopCoreOnHold() {
$hold_update_until = get_config HOLD_UPDATE_UNTIL
if ($null -eq $hold_update_until) {
return $false
}
$parsed_date = New-Object -TypeName DateTime
if ([System.DateTime]::TryParse($hold_update_until, $null, [System.Globalization.DateTimeStyles]::AssumeLocal, [ref]$parsed_date)) {
if ((New-TimeSpan $parsed_date).TotalSeconds -lt 0) {
warn "Skipping self-update of Scoop Core until $($parsed_date.ToLocalTime())..."
warn "If you want to update Scoop Core immediately, use 'scoop unhold scoop; scoop update'."
return $true
} else {
warn 'Self-update of Scoop Core is enabled again!'
}
} else {
error "'hold_update_until' has been set in the wrong format and was removed."
error 'If you want to disable self-update of Scoop Core for a moment,'
error "use 'scoop hold scoop' or 'scoop config hold_update_until <YYYY-MM-DD>/<YYYY/MM/DD>'."
}
set_config HOLD_UPDATE_UNTIL $null | Out-Null
return $false
}
function substitute($entity, [Hashtable] $params, [Bool]$regexEscape = $false) {
@@ -1057,7 +1120,7 @@ function get_hash([String] $multihash) {
}
function Get-GitHubToken {
return $env:SCOOP_GH_TOKEN, (get_config 'gh_token') | Where-Object -Property Length -Value 0 -GT | Select-Object -First 1
return $env:SCOOP_GH_TOKEN, (get_config GH_TOKEN) | Where-Object -Property Length -Value 0 -GT | Select-Object -First 1
}
function handle_special_urls($url)
@@ -1157,31 +1220,56 @@ function Out-UTF8File {
# for all communication with api.github.com
Optimize-SecurityProtocol
# Scoop config file migration
# Load Scoop config
$configHome = $env:XDG_CONFIG_HOME, "$env:USERPROFILE\.config" | Select-Object -First 1
$configFile = "$configHome\scoop\config.json"
if ((Test-Path "$env:USERPROFILE\.scoop") -and !(Test-Path $configFile)) {
New-Item -ItemType Directory (Split-Path -Path $configFile) -ErrorAction Ignore | Out-Null
Move-Item "$env:USERPROFILE\.scoop" $configFile
write-host "WARN Scoop configuration has been migrated from '~/.scoop'" -f darkyellow
write-host "WARN to '$configFile'" -f darkyellow
}
# Load Scoop config
$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
}
}
# END NOTE
# Scoop root directory
$scoopdir = $env:SCOOP, (get_config 'rootPath'), "$env:USERPROFILE\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
$scoopdir = $env:SCOOP, (get_config ROOT_PATH), "$([System.Environment]::GetFolderPath('UserProfile'))\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
# Scoop global apps directory
$globaldir = $env:SCOOP_GLOBAL, (get_config 'globalPath'), "$env:ProgramData\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -first 1
$globaldir = $env:SCOOP_GLOBAL, (get_config GLOBAL_PATH), "$([System.Environment]::GetFolderPath('CommonApplicationData'))\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
# Scoop cache directory
# Note: Setting the SCOOP_CACHE environment variable to use a shared directory
# is experimental and untested. There may be concurrency issues when
# multiple users write and access cached files at the same time.
# Use at your own risk.
$cachedir = $env:SCOOP_CACHE, (get_config 'cachePath'), "$scoopdir\cache" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -first 1
$cachedir = $env:SCOOP_CACHE, (get_config CACHE_PATH), "$scoopdir\cache" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
# OS information
$WindowsBuild = [System.Environment]::OSVersion.Version.Build
# Setup proxy globally
setup_proxy

View File

@@ -18,20 +18,21 @@ function Expand-7zipArchive {
[Switch]
$Removal
)
if ((get_config 7ZIPEXTRACT_USE_EXTERNAL)) {
if ((get_config USE_EXTERNAL_7ZIP)) {
try {
$7zPath = (Get-Command '7z' -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source
} catch [System.Management.Automation.CommandNotFoundException] {
abort "`nCannot find external 7-Zip (7z.exe) while '7ZIPEXTRACT_USE_EXTERNAL' is 'true'!`nRun 'scoop config 7ZIPEXTRACT_USE_EXTERNAL false' or install 7-Zip manually and try again."
abort "`nCannot find external 7-Zip (7z.exe) while 'use_external_7zip' is 'true'!`nRun 'scoop config use_external_7zip false' or install 7-Zip manually and try again."
}
} else {
$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", '-y')
$IsTar = ((strip_ext $Path) -match '\.tar$') -or ($Path -match '\.t[abgpx]z2?$')
if (!$IsTar -and $ExtractDir) {
$ArgList += "-ir!`"$ExtractDir\*`""
$ArgList += "-ir!$ExtractDir\*"
}
if ($Switches) {
$ArgList += (-split $Switches)
@@ -53,7 +54,7 @@ function Expand-7zipArchive {
}
if ($IsTar) {
# Check for tar
$Status = Invoke-ExternalCommand $7zPath @('l', "`"$Path`"") -LogPath $LogPath
$Status = Invoke-ExternalCommand $7zPath @('l', $Path) -LogPath $LogPath
if ($Status) {
# get inner tar file name
$TarFile = (Select-String -Path $LogPath -Pattern '[^ ]*tar$').Matches.Value
@@ -67,6 +68,9 @@ function Expand-7zipArchive {
if (($Path -replace '.*\.([^\.]*)$', '$1') -eq '001') {
# Remove splited 7-zip archive parts
Get-ChildItem "$($Path -replace '\.[^\.]*$', '').???" | Remove-Item -Force
} elseif (($Path -replace '.*\.part(\d+)\.rar$', '$1')[-1] -eq '1') {
# Remove splitted RAR archive parts
Get-ChildItem "$($Path -replace '\.part(\d+)\.rar$', '').part*.rar" | Remove-Item -Force
} else {
Remove-Item $Path -Force
}
@@ -94,7 +98,7 @@ function Expand-ZstdArchive {
$LogPath = Join-Path (Split-Path $Path) 'zstd.log'
$DestinationPath = $DestinationPath.TrimEnd('\')
ensure $DestinationPath | Out-Null
$ArgList = @('-d', "`"$Path`"", '--output-dir-flat', "`"$DestinationPath`"", '-f', '-v')
$ArgList = @('-d', $Path, '--output-dir-flat', $DestinationPath, '-f', '-v')
if ($Switches) {
$ArgList += (-split $Switches)
@@ -143,12 +147,12 @@ function Expand-MsiArchive {
$OriDestinationPath = $DestinationPath
$DestinationPath = "$DestinationPath\_tmp"
}
if ((get_config MSIEXTRACT_USE_LESSMSI)) {
if ((get_config USE_LESSMSI)) {
$MsiPath = Get-HelperPath -Helper Lessmsi
$ArgList = @('x', "`"$Path`"", "`"$DestinationPath\\`"")
$ArgList = @('x', $Path, "$DestinationPath\")
} else {
$MsiPath = 'msiexec.exe'
$ArgList = @('/a', "`"$Path`"", '/qn', "TARGETDIR=`"$DestinationPath\\SourceDir`"")
$ArgList = @('/a', "`"$Path`"", '/qn', "TARGETDIR=`"$DestinationPath\SourceDir`"")
}
$LogPath = "$(Split-Path $Path)\msi.log"
if ($Switches) {
@@ -197,7 +201,7 @@ function Expand-InnoArchive {
$Removal
)
$LogPath = "$(Split-Path $Path)\innounp.log"
$ArgList = @('-x', "-d`"$DestinationPath`"", "`"$Path`"", '-y')
$ArgList = @('-x', "-d$DestinationPath", $Path, '-y')
switch -Regex ($ExtractDir) {
'^[^{].*' { $ArgList += "-c{app}\$ExtractDir" }
'^{.*' { $ArgList += "-c$ExtractDir" }
@@ -237,7 +241,8 @@ function Expand-ZipArchive {
$OriDestinationPath = $DestinationPath
$DestinationPath = "$DestinationPath\_tmp"
}
Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force
# 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
if ($ExtractDir) {
movedir "$DestinationPath\$ExtractDir" $OriDestinationPath | Out-Null
Remove-Item $DestinationPath -Recurse -Force
@@ -264,7 +269,7 @@ function Expand-DarkArchive {
$Removal
)
$LogPath = "$(Split-Path $Path)\dark.log"
$ArgList = @('-nologo', "-x `"$DestinationPath`"", "`"$Path`"")
$ArgList = @('-nologo', '-x', $DestinationPath, $Path)
if ($Switches) {
$ArgList += (-split $Switches)
}

View File

@@ -106,10 +106,10 @@ function Get-InstallationHelper {
$installer = arch_specific 'installer' $Manifest $Architecture
$post_install = arch_specific 'post_install' $Manifest $Architecture
$script = $pre_install + $installer.script + $post_install
if (((Test-7zipRequirement -Uri $url) -or ($script -like '*Expand-7zipArchive *')) -and !(get_config 7ZIPEXTRACT_USE_EXTERNAL)) {
if (((Test-7zipRequirement -Uri $url) -or ($script -like '*Expand-7zipArchive *')) -and !(get_config USE_EXTERNAL_7ZIP)) {
$helper += '7zip'
}
if (((Test-LessmsiRequirement -Uri $url) -or ($script -like '*Expand-MsiArchive *')) -and (get_config MSIEXTRACT_USE_LESSMSI)) {
if (((Test-LessmsiRequirement -Uri $url) -or ($script -like '*Expand-MsiArchive *')) -and (get_config USE_LESSMSI)) {
$helper += 'lessmsi'
}
if ($Manifest.innosetup -or ($script -like '*Expand-InnoArchive *')) {

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

@@ -8,6 +8,11 @@
# array of strings that are long-form options. options that take
# a parameter should end with '='
# returns @(opts hash, remaining_args array, error string)
# NOTES:
# The first "--" in $argv, if any, will terminate all options; any
# 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) {
$opts = @{}; $rem = @()
@@ -16,29 +21,35 @@ function getopt($argv, $shortopts, $longopts) {
}
function regex_escape($str) {
return [regex]::escape($str)
return [Regex]::Escape($str)
}
# ensure these are arrays
$argv = @($argv)
$argv = @($argv -split ' ')
$longopts = @($longopts)
for($i = 0; $i -lt $argv.length; $i++) {
for ($i = 0; $i -lt $argv.Length; $i++) {
$arg = $argv[$i]
if($null -eq $arg) { continue }
if ($null -eq $arg) { continue }
# don't try to parse array arguments
if($arg -is [array]) { $rem += ,$arg; continue }
if($arg -is [int]) { $rem += $arg; continue }
if($arg -is [decimal]) { $rem += $arg; continue }
if ($arg -is [Array]) { $rem += , $arg; continue }
if ($arg -is [Int]) { $rem += $arg; continue }
if ($arg -is [Decimal]) { $rem += $arg; continue }
if($arg.startswith('--')) {
$name = $arg.substring(2)
if ($arg -eq '--') {
if ($i -lt $argv.Length - 1) {
$rem += $argv[($i + 1)..($argv.Length - 1)]
}
break
} elseif ($arg.StartsWith('--')) {
$name = $arg.Substring(2)
$longopt = $longopts | Where-Object { $_ -match "^$name=?$" }
if($longopt) {
if($longopt.endswith('=')) { # requires arg
if($i -eq $argv.length - 1) {
if ($longopt) {
if ($longopt.EndsWith('=')) {
# requires arg
if ($i -eq $argv.Length - 1) {
return err "Option --$name requires an argument."
}
$opts.$name = $argv[++$i]
@@ -48,14 +59,14 @@ function getopt($argv, $shortopts, $longopts) {
} else {
return err "Option --$name not recognized."
}
} elseif($arg.startswith('-') -and $arg -ne '-') {
for($j = 1; $j -lt $arg.length; $j++) {
$letter = $arg[$j].tostring()
} elseif ($arg.StartsWith('-') -and $arg -ne '-') {
for ($j = 1; $j -lt $arg.Length; $j++) {
$letter = $arg[$j].ToString()
if($shortopts -match "$(regex_escape $letter)`:?") {
$shortopt = $matches[0]
if($shortopt[1] -eq ':') {
if($j -ne $arg.length -1 -or $i -eq $argv.length - 1) {
if ($shortopts -match "$(regex_escape $letter)`:?") {
$shortopt = $Matches[0]
if ($shortopt[1] -eq ':') {
if ($j -ne $arg.Length - 1 -or $i -eq $argv.Length - 1) {
return err "Option -$letter requires an argument."
}
$opts.$letter = $argv[++$i]

View File

@@ -1,9 +1,8 @@
function nightly_version($date, $quiet = $false) {
$date_str = $date.tostring("yyyyMMdd")
function nightly_version($quiet = $false) {
if (!$quiet) {
warn "This is a nightly version. Downloaded files won't be verified."
}
"nightly-$date_str"
return "nightly-$(Get-Date -Format 'yyyyMMdd')"
}
function install_app($app, $architecture, $global, $suggested, $use_cache = $true, $check_hash = $true) {
@@ -21,18 +20,19 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
$is_nightly = $version -eq 'nightly'
if ($is_nightly) {
$version = nightly_version $(get-date)
$version = nightly_version
$check_hash = $false
}
if(!(supports_architecture $manifest $architecture)) {
write-host -f DarkRed "'$app' doesn't support $architecture architecture!"
$architecture = Get-SupportedArchitecture $manifest $architecture
if ($null -eq $architecture) {
error "'$app' doesn't support current architecture!"
return
}
if ((get_config 'manifest_review' $false) -and ($MyInvocation.ScriptName -notlike '*scoop-update*')) {
if ((get_config SHOW_MANIFEST $false) -and ($MyInvocation.ScriptName -notlike '*scoop-update*')) {
Write-Host "Manifest: $app.json"
$style = get_config cat_style
$style = get_config CAT_STYLE
if ($style) {
$manifest | ConvertToPrettyJson | bat --no-paging --style $style --language json
} else {
@@ -43,13 +43,13 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
return
}
}
write-output "Installing '$app' ($version) [$architecture]"
Write-Output "Installing '$app' ($version) [$architecture]$(if ($bucket) { " from $bucket bucket" })"
$dir = ensure (versiondir $app $version $global)
$original_dir = $dir # keep reference to real (not linked) directory
$persist_dir = persistdir $app $global
$fname = dl_urls $app $version $manifest $bucket $architecture $dir $use_cache $check_hash
$fname = Invoke-ScoopDownload $app $version $manifest $bucket $architecture $dir $use_cache $check_hash
Invoke-HookScript -HookType 'pre_install' -Manifest $manifest -Arch $architecture
run_installer $fname $manifest $architecture $dir $global
@@ -80,27 +80,31 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
show_notes $manifest $dir $original_dir $persist_dir
}
function dl_with_cache($app, $version, $url, $to, $cookies = $null, $use_cache = $true) {
function Invoke-CachedDownload ($app, $version, $url, $to, $cookies = $null, $use_cache = $true) {
$cached = fullpath (cache_path $app $version $url)
if(!(test-path $cached) -or !$use_cache) {
ensure $cachedir | Out-Null
do_dl $url "$cached.download" $cookies
Start-Download $url "$cached.download" $cookies
Move-Item "$cached.download" $cached -force
} else { write-host "Loading $(url_remote_filename $url) from cache"}
if (!($null -eq $to)) {
Copy-Item $cached $to
if ($use_cache) {
Copy-Item $cached $to
} else {
Move-Item $cached $to -Force
}
}
}
function do_dl($url, $to, $cookies) {
function Start-Download ($url, $to, $cookies) {
$progress = [console]::isoutputredirected -eq $false -and
$host.name -ne 'Windows PowerShell ISE Host'
try {
$url = handle_special_urls $url
dl $url $to $cookies $progress
Invoke-Download $url $to $cookies $progress
} catch {
$e = $_.exception
if($e.innerexception) { $e = $e.innerexception }
@@ -175,7 +179,7 @@ function get_filename_from_metalink($file) {
return $filename
}
function dl_with_cache_aria2($app, $version, $manifest, $architecture, $dir, $cookies = $null, $use_cache = $true, $check_hash = $true) {
function Invoke-CachedAria2Download ($app, $version, $manifest, $architecture, $dir, $cookies = $null, $use_cache = $true, $check_hash = $true) {
$data = @{}
$urls = @(script:url $manifest $architecture)
@@ -210,7 +214,7 @@ function dl_with_cache_aria2($app, $version, $manifest, $architecture, $dir, $co
$options += "--header='Cookie: $(cookie_header $cookies)'"
}
$proxy = get_config 'proxy'
$proxy = get_config PROXY
if ($proxy -ne 'none') {
if ([Net.Webrequest]::DefaultWebProxy.Address) {
$options += "--all-proxy='$([Net.Webrequest]::DefaultWebProxy.Address.Authority)'"
@@ -351,7 +355,7 @@ function dl_with_cache_aria2($app, $version, $manifest, $architecture, $dir, $co
}
# download with filesize and progress indicator
function dl($url, $to, $cookies, $progress) {
function Invoke-Download ($url, $to, $cookies, $progress) {
$reqUrl = ($url -split '#')[0]
$wreq = [Net.WebRequest]::Create($reqUrl)
if ($wreq -is [Net.HttpWebRequest]) {
@@ -367,7 +371,7 @@ function dl($url, $to, $cookies, $progress) {
$wreq.Headers.Add('Cookie', (cookie_header $cookies))
}
get_config 'private_hosts' | Where-Object { $_ -ne $null -and $url -match $_.match } | ForEach-Object {
get_config PRIVATE_HOSTS | Where-Object { $_ -ne $null -and $url -match $_.match } | ForEach-Object {
(ConvertFrom-StringData -StringData $_.Headers).GetEnumerator() | ForEach-Object {
$wreq.Headers[$_.Key] = $_.Value
}
@@ -405,7 +409,7 @@ function dl($url, $to, $cookies, $progress) {
$newUrl = "$newUrl#/$postfix"
}
dl $newUrl $to $cookies $progress
Invoke-Download $newUrl $to $cookies $progress
return
}
@@ -416,12 +420,12 @@ function dl($url, $to, $cookies, $progress) {
if ($progress -and ($total -gt 0)) {
[console]::CursorVisible = $false
function dl_onProgress($read) {
dl_progress $read $total $url
function Trace-DownloadProgress ($read) {
Write-DownloadProgress $read $total $url
}
} else {
write-host "Downloading $url ($(filesize $total))..."
function dl_onProgress {
function Trace-DownloadProgress {
#no op
}
}
@@ -433,17 +437,17 @@ function dl($url, $to, $cookies, $progress) {
$totalRead = 0
$sw = [diagnostics.stopwatch]::StartNew()
dl_onProgress $totalRead
Trace-DownloadProgress $totalRead
while(($read = $s.read($buffer, 0, $buffer.length)) -gt 0) {
$fs.write($buffer, 0, $read)
$totalRead += $read
if ($sw.elapsedmilliseconds -gt 100) {
$sw.restart()
dl_onProgress $totalRead
Trace-DownloadProgress $totalRead
}
}
$sw.stop()
dl_onProgress $totalRead
Trace-DownloadProgress $totalRead
} finally {
if ($progress) {
[console]::CursorVisible = $true
@@ -459,7 +463,7 @@ function dl($url, $to, $cookies, $progress) {
}
}
function dl_progress_output($url, $read, $total, $console) {
function Format-DownloadProgress ($url, $read, $total, $console) {
$filename = url_remote_filename $url
# calculate current percentage done
@@ -498,14 +502,14 @@ function dl_progress_output($url, $read, $total, $console) {
"$left [$dashes$spaces] $right"
}
function dl_progress($read, $total, $url) {
function Write-DownloadProgress ($read, $total, $url) {
$console = $host.UI.RawUI;
$left = $console.CursorPosition.X;
$top = $console.CursorPosition.Y;
$width = $console.BufferSize.Width;
if($read -eq 0) {
$maxOutputLength = $(dl_progress_output $url 100 $total $console).length
$maxOutputLength = $(Format-DownloadProgress $url 100 $total $console).length
if (($left + $maxOutputLength) -gt $width) {
# not enough room to print progress on this line
# print on new line
@@ -516,11 +520,11 @@ function dl_progress($read, $total, $url) {
}
}
write-host $(dl_progress_output $url $read $total $console) -nonewline
write-host $(Format-DownloadProgress $url $read $total $console) -nonewline
[console]::SetCursorPosition($left, $top)
}
function dl_urls($app, $version, $manifest, $bucket, $architecture, $dir, $use_cache = $true, $check_hash = $true) {
function Invoke-ScoopDownload ($app, $version, $manifest, $bucket, $architecture, $dir, $use_cache = $true, $check_hash = $true) {
# we only want to show this warning once
if(!$use_cache) { warn "Cache is being ignored." }
@@ -541,13 +545,13 @@ function dl_urls($app, $version, $manifest, $bucket, $architecture, $dir, $use_c
# download first
if(Test-Aria2Enabled) {
dl_with_cache_aria2 $app $version $manifest $architecture $dir $cookies $use_cache $check_hash
Invoke-CachedAria2Download $app $version $manifest $architecture $dir $cookies $use_cache $check_hash
} else {
foreach($url in $urls) {
$fname = url_filename $url
try {
dl_with_cache $app $version $url "$dir\$fname" $cookies $use_cache
Invoke-CachedDownload $app $version $url "$dir\$fname" $cookies $use_cache
} catch {
write-host -f darkred $_
abort "URL $url is not valid"
@@ -584,7 +588,7 @@ function dl_urls($app, $version, $manifest, $bucket, $architecture, $dir, $use_c
$extract_fn = 'Expand-InnoArchive'
} elseif($fname -match '\.zip$') {
# Use 7zip when available (more fast)
if (((get_config 7ZIPEXTRACT_USE_EXTERNAL) -and (Test-CommandAvailable 7z)) -or (Test-HelperInstalled -Helper 7zip)) {
if (((get_config USE_EXTERNAL_7ZIP) -and (Test-CommandAvailable 7z)) -or (Test-HelperInstalled -Helper 7zip)) {
$extract_fn = 'Expand-7zipArchive'
} else {
$extract_fn = 'Expand-ZipArchive'
@@ -655,7 +659,7 @@ function hash_for_url($manifest, $url, $arch) {
function check_hash($file, $hash, $app_name) {
$file = fullpath $file
if(!$hash) {
warn "Warning: No hash in manifest. SHA256 for '$(fname $file)' is:`n $(compute_hash $file 'sha256')"
warn "Warning: No hash in manifest. SHA256 for '$(fname $file)' is:`n $((Get-FileHash -Path $file -Algorithm SHA256).Hash.ToLower())"
return $true, $null
}
@@ -667,7 +671,7 @@ function check_hash($file, $hash, $app_name) {
return $false, "Hash type '$algorithm' isn't supported."
}
$actual = compute_hash $file $algorithm
$actual = (Get-FileHash -Path $file -Algorithm $algorithm).Hash.ToLower()
$expected = $expected.ToLower()
if($actual -ne $expected) {
@@ -687,25 +691,6 @@ function check_hash($file, $hash, $app_name) {
return $true, $null
}
function compute_hash($file, $algname) {
try {
if(Test-CommandAvailable Get-FileHash) {
return (Get-FileHash -Path $file -Algorithm $algname).Hash.ToLower()
} else {
$fs = [system.io.file]::openread($file)
$alg = [system.security.cryptography.hashalgorithm]::create($algname)
$hexbytes = $alg.computehash($fs) | ForEach-Object { $_.tostring('x2') }
return [string]::join('', $hexbytes)
}
} catch {
error $_.exception.message
} finally {
if($fs) { $fs.dispose() }
if($alg) { $alg.dispose() }
}
return ''
}
# for dealing with installers
function args($config, $dir, $global) {
if($config) { return $config | ForEach-Object { (format $_ @{'dir'=$dir;'global'=$global}) } }
@@ -905,7 +890,7 @@ function rm_shims($app, $manifest, $global, $arch) {
# Returns the 'current' junction directory if in use, otherwise
# the version directory.
function link_current($versiondir) {
if (get_config NO_JUNCTIONS) { return $versiondir.ToString() }
if (get_config NO_JUNCTION) { return $versiondir.ToString() }
$currentdir = "$(Split-Path $versiondir)\current"
@@ -932,7 +917,7 @@ function link_current($versiondir) {
# Returns the 'current' junction directory (if it exists),
# otherwise the normal version directory.
function unlink_current($versiondir) {
if (get_config NO_JUNCTIONS) { return $versiondir.ToString() }
if (get_config NO_JUNCTION) { return $versiondir.ToString() }
$currentdir = "$(Split-Path $versiondir)\current"
if (Test-Path $currentdir) {
@@ -1042,7 +1027,7 @@ function Invoke-HookScript {
[ValidateNotNullOrEmpty()]
[PSCustomObject] $Manifest,
[Parameter(Mandatory = $true)]
[ValidateSet('32bit', '64bit')]
[ValidateSet('32bit', '64bit', 'arm64')]
[String] $Arch
)
@@ -1227,14 +1212,16 @@ function persist_permission($manifest, $global) {
# test if there are running processes
function test_running_process($app, $global) {
$processdir = appdir $app $global | Convert-Path
$running_processes = Get-Process | Where-Object { $_.Path -like "$processdir\*" }
$running_processes = Get-Process | Where-Object { $_.Path -like "$processdir\*" } | Out-String
if ($running_processes) {
if (get_config 'ignore_running_processes') {
warn "Application `"$app`" is still running. Scoop is configured to ignore this condition."
if (get_config IGNORE_RUNNING_PROCESSES) {
warn "The following instances of `"$app`" are still running. Scoop is configured to ignore this condition."
Write-Host $running_processes
return $false
} else {
error "Application `"$app`" is still running. Close all instances and try again."
error "The following instances of `"$app`" are still running. Close them and try again."
Write-Host $running_processes
return $true
}
} else {

View File

@@ -98,7 +98,9 @@ function json_path([String] $json, [String] $jsonpath, [Hashtable] $substitution
$jsonpath = substitute $jsonpath $substitutions ($jsonpath -like "*=~*")
}
try {
$obj = [Newtonsoft.Json.Linq.JValue]::Parse($json)
$settings = New-Object -Type Newtonsoft.Json.JsonSerializerSettings
$settings.DateParseHandling = [Newtonsoft.Json.DateParseHandling]::None
$obj = [Newtonsoft.Json.JsonConvert]::DeserializeObject($json, $settings)
} catch [Newtonsoft.Json.JsonReaderException] {
return $null
}

View File

@@ -1,10 +1,14 @@
function manifest_path($app, $bucket) {
fullpath "$(Find-BucketDirectory $bucket)\$(sanitary_path $app).json"
(Get-ChildItem (Find-BucketDirectory $bucket) -Filter "$(sanitary_path $app).json" -Recurse).FullName
}
function parse_json($path) {
if(!(test-path $path)) { return $null }
Get-Content $path -raw -Encoding UTF8 | convertfrom-json -ea stop
if ($null -eq $path -or !(Test-Path $path)) { return $null }
try {
Get-Content $path -Raw -Encoding UTF8 | ConvertFrom-Json -ErrorAction Stop
} catch {
warn "Error parsing JSON at $path."
}
}
function url_manifest($url) {
@@ -20,7 +24,11 @@ function url_manifest($url) {
throw
}
if(!$str) { return $null }
$str | convertfrom-json
try {
$str | ConvertFrom-Json -ErrorAction Stop
} catch {
warn "Error parsing JSON at $url."
}
}
function Get-Manifest($app) {
@@ -94,23 +102,6 @@ function install_info($app, $version, $global) {
parse_json $path
}
function default_architecture {
$arch = get_config 'default_architecture'
$system = if ([Environment]::Is64BitOperatingSystem) { '64bit' } else { '32bit' }
if ($null -eq $arch) {
$arch = $system
} else {
try {
$arch = ensure_architecture $arch
} catch {
warn 'Invalid default architecture configured. Determining default system architecture'
$arch = $system
}
}
return $arch
}
function arch_specific($prop, $manifest, $architecture) {
if ($manifest.architecture) {
$val = $manifest.architecture.$architecture.$prop
@@ -120,8 +111,22 @@ function arch_specific($prop, $manifest, $architecture) {
if ($manifest.$prop) { return $manifest.$prop }
}
function supports_architecture($manifest, $architecture) {
return -not [String]::IsNullOrEmpty((arch_specific 'url' $manifest $architecture))
function Get-SupportedArchitecture($manifest, $architecture) {
if ($architecture -eq 'arm64' -and ($manifest | ConvertToPrettyJson) -notmatch '[''"]arm64["'']') {
# Windows 10 enables existing unmodified x86 apps to run on Arm devices.
# Windows 11 adds the ability to run unmodified x64 Windows apps on Arm devices!
# Ref: https://learn.microsoft.com/en-us/windows/arm/overview
if ($WindowsBuild -ge 22000) {
# Windows 11
$architecture = '64bit'
} else {
# Windows 10
$architecture = '32bit'
}
}
if (![String]::IsNullOrEmpty((arch_specific 'url' $manifest $architecture))) {
return $architecture
}
}
function generate_user_manifest($app, $bucket, $version) {
@@ -137,10 +142,10 @@ function generate_user_manifest($app, $bucket, $version) {
abort "'$app' does not have autoupdate capability`r`ncouldn't find manifest for '$app@$version'"
}
ensure $(usermanifestsdir) | out-null
ensure (usermanifestsdir) | out-null
try {
Invoke-AutoUpdate $app "$(resolve-path $(usermanifestsdir))" $manifest $version $(@{ })
return "$(resolve-path $(usermanifest $app))"
Invoke-AutoUpdate $app "$(Convert-Path (usermanifestsdir))\$app.json" $manifest $version $(@{ })
return Convert-Path (usermanifest $app)
} catch {
write-host -f darkred "Could not install $app@$version"
}

View File

@@ -23,7 +23,7 @@ function install_psmodule($manifest, $dir, $global) {
if (Test-Path $linkfrom) {
warn "$(friendly_path $linkfrom) already exists. It will be replaced."
Remove-Item -Path $linkfrom -Force -ErrorAction SilentlyContinue
Remove-Item -Path $linkfrom -Force -Recurse -ErrorAction SilentlyContinue
}
New-DirectoryJunction $linkfrom $dir | Out-Null
@@ -39,8 +39,8 @@ function uninstall_psmodule($manifest, $dir, $global) {
$linkfrom = "$modulesdir\$module_name"
if (Test-Path $linkfrom) {
Write-Host "Removing $(friendly_path $linkfrom)"
$linkfrom = Resolve-Path $linkfrom
Remove-Item -Path $linkfrom -Force -ErrorAction SilentlyContinue
$linkfrom = Convert-Path $linkfrom
Remove-Item -Path $linkfrom -Force -Recurse -ErrorAction SilentlyContinue
}
}

View File

@@ -20,11 +20,12 @@ function create_startmenu_shortcuts($manifest, $dir, $global, $arch) {
}
function shortcut_folder($global) {
$directory = [System.IO.Path]::Combine([Environment]::GetFolderPath('startmenu'), 'Programs', 'Scoop Apps')
if($global) {
$directory = [System.IO.Path]::Combine([Environment]::GetFolderPath('commonstartmenu'), 'Programs', 'Scoop Apps')
if ($global) {
$startmenu = 'CommonStartMenu'
} else {
$startmenu = 'StartMenu'
}
return $(ensure $directory)
return Convert-Path (ensure ([System.IO.Path]::Combine([Environment]::GetFolderPath($startmenu), 'Programs', 'Scoop Apps')))
}
function startmenu_shortcut([System.IO.FileInfo] $target, $shortcutName, $arguments, [System.IO.FileInfo]$icon, $global) {
@@ -67,18 +68,5 @@ function rm_startmenu_shortcuts($manifest, $global, $arch) {
if(Test-Path -Path $shortcut) {
Remove-Item $shortcut
}
# Before issue 1514 Startmenu shortcut removal
#
# Shortcuts that should have been installed globally would
# have been installed locally up until 27 June 2017.
#
# TODO: Remove this 'if' block and comment after
# 27 June 2018.
if($global) {
$shortcut = "$(shortcut_folder $false)\$name.lnk"
if(Test-Path -Path $shortcut) {
Remove-Item $shortcut
}
}
}
}

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 'rootPath'), (Join-Path $env:HOME "scoop") | Select-Object -first 1
$globaldir = $env:SCOOP_GLOBAL, (get_config 'globalPath'), "/usr/local/scoop" | Select-Object -first 1
$cachedir = $env:SCOOP_CACHE, (get_config 'cachePath'), (Join-Path $scoopdir "cache") | Select-Object -first 1
# core.ps1
function ensure($dir) {
mkdir -p $dir > $null
return resolve-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

@@ -50,7 +50,7 @@ function Select-CurrentVersion { # 'manifest.ps1'
)
process {
$currentPath = "$(appdir $AppName $Global)\current"
if (!(get_config NO_JUNCTIONS)) {
if (!(get_config NO_JUNCTION)) {
$currentVersion = (parse_json "$currentPath\manifest.json").version
if ($currentVersion -eq 'nightly') {
$currentVersion = (Get-Item $currentPath).Target | Split-Path -Leaf
@@ -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

@@ -52,10 +52,10 @@ function add_alias($name, $command) {
# generate script
$shimdir = shimdir $false
$script =
@"
# Summary: $description
$command
"@
@(
"# Summary: $description",
"$command"
) -join "`r`n"
$script | Out-UTF8File "$shimdir\$alias_file.ps1"
# add alias to config

View File

@@ -10,6 +10,8 @@
#
# To clear everything in your cache, use:
# scoop cache rm *
# You can also use the `-a/--all` switch in place of `*` here
param($cmd)
function cacheinfo($file) {
@@ -36,7 +38,7 @@ function cacheremove($app) {
'ERROR: <app(s)> missing'
my_usage
exit 1
} elseif ($app -eq '*') {
} elseif ($app -eq '*' -or $app -eq '-a' -or $app -eq '--all') {
$files = @(Get-ChildItem $cachedir)
} else {
$app = '(' + ($app -join '|') + ')'

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,7 +14,7 @@ if (!$app) { error '<app> missing'; my_usage; exit 1 }
$null, $manifest, $bucket, $url = Get-Manifest $app
if ($manifest) {
$style = get_config cat_style
$style = get_config CAT_STYLE
if ($style) {
$manifest | ConvertToPrettyJson | bat --no-paging --style $style --language json
} else {

View File

@@ -17,6 +17,7 @@ if ($adminPrivileges) {
$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'."
@@ -35,13 +36,13 @@ if (!(Test-HelperInstalled -Helper Dark)) {
$globaldir = New-Object System.IO.DriveInfo($globaldir)
if ($globaldir.DriveFormat -ne 'NTFS') {
error "Scoop requires an NTFS volume to work! Please point `$env:SCOOP_GLOBAL or 'globalPath' variable in '~/.config/scoop/config.json' to another Drive."
error "Scoop requires an NTFS volume to work! Please point `$env:SCOOP_GLOBAL or 'global_path' variable in '~/.config/scoop/config.json' to another Drive."
$issues++
}
$scoopdir = New-Object System.IO.DriveInfo($scoopdir)
if ($scoopdir.DriveFormat -ne 'NTFS') {
error "Scoop requires an NTFS volume to work! Please point `$env:SCOOP or 'rootPath' variable in '~/.config/scoop/config.json' to another Drive."
error "Scoop requires an NTFS volume to work! Please point `$env:SCOOP or 'root_path' variable in '~/.config/scoop/config.json' to another Drive."
$issues++
}

View File

@@ -21,20 +21,20 @@
# Settings
# --------
#
# 7ZIPEXTRACT_USE_EXTERNAL: $true|$false
# use_external_7zip: $true|$false
# External 7zip (from path) will be used for archives extraction.
#
# MSIEXTRACT_USE_LESSMSI: $true|$false
# use_lessmsi: $true|$false
# Prefer lessmsi utility over native msiexec.
#
# NO_JUNCTIONS: $true|$false
# no_junction: $true|$false
# The 'current' version alias will not be used. Shims and shortcuts will point to specific version instead.
#
# SCOOP_REPO: http://github.com/ScoopInstaller/Scoop
# scoop_repo: http://github.com/ScoopInstaller/Scoop
# Git repository containining scoop source code.
# This configuration is useful for custom forks.
#
# SCOOP_BRANCH: master|develop
# scoop_branch: master|develop
# Allow to use different branch than master.
# Could be used for testing specific functionalities before released into all users.
# If you want to receive updates earlier to test new functionalities use develop (see: 'https://github.com/ScoopInstaller/Scoop/issues/2939')
@@ -47,9 +47,13 @@
# * An empty or unset value for proxy is equivalent to 'default' (with no username or password)
# * To bypass the system proxy and connect directly, use 'none' (with no username or password)
#
# default_architecture: 64bit|32bit
# autostash_on_conflict: $true|$false
# When a conflict is detected during updating, Scoop will auto-stash the uncommitted changes.
# (Default is $false, which will abort the update)
#
# 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.
@@ -60,20 +64,20 @@
# show_update_log: $true|$false
# Do not show changed commits on 'scoop update'
#
# manifest_review: $true|$false
# show_manifest: $true|$false
# Displays the manifest of every app that's about to
# be installed, then asks user if they wish to proceed.
#
# shim: kiennq|scoopcs|71
# Choose scoop shim build.
#
# rootPath: $Env:UserProfile\scoop
# root_path: $Env:UserProfile\scoop
# Path to Scoop root directory.
#
# globalPath: $Env:ProgramData\scoop
# global_path: $Env:ProgramData\scoop
# Path to Scoop root directory for global apps.
#
# cachePath:
# cache_path:
# For downloads, defaults to 'cache' folder under Scoop root directory.
#
# gh_token:
@@ -101,6 +105,16 @@
# For example, if you want to access a private GitHub repository,
# you need to add the host to this list with 'match' and 'headers' strings.
#
# hold_update_until:
# Disable/Hold Scoop self-updates, until the specified date.
# `scoop hold scoop` will set the value to one day later.
# 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.
#
# ARIA2 configuration
# -------------------
#
@@ -137,17 +151,39 @@ 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

@@ -1,25 +1,28 @@
# Usage: scoop depends <app>
# Summary: List dependencies for an app
# Summary: List dependencies for an app, in the order they'll be installed
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\depends.ps1" # 'Get-Dependency'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest' (indirectly)
$opt, $apps, $err = getopt $args 'a:' 'arch='
$app = $apps[0]
if(!$app) { error '<app> missing'; my_usage; exit 1 }
$architecture = default_architecture
$architecture = Get-DefaultArchitecture
try {
$architecture = ensure_architecture ($opt.a + $opt.arch)
$architecture = Format-ArchitectureString ($opt.a + $opt.arch)
} catch {
abort "ERROR: $_"
}
$deps = @(Get-Dependency $app $architecture) -ne $app
if($deps) {
$deps[($deps.length - 1)..0]
$deps = @()
Get-Dependency $app $architecture | ForEach-Object {
$dep = [ordered]@{}
$dep.Source, $dep.Name = $_ -split '/'
$deps += [PSCustomObject]$dep
}
$deps
exit 0

View File

@@ -3,6 +3,10 @@
# Help: e.g. The usual way to download an app, without installing it (uses your local 'buckets'):
# scoop download git
#
# To download a different version of the app
# (note that this will auto-generate the manifest using current version):
# scoop download gh@2.7.0
#
# To download an app from a manifest at a URL:
# scoop download https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/runat.json
#
@@ -10,15 +14,15 @@
# scoop download path\to\app.json
#
# Options:
# -f, --force Force download (overwrite cache)
# -h, --no-hash-check Skip hash verification (use with caution!)
# -u, --no-update-scoop Don't update Scoop before downloading if it's outdated
# -a, --arch <32bit|64bit> Use the specified architecture, if the app supports it
# -f, --force Force download (overwrite cache)
# -h, --no-hash-check Skip hash verification (use with caution!)
# -u, --no-update-scoop Don't update Scoop before downloading if it's outdated
# -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' (indirectly)
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture' 'generate_user_manifest' 'Get-Manifest'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest'
. "$PSScriptRoot\..\lib\install.ps1"
$opt, $apps, $err = getopt $args 'fhua:' 'force', 'no-hash-check', 'no-update-scoop', 'arch='
@@ -26,9 +30,9 @@ if ($err) { error "scoop download: $err"; exit 1 }
$check_hash = !($opt.h -or $opt.'no-hash-check')
$use_cache = !($opt.f -or $opt.force)
$architecture = default_architecture
$architecture = Get-DefaultArchitecture
try {
$architecture = ensure_architecture ($opt.a + $opt.arch)
$architecture = Format-ArchitectureString ($opt.a + $opt.arch)
} catch {
abort "ERROR: $_"
}
@@ -81,21 +85,22 @@ 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
}
if(!(supports_architecture $manifest $architecture)) {
error "'$app' doesn't support $architecture architecture!"
$architecture = Get-SupportedArchitecture $manifest $architecture
if ($null -eq $architecture) {
error "'$app' doesn't support current architecture!"
continue
}
if(Test-Aria2Enabled) {
dl_with_cache_aria2 $app $version $manifest $architecture $cachedir $manifest.cookie $use_cache $curr_check_hash
Invoke-CachedAria2Download $app $version $manifest $architecture $cachedir $manifest.cookie $use_cache $curr_check_hash
} else {
foreach($url in script:url $manifest $architecture) {
try {
dl_with_cache $app $version $url $null $manifest.cookie $use_cache
Invoke-CachedDownload $app $version $url $null $manifest.cookie $use_cache
} catch {
write-host -f darkred $_
error "URL $url is not valid"

View File

@@ -1,57 +1,23 @@
# Usage: scoop export > filename
# Summary: Exports (an importable) list of installed apps
# Help: Lists all installed apps.
# Usage: scoop export > scoopfile.json
# Summary: Exports installed apps, buckets (and optionally configs) in JSON format
# Help: Options:
# -c, --config Export the Scoop configuration file too
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\json.ps1" # 'ConvertToPrettyJson'
$def_arch = default_architecture
$export = @{}
$local = installed_apps $false | ForEach-Object { @{ name = $_; global = $false } }
$global = installed_apps $true | ForEach-Object { @{ name = $_; global = $true } }
$apps = @($local) + @($global)
$count = 0
# json
# echo "{["
if($apps) {
$apps | Sort-Object { $_.name } | Where-Object { !$query -or ($_.name -match $query) } | ForEach-Object {
$app = $_.name
$global = $_.global
$ver = Select-CurrentVersion -AppName $app -Global:$global
$global_display = $null; if($global) { $global_display = ' *global*'}
$install_info = install_info $app $ver $global
$bucket = ''
if ($install_info.bucket) {
$bucket = ' [' + $install_info.bucket + ']'
} elseif ($install_info.url) {
$bucket = ' [' + $install_info.url + ']'
}
if ($install_info.architecture -and $def_arch -ne $install_info.architecture) {
$arch = ' {' + $install_info.architecture + '}'
} else {
$arch = ''
}
# json
# $val = "{ 'name': '$app', 'version': '$ver', 'global': $($global.toString().tolower()) }"
# if($count -gt 0) {
# " ," + $val
# } else {
# " " + $val
# }
# "$app (v:$ver) global:$($global.toString().tolower())"
"$app (v:$ver)$global_display$bucket$arch"
$count++
if ($args[0] -eq '-c' -or $args[0] -eq '--config') {
$export.config = $scoopConfig
# Remove machine-specific properties
foreach ($prop in 'last_update', 'root_path', 'global_path', 'cache_path', 'alias') {
$export.config.PSObject.Properties.Remove($prop)
}
}
# json
# echo "]}"
$export.buckets = list_buckets
$export.apps = @(& "$PSScriptRoot\scoop-list.ps1" 6>$null)
$export | ConvertToPrettyJSON
exit 0

View File

@@ -3,41 +3,42 @@
param($cmd)
function print_help($cmd) {
$file = Get-Content (command_path $cmd) -raw
$file = Get-Content (command_path $cmd) -Raw
$usage = usage $file
$summary = summary $file
$help = scoop_help $file
if($usage) { "$usage`n" }
if($help) { $help }
if ($usage) { "$usage`n" }
if ($help) { $help }
}
function print_summaries {
$commands = @{}
$commands = @()
command_files | ForEach-Object {
$command = command_name $_
$summary = summary (Get-Content (command_path $command) -raw)
if(!($summary)) { $summary = '' }
$commands.add("$command ", $summary) # add padding
$command = [ordered]@{}
$command.Command = command_name $_
$command.Summary = summary (Get-Content (command_path $command.Command))
$commands += [PSCustomObject]$command
}
$commands.getenumerator() | Sort-Object name | Format-Table -hidetablehead -autosize -wrap
$commands
}
$commands = commands
if(!($cmd)) {
"Usage: scoop <command> [<args>]
Write-Host "Usage: scoop <command> [<args>]
Some useful commands are:"
Available commands are listed below.
Type 'scoop help <command>' to get more help for a specific command."
print_summaries
"Type 'scoop help <command>' to get help for a specific command."
} elseif($commands -contains $cmd) {
print_help $cmd
} else {
"scoop help: no such command '$cmd'"; exit 1
warn "scoop help: no such command '$cmd'"
exit 1
}
exit 0

View File

@@ -32,6 +32,12 @@ if ($global -and !(is_admin)) {
$apps | ForEach-Object {
$app = $_
if ($app -eq 'scoop') {
$hold_update_until = [System.DateTime]::Now.AddDays(1)
set_config HOLD_UPDATE_UNTIL $hold_update_until.ToString('o') | Out-Null
success "$app is now held and might not be updated until $($hold_update_until.ToLocalTime())."
return
}
if (!(installed $app $global)) {
if ($global) {
error "'$app' is not installed globally."
@@ -41,7 +47,7 @@ $apps | ForEach-Object {
return
}
if (get_config NO_JUNCTIONS) {
if (get_config NO_JUNCTION){
$version = Select-CurrentVersion -App $app -Global:$global
} else {
$version = 'current'

66
libexec/scoop-import.ps1 Normal file
View File

@@ -0,0 +1,66 @@
# Usage: scoop import <path/url to scoopfile.json>
# Summary: Imports apps, buckets and configs from a Scoopfile in JSON format
# Help: To replicate a Scoop installation from a file stored on Desktop, run
# scoop import Desktop\scoopfile.json
param(
[Parameter(Mandatory)]
[String]
$scoopfile
)
. "$PSScriptRoot\..\lib\manifest.ps1"
$import = $null
$bucket_names = @()
$def_arch = Get-DefaultArchitecture
if (Test-Path $scoopfile) {
$import = parse_json $scoopfile
} elseif ($scoopfile -match '^(ht|f)tps?://|\\\\') {
$import = url_manifest $scoopfile
}
if (!$import) { abort 'Input file not a valid JSON.' }
foreach ($item in $import.config.PSObject.Properties) {
set_config $item.Name $item.Value | Out-Null
Write-Host "'$($item.Name)' has been set to '$($item.Value)'"
}
foreach ($item in $import.buckets) {
add_bucket $item.Name $item.Source | Out-Null
$bucket_names += $item.Name
}
foreach ($item in $import.apps) {
$info = $item.Info -Split ', '
$global = if ('Global install' -in $info) {
' --global'
} else {
''
}
$arch = if ('64bit' -in $info -and '64bit' -ne $def_arch) {
' --arch 64bit'
} elseif ('32bit' -in $info -and '32bit' -ne $def_arch) {
' --arch 32bit'
} elseif ('arm64' -in $info -and 'arm64' -ne $def_arch) {
' --arch arm64'
} else {
''
}
$app = if ($item.Source -in $bucket_names) {
"$($item.Source)/$($item.Name)"
} elseif ($item.Source -eq '<auto-generated>') {
"$($item.Name)@$($item.Version)"
} else {
$item.Source
}
& "$PSScriptRoot\scoop-install.ps1" $app$global$arch
if ('Held package' -in $info) {
& "$PSScriptRoot\scoop-hold.ps1" $($item.Name)$global
}
}

View File

@@ -105,6 +105,85 @@ if ($status.installed) {
$installed_output += if ($verbose) { versiondir $app $_ $global } else { "$_$(if ($global) { " *global*" })" }
}
$item.Installed = $installed_output -join "`n"
if ($verbose) {
# Show size of installation
$appsdir = appsdir $global
# Collect file list from each location
$appFiles = Get-ChildItem $appsdir -Filter $app
$currentFiles = Get-ChildItem $appFiles -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#*"
# Get the sum of each file list
$fileTotals = @()
foreach ($fileType in ($appFiles, $currentFiles, $persistFiles, $cacheFiles)) {
if ($null -ne $fileType) {
$fileSum = (Get-ChildItem $fileType -Recurse | Measure-Object -Property Length -Sum).Sum
$fileTotals += coalesce $fileSum 0
} else {
$fileTotals += 0
}
}
# Old versions = app total - current version size
$fileTotals += $fileTotals[0] - $fileTotals[1]
if ($fileTotals[2] + $fileTotals[3] + $fileTotals[4] -eq 0) {
# Simple app size output if no old versions, persisted data, cached downloads
$item.'Installed size' = filesize $fileTotals[1]
} else {
$fileSizes = [ordered] @{
'Current version: ' = $fileTotals[1]
'Old versions: ' = $fileTotals[4]
'Persisted data: ' = $fileTotals[2]
'Cached downloads: ' = $fileTotals[3]
'Total: ' = $fileTotals[0] + $fileTotals[2] + $fileTotals[3]
}
$fileSizeOutput = @()
# Don't output empty categories
$fileSizes.GetEnumerator() | ForEach-Object {
if ($_.Value -ne 0) {
$fileSizeOutput += $_.Key + (filesize $_.Value)
}
}
$item.'Installed size' = $fileSizeOutput -join "`n"
}
}
} else {
if ($verbose) {
# Get download size if app not installed
$totalPackage = 0
foreach ($url in @(url $manifest (Get-DefaultArchitecture))) {
try {
if (Test-Path (fullpath (cache_path $app $manifest.version $url))) {
$cached = " (latest version is cached)"
} else {
$cached = $null
}
[int]$urlLength = (Invoke-WebRequest $url -Method Head).Headers.'Content-Length'[0]
$totalPackage += $urlLength
} catch [System.Management.Automation.RuntimeException] {
$totalPackage = 0
$packageError = "the server at $(([System.Uri]$url).Host) did not send a Content-Length header"
break
} catch {
$totalPackage = 0
$packageError = "the server at $(([System.Uri]$url).Host) is down"
break
}
}
if ($totalPackage -ne 0) {
$item.'Download size' = "$(filesize $totalPackage)$cached"
} else {
$item.'Download size' = "Unknown ($packageError)$cached"
}
}
}
$binaries = @(arch_specific 'bin' $manifest $install.architecture)

View File

@@ -3,6 +3,10 @@
# Help: e.g. The usual way to install an app (uses your local 'buckets'):
# scoop install git
#
# To install a different version of the app
# (note that this will auto-generate the manifest using current version):
# scoop install gh@2.7.0
#
# To install an app from a manifest at a URL:
# scoop install https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/runat.json
#
@@ -10,17 +14,17 @@
# scoop install \path\to\app.json
#
# Options:
# -g, --global Install the app globally
# -i, --independent Don't install dependencies automatically
# -k, --no-cache Don't use the download cache
# -u, --no-update-scoop Don't update Scoop before installing if it's outdated
# -s, --skip Skip hash validation (use with caution!)
# -a, --arch <32bit|64bit> Use the specified architecture, if the app supports it
# -g, --global Install the app globally
# -i, --independent Don't install dependencies automatically
# -k, --no-cache Don't use the download cache
# -u, --no-update-scoop Don't update Scoop before installing if it's outdated
# -s, --skip Skip hash validation (use with caution!)
# -a, --arch <32bit|64bit|arm64> Use the specified architecture, if the app supports it
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' 'manifest.ps1' (indirectly)
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'default_architecture' 'generate_user_manifest' 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'generate_user_manifest' 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\decompress.ps1"
. "$PSScriptRoot\..\lib\shortcuts.ps1"
@@ -35,9 +39,9 @@ $global = $opt.g -or $opt.global
$check_hash = !($opt.s -or $opt.skip)
$independent = $opt.i -or $opt.independent
$use_cache = !($opt.k -or $opt.'no-cache')
$architecture = default_architecture
$architecture = Get-DefaultArchitecture
try {
$architecture = ensure_architecture ($opt.a + $opt.arch)
$architecture = Format-ArchitectureString ($opt.a + $opt.arch)
} catch {
abort "ERROR: $_"
}

View File

@@ -6,7 +6,7 @@ param($query)
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\manifest.ps1" # 'parse_json' 'Select-CurrentVersion' (indirectly)
$def_arch = default_architecture
$def_arch = Get-DefaultArchitecture
if (-not (Get-FormatData ScoopApps)) {
Update-FormatData "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
}

View File

@@ -3,6 +3,8 @@
# Help: Used to resolve conflicts in favor of a particular app. For example,
# if you've installed 'python' and 'python27', you can use 'scoop reset' to switch between
# using one or the other.
#
# You can use '*' in place of <app> or `-a`/`--all` switch to reset all apps.
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Select-CurrentVersion' (indirectly)
@@ -10,12 +12,13 @@
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
. "$PSScriptRoot\..\lib\shortcuts.ps1"
$opt, $apps, $err = getopt $args
$opt, $apps, $err = getopt $args 'a' 'all'
if($err) { "scoop reset: $err"; exit 1 }
$all = $opt.a -or $opt.all
if(!$apps) { error '<app> missing'; my_usage; exit 1 }
if(!$apps -and !$all) { error '<app> missing'; my_usage; exit 1 }
if($apps -eq '*') {
if($apps -eq '*' -or $all) {
$local = installed_apps $false | ForEach-Object { ,@($_, $false) }
$global = installed_apps $true | ForEach-Object { ,@($_, $true) }
$apps = @($local) + @($global)
@@ -60,7 +63,7 @@ $apps | ForEach-Object {
write-host "Resetting $app ($version)."
$dir = resolve-path (versiondir $app $version $global)
$dir = Convert-Path (versiondir $app $version $global)
$original_dir = $dir
$persist_dir = persistdir $app $global

View File

@@ -9,35 +9,43 @@ param($query)
. "$PSScriptRoot\..\lib\manifest.ps1" # 'manifest'
. "$PSScriptRoot\..\lib\versions.ps1" # 'Get-LatestVersion'
function bin_match($manifest, $query) {
if(!$manifest.bin) { return $false }
foreach($bin in $manifest.bin) {
$exe, $alias, $args = $bin
$fname = split-path $exe -leaf -ea stop
$list = @()
if((strip_ext $fname) -match $query) { return $fname }
if($alias -match $query) { return $alias }
try {
$query = New-Object Regex $query, 'IgnoreCase'
} catch {
abort "Invalid regular expression: $($_.Exception.InnerException.Message)"
}
$githubtoken = Get-GitHubToken
$authheader = @{}
if ($githubtoken) {
$authheader = @{'Authorization' = "token $githubtoken"}
}
function bin_match($manifest, $query) {
if (!$manifest.bin) { return $false }
$bins = foreach ($bin in $manifest.bin) {
$exe, $alias, $args = $bin
$fname = Split-Path $exe -Leaf -ErrorAction Stop
if ((strip_ext $fname) -match $query) { $fname }
elseif ($alias -match $query) { $alias }
}
$false
if ($bins) { return $bins }
else { return $false }
}
function search_bucket($bucket, $query) {
$apps = apps_in_bucket (Find-BucketDirectory $bucket) | ForEach-Object {
@{ name = $_ }
}
if($query) {
try {
$query = new-object regex $query, 'IgnoreCase'
} catch {
abort "Invalid regular expression: $($_.exception.innerexception.message)"
}
$apps = apps_in_bucket (Find-BucketDirectory $bucket) | ForEach-Object { @{ name = $_ } }
if ($query) {
$apps = $apps | Where-Object {
if($_.name -match $query) { return $true }
if ($_.name -match $query) { return $true }
$bin = bin_match (manifest $_.name $bucket) $query
if($bin) {
$_.bin = $bin; return $true;
if ($bin) {
$_.bin = $bin
return $true
}
}
}
@@ -46,14 +54,19 @@ function search_bucket($bucket, $query) {
function download_json($url) {
$ProgressPreference = 'SilentlyContinue'
$result = Invoke-WebRequest $url -UseBasicParsing | Select-Object -ExpandProperty content | ConvertFrom-Json
$result = Invoke-WebRequest $url -UseBasicParsing -Headers $authheader | Select-Object -ExpandProperty content | ConvertFrom-Json
$ProgressPreference = 'Continue'
$result
}
function github_ratelimit_reached {
$api_link = 'https://api.github.com/rate_limit'
(download_json $api_link).rate.remaining -eq 0
$ret = (download_json $api_link).rate.remaining -eq 0
if ($ret) {
Write-Host "GitHub API rate limit reached.
Please try again later or configure your API token using 'scoop config gh_token <your token>'."
}
$ret
}
function search_remote($bucket, $query) {
@@ -72,44 +85,59 @@ function search_remote($bucket, $query) {
function search_remotes($query) {
$buckets = known_bucket_repos
$names = $buckets | get-member -m noteproperty | Select-Object -exp name
$names = $buckets | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name
$results = $names | Where-Object { !(test-path $(Find-BucketDirectory $_)) } | ForEach-Object {
@{"bucket" = $_; "results" = (search_remote $_ $query)}
$results = $names | Where-Object { !(Test-Path $(Find-BucketDirectory $_)) } | ForEach-Object {
@{ "bucket" = $_; "results" = (search_remote $_ $query) }
} | Where-Object { $_.results }
if ($results.count -gt 0) {
"Results from other known buckets..."
"(add them using 'scoop bucket add <name>')"
""
Write-Host "Results from other known buckets...
(add them using 'scoop bucket add <bucket name>')"
}
$results | ForEach-Object {
"'$($_.bucket)' bucket (install using 'scoop install $($_.bucket)/<app>'):"
$_.results | ForEach-Object { " $_" }
""
$name = $_.bucket
$_.results | ForEach-Object {
$item = [ordered]@{}
$item.Name = $_
$item.Source = $name
$list += [PSCustomObject]$item
}
}
$list
}
Get-LocalBucket | ForEach-Object {
$res = search_bucket $_ $query
$local_results = $local_results -or $res
if($res) {
if ($res) {
$name = "$_"
Write-Host "'$name' bucket:"
$res | ForEach-Object {
$item = " $($_.name) ($($_.version))"
if($_.bin) { $item += " --> includes '$($_.bin)'" }
$item
$item = [ordered]@{}
$item.Name = $_.name
$item.Version = $_.version
$item.Source = $name
$item.Binaries = ""
if ($_.bin) { $item.Binaries = $_.bin -join ' | ' }
$list += [PSCustomObject]$item
}
""
}
}
if ($list.Length -gt 0) {
Write-Host "Results from local buckets..."
$list
}
if (!$local_results -and !(github_ratelimit_reached)) {
$remote_results = search_remotes $query
if(!$remote_results) { [console]::error.writeline("No matches found."); exit 1 }
if (!$remote_results) {
warn "No matches found."
exit 1
}
$remote_results
}

View File

@@ -1,18 +1,18 @@
# Usage: scoop shim <subcommand> [<shim_names>] [<command_path> [<args>...]] [-g(lobal)]
# Usage: scoop shim <subcommand> [<shim_name>...] [options] [other_args]
# Summary: Manipulate Scoop shims
# Help: Manipulate Scoop shims: add, rm, list, info, alter, etc.
# Help: Available subcommands: add, rm, list, info, alter.
#
# To add a custom shim, use the 'add' subcommand:
#
# scoop shim add <shim_name> <command_path> [<args>...]
#
# To remove a shim, use the 'rm' subcommand (CAUTION: this could remove shims added by an app manifest):
# To remove shims, use the 'rm' subcommand: (CAUTION: this could remove shims added by an app manifest)
#
# scoop shim rm <shim_names>
# scoop shim rm <shim_name> [<shim_name>...]
#
# To list all shims or matching shims, use the 'list' subcommand:
#
# scoop shim list [<shim_names>]
# scoop shim list [<shim_name>/<pattern>...]
#
# To show a shim's information, use the 'info' subcommand:
#
@@ -23,58 +23,38 @@
# scoop shim alter <shim_name>
#
# Options:
# -g(lobal) Add/Remove/Info/Alter global shim(s)
# (NOTICE: USING SINGLE DASH)
# (HINT: To pass arguments like '-g' or '-global' to the shim, use quotes)
# -g, --global Manipulate global shim(s)
#
# HINT: The FIRST double-hyphen '--', if any, will be treated as the POSIX-style command option terminator
# and will NOT be included in arguments, so if you want to pass arguments like '-g' or '--global' to
# the shim, put them after a '--'. Note that in PowerShell, you must use a QUOTED '--', e.g.,
#
# scoop shim add myapp 'D:\path\myapp.exe' '--' myapp_args --global
param($SubCommand, $ShimName, [Switch]$global)
param($SubCommand)
. "$PSScriptRoot\..\lib\getopt.ps1"
. "$PSScriptRoot\..\lib\install.ps1" # for rm_shim
if ($SubCommand -notin @('add', 'rm', 'list', 'info', 'alter')) {
'ERROR: <subcommand> must be one of: add, rm, list, info, alter'
my_usage
exit 1
}
if ($SubCommand -ne 'list' -and !$ShimName) {
"ERROR: <shim_name> must be specified for subcommand '$SubCommand'"
my_usage
exit 1
}
if ($Args) {
switch ($SubCommand) {
'add' {
if ($Args[0] -like '-*') {
"ERROR: <command_path> must be specified for subcommand 'add'"
my_usage
exit 1
} else {
if (($Args -join ' ') -match "^'(.*?)'\s*(.*?)$") {
$commandPath = $Matches[1]
$commandArgs = $Matches[2]
} else {
$commandPath = $Args[0]
if ($Args.Length -gt 1) {
$commandArgs = $Args[1..($Args.Length - 1)]
}
}
}
}
'rm' {
$ShimName = @($ShimName) + $Args
}
'list' {
$ShimName = (@($ShimName) + $Args) -join '|'
}
default {
# For 'info' and 'alter'
"ERROR: Option $Args not recognized."
my_usage
exit 1
}
if (!$SubCommand) {
error '<subcommand> missing'
} else {
error "'$SubCommand' is not one of available subcommands: add, rm, list, info, alter"
}
my_usage
exit 1
}
$opt, $other, $err = getopt $Args 'g' 'global'
if ($err) { "scoop shim: $err"; exit 1 }
$global = $opt.g -or $opt.global
if ($SubCommand -ne 'list' -and $other.Length -eq 0) {
error "<shim_name> must be specified for subcommand '$SubCommand'"
my_usage
exit 1
}
if (-not (Get-FormatData ScoopShims)) {
@@ -110,6 +90,16 @@ function Get-ShimPath($ShimName, $Global) {
switch ($SubCommand) {
'add' {
if ($other.Length -lt 2 -or $other[1] -eq '') {
error "<command_path> must be specified for subcommand 'add'"
my_usage
exit 1
}
$shimName = $other[0]
$commandPath = $other[1]
if ($other.Length -gt 2) {
$commandArgs = $other[2..($other.Length - 1)]
}
if ($commandPath -notmatch '[\\/]') {
$shortPath = $commandPath
$commandPath = Get-ShimTarget (Get-ShimPath $shortPath $global)
@@ -126,12 +116,14 @@ switch ($SubCommand) {
Write-Host '...'
shim $commandPath $global $shimName $commandArgs
} else {
abort "ERROR: '$($Args[0])' does not exist" 3
Write-Host "ERROR: Command path does not exist: " -ForegroundColor Red -NoNewline
Write-Host $($other[1]) -ForegroundColor Cyan
exit 3
}
}
'rm' {
$failed = @()
$ShimName | ForEach-Object {
$other | ForEach-Object {
if (Get-ShimPath $_ $global) {
rm_shim $_ (shimdir $global)
} else {
@@ -139,61 +131,82 @@ switch ($SubCommand) {
}
}
if ($failed) {
Write-Host 'Shims not found: ' -NoNewline
Write-Host $failed -ForegroundColor Cyan
$failed | ForEach-Object {
Write-Host "ERROR: $(if ($global) { 'Global' } else {'Local' }) shim not found: " -ForegroundColor Red -NoNewline
Write-Host $_ -ForegroundColor Cyan
}
exit 3
}
}
'list' {
$shims = Get-ChildItem -Path $localShimDir -Recurse -Include '*.shim', '*.ps1' |
Where-Object { !$ShimName -or ($_.BaseName -match $ShimName) } |
Select-Object -ExpandProperty FullName
$other = @($other) -ne '*'
# Validate all given patterns before matching.
$other | ForEach-Object {
try {
$pattern = $_
[Regex]::New($pattern)
} catch {
Write-Host "ERROR: Invalid pattern: " -ForegroundColor Red -NoNewline
Write-Host $pattern -ForegroundColor Magenta
exit 1
}
}
$pattern = $other -join '|'
$shims = @()
if (!$global) {
$shims += Get-ChildItem -Path $localShimDir -Recurse -Include '*.shim', '*.ps1' |
Where-Object { !$pattern -or ($_.BaseName -match $pattern) } |
Select-Object -ExpandProperty FullName
}
if (Test-Path $globalShimDir) {
$shims += Get-ChildItem -Path $globalShimDir -Recurse -Include '*.shim', '*.ps1' |
Where-Object { !$ShimName -or ($_.BaseName -match $ShimName) } |
Where-Object { !$pattern -or ($_.BaseName -match $pattern) } |
Select-Object -ExpandProperty FullName
}
$shims.ForEach({ Get-ShimInfo $_ }) | Add-Member -TypeName 'ScoopShims' -PassThru
}
'info' {
$shimPath = Get-ShimPath $ShimName $global
$shimName = $other[0]
$shimPath = Get-ShimPath $shimName $global
if ($shimPath) {
Get-ShimInfo $shimPath
} else {
Write-Host "$(if ($global) { 'Global' } else { 'Local' }) shim not found: " -NoNewline
Write-Host $ShimName -ForegroundColor Cyan
if (Get-ShimPath $ShimName (!$global)) {
Write-Host "ERROR: $(if ($global) { 'Global' } else { 'Local' }) shim not found: " -ForegroundColor Red -NoNewline
Write-Host $shimName -ForegroundColor Cyan
if (Get-ShimPath $shimName (!$global)) {
Write-Host "But a $(if ($global) { 'local' } else {'global' }) shim exists, " -NoNewline
Write-Host "run 'scoop shim info $ShimName$(if (!$global) { ' -global' })' to show its info"
Write-Host "run 'scoop shim info $shimName$(if (!$global) { ' --global' })' to show its info"
exit 2
}
exit 3
}
}
'alter' {
$shimPath = Get-ShimPath $ShimName $global
$shimName = $other[0]
$shimPath = Get-ShimPath $shimName $global
if ($shimPath) {
$shimInfo = Get-ShimInfo $shimPath
if ($null -eq $shimInfo.Alternatives) {
Write-Host 'No alternatives of ' -NoNewline
Write-Host $ShimName -ForegroundColor Cyan -NoNewline
Write-Host ' found.'
Write-Host 'ERROR: No alternatives of ' -ForegroundColor Red -NoNewline
Write-Host $shimName -ForegroundColor Cyan -NoNewline
Write-Host ' found.' -ForegroundColor Red
exit 2
}
$shimInfo.Alternatives = $shimInfo.Alternatives.Split(' ')
[System.Management.Automation.Host.ChoiceDescription[]]$altApps = 1..$shimInfo.Alternatives.Length | ForEach-Object {
New-Object System.Management.Automation.Host.ChoiceDescription "&$($_)`b$($shimInfo.Alternatives[$_ - 1])", "Sets '$ShimName' shim from $($shimInfo.Alternatives[$_ - 1])."
New-Object System.Management.Automation.Host.ChoiceDescription "&$($_)`b$($shimInfo.Alternatives[$_ - 1])", "Sets '$shimName' shim from $($shimInfo.Alternatives[$_ - 1])."
}
$selected = $Host.UI.PromptForChoice("Alternatives of '$ShimName' command", "Please choose one that provides '$ShimName' as default:", $altApps, 0)
$selected = $Host.UI.PromptForChoice("Alternatives of '$shimName' command", "Please choose one that provides '$shimName' as default:", $altApps, 0)
if ($selected -eq 0) {
Write-Host $ShimName -ForegroundColor Cyan -NoNewline
Write-Host 'INFO: ' -ForegroundColor Blue -NoNewline
Write-Host $shimName -ForegroundColor Cyan -NoNewline
Write-Host ' is already from ' -NoNewline
Write-Host $shimInfo.Source -ForegroundColor DarkYellow -NoNewline
Write-Host ', nothing changed.'
} else {
$newApp = $shimInfo.Alternatives[$selected]
Write-Host 'Use ' -NoNewline
Write-Host $ShimName -ForegroundColor Cyan -NoNewline
Write-Host $shimName -ForegroundColor Cyan -NoNewline
Write-Host ' from ' -NoNewline
Write-Host $newApp -ForegroundColor DarkYellow -NoNewline
Write-Host ' as default...' -NoNewline
@@ -211,11 +224,11 @@ switch ($SubCommand) {
Write-Host 'done.'
}
} else {
Write-Host "$(if ($global) { 'Global' } else { 'Local' }) shim not found: " -NoNewline
Write-Host $ShimName -ForegroundColor Cyan
if (Get-ShimPath $ShimName (!$global)) {
Write-Host "ERROR: $(if ($global) { 'Global' } else { 'Local' }) shim not found: " -ForegroundColor Red -NoNewline
Write-Host $shimName -ForegroundColor Cyan
if (Get-ShimPath $shimName (!$global)) {
Write-Host "But a $(if ($global) { 'local' } else {'global' }) shim exists, " -NoNewline
Write-Host "run 'scoop shim alter $ShimName$(if (!$global) { ' -global' })' to alternate its source"
Write-Host "run 'scoop shim alter $shimName$(if (!$global) { ' --global' })' to alternate its source"
exit 2
}
exit 3

View File

@@ -1,5 +1,8 @@
# Usage: scoop status
# Summary: Show status and check for new app versions
# Help: Options:
# -l, --local Checks the status for only the locally installed apps,
# and disables remote fetching/checking for Scoop and buckets
. "$PSScriptRoot\..\lib\manifest.ps1" # 'manifest' 'parse_json' "install_info"
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
@@ -7,24 +10,45 @@
# check if scoop needs updating
$currentdir = fullpath $(versiondir 'scoop' 'current')
$needs_update = $false
$bucket_needs_update = $false
$script:network_failure = $false
$no_remotes = $args[0] -eq '-l' -or $args[0] -eq '--local'
if (!(Get-Command git -ErrorAction SilentlyContinue)) { $no_remotes = $true }
$list = @()
if (!(Get-FormatData ScoopStatus)) {
Update-FormatData "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
}
if (Test-Path "$currentdir\.git") {
git_cmd -C "`"$currentdir`"" fetch -q origin
$commits = $(git -C $currentdir log "HEAD..origin/$(get_config SCOOP_BRANCH)" --oneline)
if ($commits) { $needs_update = $true }
} else {
$needs_update = $true
function Test-UpdateStatus($repopath) {
if (Test-Path "$repopath\.git") {
git_cmd -C "`"$repopath`"" 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
if ($commits) { return $true }
else { return $false }
} else {
return $true
}
}
if (!$no_remotes) {
$needs_update = Test-UpdateStatus $currentdir
foreach ($bucket in Get-LocalBucket) {
if (Test-UpdateStatus (Find-BucketDirectory $bucket -Root)) {
$bucket_needs_update = $true
break
}
}
}
if ($needs_update) {
warn "Scoop is out of date. Run 'scoop update' to get the latest changes."
} else { success 'Scoop is up to date.' }
$failed = @()
$outdated = @()
$removed = @()
$missing_deps = @()
$onhold = @()
warn "Scoop out of date. Run 'scoop update' to get the latest changes."
} elseif ($bucket_needs_update) {
warn "Scoop bucket(s) out of date. Run 'scoop update' to get the latest changes."
} elseif (!$script:network_failure -and !$no_remotes) {
success 'Scoop is up to date.'
}
$true, $false | ForEach-Object { # local and global apps
$global = $_
@@ -34,64 +58,26 @@ $true, $false | ForEach-Object { # local and global apps
Get-ChildItem $dir | Where-Object name -NE 'scoop' | ForEach-Object {
$app = $_.name
$status = app_status $app $global
if ($status.failed) {
$failed += @{ $app = $status.version }
}
if ($status.removed) {
$removed += @{ $app = $status.version }
}
if ($status.outdated) {
$outdated += @{ $app = @($status.version, $status.latest_version) }
if ($status.hold) {
$onhold += @{ $app = @($status.version, $status.latest_version) }
}
}
if ($status.missing_deps) {
$missing_deps += , (@($app) + @($status.missing_deps))
}
if (!$status.outdated -and !$status.failed -and !$status.removed -and !$status.missing_deps) { return }
$item = [ordered]@{}
$item.Name = $app
$item.'Installed Version' = $status.version
$item.'Latest Version' = if ($status.outdated) { $status.latest_version } else { "" }
$item.'Missing Dependencies' = $status.missing_deps -Split ' ' -Join ' | '
$info = @()
if ($status.failed) { $info += 'Install failed' }
if ($status.hold) { $info += 'Held package' }
if ($status.removed) { $info += 'Manifest removed' }
$item.Info = $info -join ', '
$list += [PSCustomObject]$item
}
}
if ($outdated) {
Write-Host -f DarkCyan 'Updates are available for:'
$outdated.keys | ForEach-Object {
$versions = $outdated.$_
" $_`: $($versions[0]) -> $($versions[1])"
}
}
if ($onhold) {
Write-Host -f DarkCyan 'These apps are outdated and on hold:'
$onhold.keys | ForEach-Object {
$versions = $onhold.$_
" $_`: $($versions[0]) -> $($versions[1])"
}
}
if ($removed) {
Write-Host -f DarkCyan 'These app manifests have been removed:'
$removed.keys | ForEach-Object {
" $_"
}
}
if ($failed) {
Write-Host -f DarkCyan 'These apps failed to install:'
$failed.keys | ForEach-Object {
" $_"
}
}
if ($missing_deps) {
Write-Host -f DarkCyan 'Missing runtime dependencies:'
$missing_deps | ForEach-Object {
$app, $deps = $_
" '$app' requires '$([string]::join("', '", $deps))'"
}
}
if (!$old -and !$removed -and !$failed -and !$missing_deps -and !$needs_update) {
if ($list.Length -eq 0 -and !$needs_update -and !$bucket_needs_update -and !$script:network_failure) {
success 'Everything is ok!'
}
$list | Add-Member -TypeName ScoopStatus -PassThru
exit 0

View File

@@ -32,6 +32,11 @@ if ($global -and !(is_admin)) {
$apps | ForEach-Object {
$app = $_
if ($app -eq 'scoop') {
set_config HOLD_UPDATE_UNTIL $null | Out-Null
success "$app is no longer held and can be updated again."
return
}
if (!(installed $app $global)) {
if ($global) {
error "'$app' is not installed globally."
@@ -41,7 +46,7 @@ $apps | ForEach-Object {
return
}
if (get_config NO_JUNCTIONS) {
if (get_config NO_JUNCTION){
$version = Select-CurrentVersion -App $app -Global:$global
} else {
$version = 'current'

View File

@@ -54,16 +54,18 @@ if(($PSVersionTable.PSVersion.Major) -lt 5) {
Write-Output "Upgrade PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows"
break
}
$show_update_log = get_config SHOW_UPDATE_LOG $true
function update_scoop($show_update_log) {
# Test if Scoop Core is hold
if(Test-ScoopCoreOnHold) {
return
}
function update_scoop() {
# check for git
if(!(Test-CommandAvailable git)) { abort "Scoop uses Git to update itself. Run 'scoop install git' and try again." }
if (!(Test-CommandAvailable git)) { abort "Scoop uses Git to update itself. Run 'scoop install git' and try again." }
Write-Host "Updating Scoop..."
$last_update = $(last_scoop_update)
if ($null -eq $last_update) {$last_update = [System.DateTime]::Now}
$last_update = $last_update.ToString('s')
$show_update_log = get_config 'show_update_log' $true
$currentdir = fullpath $(versiondir 'scoop' 'current')
if (!(Test-Path "$currentdir\.git")) {
$newdir = "$currentdir\..\new"
@@ -98,6 +100,17 @@ function update_scoop() {
$isRepoChanged = !($currentRepo -match $configRepo)
$isBranchChanged = !($currentBranch -match "\*\s+$configBranch")
# Stash uncommitted changes
if (git -C "$currentdir" 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
} else {
warn "Uncommitted changes detected. Update aborted."
return
}
}
# Change remote url if the repo is changed
if ($isRepoChanged) {
git -C "$currentdir" config remote.origin.url "$configRepo"
@@ -135,6 +148,11 @@ function update_scoop() {
# }
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." }
foreach ($bucket in Get-LocalBucket) {
Write-Host "Updating '$bucket' bucket..."
@@ -165,9 +183,6 @@ function update_scoop() {
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"
}
}
set_config lastupdate ([System.DateTime]::Now.ToString('o')) | Out-Null
success 'Scoop was updated successfully!'
}
function update($app, $global, $quiet = $false, $independent, $suggested, $use_cache = $true, $check_hash = $true) {
@@ -176,7 +191,7 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
$install = install_info $app $old_version $global
# re-use architecture, bucket and url from first install
$architecture = ensure_architecture $install.architecture
$architecture = Format-ArchitectureString $install.architecture
$bucket = $install.bucket
if ($null -eq $bucket) {
$bucket = 'main'
@@ -187,7 +202,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
}
@@ -210,12 +225,12 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
# Remove and replace whole region after proper fix
Write-Host "Downloading new version"
if (Test-Aria2Enabled) {
dl_with_cache_aria2 $app $version $manifest $architecture $cachedir $manifest.cookie $true $check_hash
Invoke-CachedAria2Download $app $version $manifest $architecture $cachedir $manifest.cookie $true $check_hash
} else {
$urls = script:url $manifest $architecture
foreach ($url in $urls) {
dl_with_cache $app $version $url $null $manifest.cookie $true
Invoke-CachedDownload $app $version $url $null $manifest.cookie $true
if ($check_hash) {
$manifest_hash = hash_for_url $manifest $url $architecture
@@ -243,6 +258,8 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
$dir = versiondir $app $old_version $global
$persist_dir = persistdir $app $global
Invoke-HookScript -HookType 'pre_uninstall' -Manifest $old_manifest -Arch $architecture
#region Workaround for #2952
if (test_running_process $app $global) {
return
@@ -260,6 +277,8 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
# directory.
$refdir = unlink_current $dir
uninstall_psmodule $old_manifest $refdir $global
if ($force -and ($old_version -eq $version)) {
if (!(Test-Path "$dir/../_$version.old")) {
Move-Item "$dir" "$dir/../_$version.old"
@@ -272,6 +291,8 @@ function update($app, $global, $quiet = $false, $independent, $suggested, $use_c
}
}
Invoke-HookScript -HookType 'post_uninstall' -Manifest $old_manifest -Arch $architecture
if ($bucket) {
# add bucket name it was installed from
$app = "$bucket/$app"
@@ -300,7 +321,10 @@ if (-not ($apps -or $all)) {
error 'scoop update: --no-cache is invalid when <app> is not specified.'
exit 1
}
update_scoop
update_scoop $show_update_log
update_bucket $show_update_log
set_config LAST_UPDATE ([System.DateTime]::Now.ToString('o')) | Out-Null
success 'Scoop was updated successfully!'
} else {
if ($global -and !(is_admin)) {
'ERROR: You need admin rights to update global apps.'; exit 1
@@ -312,7 +336,10 @@ if (-not ($apps -or $all)) {
$apps_param = $apps
if ($updateScoop) {
update_scoop
update_scoop $show_update_log
update_bucket $show_update_log
set_config LAST_UPDATE ([System.DateTime]::Now.ToString('o')) | Out-Null
success 'Scoop was updated successfully!'
}
if ($apps_param -eq '*' -or $all) {
@@ -336,7 +363,7 @@ if (-not ($apps -or $all)) {
} else {
warn "'$app' is held to version $($status.version)"
}
} elseif ($apps_param -ne '*') {
} elseif ($apps_param -ne '*' -and !$all) {
if ($status.installed) {
ensure_none_failed $app
Write-Host "$app`: $($status.version) (latest version)" -ForegroundColor Green

View File

@@ -2,7 +2,7 @@
# Summary: Look for app's hash or url on virustotal.com
# Help: Look for app's hash or url on virustotal.com
#
# Use a single '*' for app to check all installed apps.
# Use a single '*' or the '-a/--all' switch to check all installed apps.
#
# To use this command, you have to sign up to VirusTotal's community,
# and get an API key. Then, tell scoop about your API key with:
@@ -20,7 +20,7 @@
# 2 & 4 combined
#
# Options:
# -a, --arch <32bit|64bit> Use the specified architecture, if the app supports it
# -a, --all Check for all installed apps
# -s, --scan For packages where VirusTotal has no information, send download URL
# for analysis (and future retrieval). This requires you to configure
# your virustotal_api_key.
@@ -34,10 +34,10 @@
. "$PSScriptRoot\..\lib\install.ps1" # 'hash_for_url'
. "$PSScriptRoot\..\lib\depends.ps1" # 'Get-Dependency'
$opt, $apps, $err = getopt $args 'a:snup' @('arch=', 'scan', 'no-depends', 'no-update-scoop', 'passthru')
$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 }
$architecture = ensure_architecture ($opt.a + $opt.arch)
$architecture = Format-ArchitectureString
if (is_scoop_outdated) {
if ($opt.u -or $opt.'no-update-scoop') {
@@ -49,7 +49,7 @@ if (is_scoop_outdated) {
$apps_param = $apps
if ($apps_param -eq '*') {
if ($apps_param -eq '*' -or $opt.a -or $opt.all) {
$apps = installed_apps $false
$apps += installed_apps $true
}
@@ -66,7 +66,7 @@ $_ERR_NO_API_KEY = 16
$exit_code = 0
# Global API key:
$api_key = get_config virustotal_api_key
$api_key = get_config VIRUSTOTAL_API_KEY
if (!$api_key) {
abort ("VirusTotal API key is not configured`n" +
" You could get one from https://www.virustotal.com/gui/my-apikey and set with`n" +

View File

@@ -179,19 +179,11 @@
"type": "array"
},
"autoupdateArch": {
"type": "object",
"additionalProperties": false,
"properties": {
"url": {
"$ref": "#/definitions/autoupdateUriOrArrayOfAutoupdateUris"
},
"hash": {
"$ref": "#/definitions/hashExtractionOrArrayOfHashExtractions"
},
"extract_dir": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"extract_to": {
"$ref": "#/definitions/stringOrArrayOfStrings"
"bin": {
"$ref": "#/definitions/stringOrArrayOfStringsOrAnArrayOfArrayOfStrings"
},
"env_add_path": {
"$ref": "#/definitions/stringOrArrayOfStrings"
@@ -199,71 +191,97 @@
"env_set": {
"type": "object"
},
"bin": {
"$ref": "#/definitions/stringOrArrayOfStringsOrAnArrayOfArrayOfStrings"
"extract_dir": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"shortcuts": {
"$ref": "#/definitions/shortcutsArray"
"hash": {
"$ref": "#/definitions/hashExtractionOrArrayOfHashExtractions"
},
"installer": {
"type": "object",
"additionalProperties": false,
"properties": {
"file": {
"type": "string"
}
},
"type": "object"
}
},
"post_install": {
"$ref": "#/definitions/stringOrArrayOfStrings"
"shortcuts": {
"$ref": "#/definitions/shortcutsArray"
},
"psmodule": {
"url": {
"$ref": "#/definitions/autoupdateUriOrArrayOfAutoupdateUris"
}
}
},
"autoupdate": {
"type": "object",
"additionalProperties": false,
"properties": {
"architecture": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
"32bit": {
"$ref": "#/definitions/autoupdateArch"
},
"64bit": {
"$ref": "#/definitions/autoupdateArch"
},
"arm64": {
"$ref": "#/definitions/autoupdateArch"
}
},
}
},
"bin": {
"$ref": "#/definitions/stringOrArrayOfStringsOrAnArrayOfArrayOfStrings"
},
"env_add_path": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"env_set": {
"type": "object"
},
"persist": {
"$ref": "#/definitions/stringOrArrayOfStringsOrAnArrayOfArrayOfStrings"
"extract_dir": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"hash": {
"$ref": "#/definitions/hashExtractionOrArrayOfHashExtractions"
},
"installer": {
"type": "object",
"additionalProperties": false,
"properties": {
"file": {
"type": "string"
}
}
},
"license": {
"$ref": "#/definitions/license"
},
"notes": {
"$ref": "#/definitions/stringOrArrayOfStrings"
}
},
"type": "object"
},
"autoupdate": {
"anyOf": [
{
"$ref": "#/definitions/autoupdateArch"
},
{
"persist": {
"$ref": "#/definitions/stringOrArrayOfStringsOrAnArrayOfArrayOfStrings"
},
"psmodule": {
"type": "object",
"additionalProperties": false,
"properties": {
"notes": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"architecture": {
"type": "object",
"additionalProperties": false,
"properties": {
"32bit": {
"$ref": "#/definitions/autoupdateArch"
},
"64bit": {
"$ref": "#/definitions/autoupdateArch"
}
}
"name": {
"type": "string"
}
}
},
"shortcuts": {
"$ref": "#/definitions/shortcutsArray"
},
"url": {
"$ref": "#/definitions/autoupdateUriOrArrayOfAutoupdateUris"
}
],
"type": "object"
}
},
"checkver": {
"anyOf": [
@@ -315,6 +333,25 @@
"script": {
"$ref": "#/definitions/stringOrArrayOfStrings",
"description": "Custom PowerShell script to retrieve application version using more complex approach."
},
"sourceforge": {
"anyOf": [
{
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"project": {
"type": "string"
},
"path": {
"type": "string"
}
},
"type": "object"
}
]
}
},
"type": "object"
@@ -508,6 +545,9 @@
},
"64bit": {
"$ref": "#/definitions/architecture"
},
"arm64": {
"$ref": "#/definitions/architecture"
}
},
"type": "object"
@@ -614,6 +654,34 @@
"type": "string"
}
},
"if": {
"properties": {
"architecture": {
"properties": {
"64bit": {
"properties": {
"url": false
}
},
"32bit": {
"properties": {
"url": false
}
},
"arm64": {
"properties": {
"url": false
}
}
}
}
}
},
"then": {
"required": [
"url"
]
},
"required": [
"version",
"homepage",

View File

@@ -60,5 +60,34 @@
</TableRowEntries>
</TableControl>
</View>
<View>
<Name>ScoopStatusType</Name>
<ViewSelectedBy>
<TypeName>ScoopStatus</TypeName>
</ViewSelectedBy>
<TableControl>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem>
<PropertyName>Name</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>Installed Version</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>Latest Version</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>Missing Dependencies</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>Info</PropertyName>
</TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
</Configuration>

View File

@@ -16,7 +16,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,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Net.Compilers.Toolset" version="4.0.1" targetFramework="net45" developmentDependency="true" />
<package id="Microsoft.Net.Compilers.Toolset" version="4.2.0" targetFramework="net45" developmentDependency="true" />
</packages>

View File

@@ -1,36 +1,36 @@
<?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.0.1\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.0.1\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">
<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>
<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>
<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>
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props'))" />
</Target>
<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,4 +1,4 @@
7f912b28a07c226e0be3acfb2f57f050538aba0100fa1f0bf2c39f1a1f1da814 *Newtonsoft.Json.dll
cff8fc4ce358d7daff84ab47129a776797a4ec819c1586a15bd5e63144f5b73f *Newtonsoft.Json.Schema.dll
0d6b228378cbabff23a30456d22f1a31337c466f90cf8b7997cc48bd171155f3 *Scoop.Validator.dll
b624949df8b0e3a6153fdfb730a7c6f4990b6592ee0d922e1788433d276610f3 *Newtonsoft.Json.dll
9abb57d73d82a2d77008321a85aff2b62e5ac68bebb54ece8668c96cc112e36b *Newtonsoft.Json.Schema.dll
0318c8221ce4d44806f8def619bcc02886be0902aab80080e6251c50c6ca53a9 *Scoop.Validator.dll
40a70bee96d108701f8f2e81392f9b79fd003f1cb4e1653ad2429753153fd7ee *validator.exe

View File

@@ -1,4 +1,4 @@
3398094ce429ab5dcdecf2ad04803230669bb4accaef7083992e9b87afac55841ba8def2a5168358bd17e60799e55d076b0e5ca44c86b9e6c91150d3dc37c721 *Newtonsoft.Json.dll
298d3d0b656acbb1fe5ed0c3abb49a640c47889184ab7bd4b594e51a7d7f829d5c8685edbd10a286fd56bfd8d601b9f187da463a5a9c8509365eddaea280642f *Newtonsoft.Json.Schema.dll
afabe1df6ab837395a5da5ec8dd12bf3f36a8512b76e6f751c14045544246980e9d4061d437792836db792864b7db2761e84f1bf65bac688657a862b68fc7b45 *Scoop.Validator.dll
2fdf035661f349206f58ea1feed8805b7f9517a21f9c113e7301c69de160f184c774350a12a710046e3ff6baa37345d319b6f47fd24fbba4e042d54014bee511 *Newtonsoft.Json.dll
855ab2e30c9d523c9f321ae861c5969244185f660fa47e05cec96df8e2970d19843dbd3d89a0fca845544641915d1adf4b4a2145ef568dd99da7791e5064d70e *Newtonsoft.Json.Schema.dll
338793e6127330c0b05728291fcf18441127ffb56e1bd5c0f0588cd7436605f4b852f4bb622f655896a7eb7b1262add142b200fd5f37391b47d1401becb6b81c *Scoop.Validator.dll
d497c27b48f44f4cff270d3c8801b0cecc74108f8786a4a7c40e57541308ae33a69f5456cfc43ae1ce4214038d20da9fbeac1bcf76cc58d972863b58dab18401 *validator.exe

View File

@@ -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="12.0.3" targetFramework="net45" />
<package id="Newtonsoft.Json.Schema" version="3.0.14" targetFramework="net45" />
<package id="Microsoft.Net.Compilers.Toolset" version="4.0.1" targetFramework="net45" developmentDependency="true" />
<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" />
</packages>

View File

@@ -1,48 +1,48 @@
<?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.0.1\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.0.1\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>{8EB9B38A-1BAB-4D89-B4CB-ACE5E7C1073B}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>Scoop.Validator</RootNamespace>
<AssemblyName>Scoop.Validator</AssemblyName>
<TargetFrameworkVersion>v4.5.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<HintPath>packages\Newtonsoft.Json.12.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.14\lib\net45\Newtonsoft.Json.Schema.dll</HintPath>
<Private>True</Private>
</Reference>
<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="validator.cs" />
<Compile Include="Scoop.Validator.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<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>
<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>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8EB9B38A-1BAB-4D89-B4CB-ACE5E7C1073B}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>Scoop.Validator</RootNamespace>
<AssemblyName>Scoop.Validator</AssemblyName>
<TargetFrameworkVersion>v4.5.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props'))" />
</Target>
<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>
<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>
<Private>True</Private>
</Reference>
<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="validator.cs" />
<Compile Include="Scoop.Validator.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</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,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,54 @@
#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
$changedManifests = @(Get-GitChangedFile -Path $bucketDir -Include '*.json' -Commit $env:BHCommitHash)
}
$manifestFiles = (Get-ChildItem $bucketDir -Filter '*.json' -Recurse).FullName
if ($changedManifests) {
$manifestFiles = $manifestFiles | Where-Object { $_ -in $changedManifests }
}
$manifest_files = Get-ChildItem $bucketdir *.json
$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 '(unformated)' }
)
$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 = Get-Content -Raw $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")"
}
}
}

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

@@ -0,0 +1,189 @@
param(
[String] $TestPath = "$PSScriptRoot\.."
)
BeforeDiscovery {
$project_file_exclusions = @(
'[\\/]\.git[\\/]',
'\.sublime-workspace$',
'\.DS_Store$',
'supporting(\\|/)validator(\\|/)packages(\\|/)*',
'supporting(\\|/)shimexe(\\|/)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,45 @@
. "$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 alias if alias exists' {
$alias_file = "$shimdir\scoop-rm.ps1"
New-Item $alias_file -Type File -Force
$alias_file | Should -Exist
add_alias 'rm' 'test'
& $alias_file | Should -Not -Be 'test'
}
It 'Removes an existing alias' {
$alias_file = "$shimdir\scoop-rm.ps1"
add_alias 'rm' '"hello, world!"'
$alias_file | Should -Exist
Mock get_config { @(@{'rm' = 'scoop-rm' }) }
rm_alias 'rm'
$alias_file | Should -Not -Exist
}
}

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,8 @@
. "$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\install.ps1"
}
Describe 'Get-AppFilePath' -Tag 'Scoop' {
BeforeAll {
@@ -128,15 +126,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 +141,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 +154,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 +163,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
}
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 +183,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 +197,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
}
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,7 +216,7 @@ 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
@@ -229,11 +224,11 @@ Describe 'get_app_name_from_shim' -Tag 'Scoop' {
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 +241,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,35 +258,33 @@ 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
}
Describe 'ensure_robocopy_in_path' -Tag 'Scoop', 'Windows' {
BeforeAll {
$shimdir = shimdir $false
Mock versiondir { "$PSScriptRoot\.." }
}
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
It 'shims robocopy when not on path' {
Mock Test-CommandAvailable { $false }
Test-CommandAvailable robocopy | Should -Be $false
ensure_robocopy_in_path
ensure_robocopy_in_path
# "$shimdir/robocopy.ps1" | should -not -exist
"$shimdir/robocopy.exe" | Should -Not -Exist
}
# "$shimdir/robocopy.ps1" | should -exist
"$shimdir/robocopy.exe" | Should -Exist
# clean up
rm_shim robocopy $(shimdir $false) | Out-Null
}
It 'does not shim robocopy when it is in path' {
Mock Test-CommandAvailable { $true }
Test-CommandAvailable robocopy | Should -Be $true
ensure_robocopy_in_path
# "$shimdir/robocopy.ps1" | should -not -exist
"$shimdir/robocopy.exe" | Should -Not -Exist
}
}
@@ -397,3 +390,40 @@ Describe 'app' -Tag 'Scoop' {
$version | Should -Be '1.8.0-rc2'
}
}
Describe 'Format Architecture String' -Tag 'Scoop' {
It 'should keep correct architectures' {
Format-ArchitectureString '32bit' | Should -Be '32bit'
Format-ArchitectureString '32' | Should -Be '32bit'
Format-ArchitectureString 'x86' | Should -Be '32bit'
Format-ArchitectureString 'X86' | Should -Be '32bit'
Format-ArchitectureString 'i386' | Should -Be '32bit'
Format-ArchitectureString '386' | Should -Be '32bit'
Format-ArchitectureString 'i686' | Should -Be '32bit'
Format-ArchitectureString '64bit' | Should -Be '64bit'
Format-ArchitectureString '64' | Should -Be '64bit'
Format-ArchitectureString 'x64' | Should -Be '64bit'
Format-ArchitectureString 'X64' | Should -Be '64bit'
Format-ArchitectureString 'amd64' | Should -Be '64bit'
Format-ArchitectureString 'AMD64' | Should -Be '64bit'
Format-ArchitectureString 'x86_64' | Should -Be '64bit'
Format-ArchitectureString 'x86-64' | Should -Be '64bit'
Format-ArchitectureString 'arm64' | Should -Be 'arm64'
Format-ArchitectureString 'arm' | Should -Be 'arm64'
Format-ArchitectureString 'aarch64' | Should -Be 'arm64'
Format-ArchitectureString 'ARM64' | Should -Be 'arm64'
Format-ArchitectureString 'ARM' | Should -Be 'arm64'
Format-ArchitectureString 'AARCH64' | Should -Be 'arm64'
}
It 'should fallback to the default architecture on empty input' {
Format-ArchitectureString '' | Should -Be $(Get-DefaultArchitecture)
Format-ArchitectureString $null | Should -Be $(Get-DefaultArchitecture)
}
It 'should show an error with an invalid architecture' {
{ Format-ArchitectureString 'PPC' } | Should -Throw "Invalid architecture: 'ppc'"
}
}

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 '16507166814dbd02be80c14b737eb6b0245c47439ca3ed308b5625d64babecc8'
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
}
}
@@ -44,16 +47,19 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
$test5_1 = "$working_dir\7ZipTest5.7z.001"
$test5_2 = "$working_dir\7ZipTest5.7z.002"
$test5_3 = "$working_dir\7ZipTest5.7z.003"
$test6_1 = "$working_dir\7ZipTest6.part01.rar"
$test6_2 = "$working_dir\7ZipTest6.part02.rar"
$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
@@ -67,21 +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 'works with "-Removal" switch ($removal param)' -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)' {
$test1 | Should -Exist
test_extract 'Expand-7zipArchive' $test1 $true
$test1 | Should -Not -Exist
@@ -92,6 +105,13 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
$test5_1 | Should -Not -Exist
$test5_2 | Should -Not -Exist
$test5_3 | Should -Not -Exist
$test6_1 | Should -Exist
$test6_2 | Should -Exist
$test6_3 | Should -Exist
test_extract 'Expand-7zipArchive' $test6_1 $true
$test6_1 | Should -Not -Exist
$test6_2 | Should -Not -Exist
$test6_3 | Should -Not -Exist
}
}
@@ -109,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
@@ -142,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
@@ -150,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
@@ -175,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
@@ -195,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' {
@@ -32,7 +34,7 @@ Describe 'Package Dependencies' -Tag 'Scoop' {
Context 'InstallationHelper function' {
BeforeAll {
$working_dir = setup_working 'format/formated'
$working_dir = setup_working 'format/formatted'
$manifest1 = parse_json (Join-Path $working_dir '3-array-with-single-and-multi.json')
$manifest2 = parse_json (Join-Path $working_dir '4-script-block.json')
Mock Test-HelperInstalled { $false }
@@ -46,14 +48,14 @@ Describe 'Package Dependencies' -Tag 'Scoop' {
Get-InstallationHelper -Manifest $manifest2 -Architecture '32bit' | Should -Be @('7zip')
}
It 'Helpers reflect config changes' {
Mock get_config { $false } -ParameterFilter { $name -eq 'MSIEXTRACT_USE_LESSMSI' }
Mock get_config { $true } -ParameterFilter { $name -eq '7ZIPEXTRACT_USE_EXTERNAL' }
Mock get_config { $false } -ParameterFilter { $name -eq 'USE_LESSMSI' }
Mock get_config { $true } -ParameterFilter { $name -eq 'USE_EXTERNAL_7ZIP' }
Get-InstallationHelper -Manifest $manifest1 -Architecture '32bit' | Should -BeNullOrEmpty
Get-InstallationHelper -Manifest $manifest2 -Architecture '32bit' | Should -BeNullOrEmpty
}
It 'Not return installed helpers' {
Mock get_config { $true } -ParameterFilter { $name -eq 'MSIEXTRACT_USE_LESSMSI' }
Mock get_config { $false } -ParameterFilter { $name -eq '7ZIPEXTRACT_USE_EXTERNAL' }
Mock get_config { $true } -ParameterFilter { $name -eq 'USE_LESSMSI' }
Mock get_config { $false } -ParameterFilter { $name -eq 'USE_EXTERNAL_7ZIP' }
Mock Test-HelperInstalled { $true }-ParameterFilter { $Helper -eq '7zip' }
Mock Test-HelperInstalled { $false }-ParameterFilter { $Helper -eq 'Lessmsi' }
Get-InstallationHelper -Manifest $manifest1 -Architecture '32bit' | Should -Be @('lessmsi')
@@ -68,7 +70,7 @@ Describe 'Package Dependencies' -Tag 'Scoop' {
Context 'Dependencies resolution' {
BeforeAll {
Mock Test-HelperInstalled { $false }
Mock get_config { $true } -ParameterFilter { $name -eq 'MSIEXTRACT_USE_LESSMSI' }
Mock get_config { $true } -ParameterFilter { $name -eq 'USE_LESSMSI' }
Mock Get-Manifest { 'lessmsi', @{}, $null, $null } -ParameterFilter { $app -eq 'lessmsi' }
Mock Get-Manifest { '7zip', @{ url = 'test.msi' }, $null, $null } -ParameterFilter { $app -eq '7zip' }
Mock Get-Manifest { 'innounp', @{}, $null, $null } -ParameterFilter { $app -eq 'innounp' }

View File

@@ -1,22 +0,0 @@
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
Describe 'Pretty json formating' -Tag 'Scoop' {
BeforeAll {
$format = "$PSScriptRoot\fixtures\format"
$manifests = Get-ChildItem "$format\formated" -File -Filter '*.json'
}
Context 'Beautify manifest' {
$manifests | ForEach-Object {
if ($PSVersionTable.PSVersion.Major -gt 5) { $_ = $_.Name } # Fix for pwsh
It "$_" {
$pretty_json = (parse_json "$format\unformated\$_") | ConvertToPrettyJson
$correct = (Get-Content "$format\formated\$_") -join "`r`n"
$correct.CompareTo($pretty_json) | Should -Be 0
}
}
}
}

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' {
@@ -68,4 +70,18 @@ Describe 'getopt' -Tag 'Scoop' {
$err | Should -BeNullOrEmpty
$opt.'long-arg' | Should -Be 'test'
}
It 'handles the option terminator' {
$opt, $rem, $err = getopt '--long-arg', '--' '' 'long-arg'
$err | Should -BeNullOrEmpty
$opt.'long-arg' | Should -BeTrue
$rem[0] | Should -BeNullOrEmpty
$opt, $rem, $err = getopt '--long-arg', '--', '-x', '-y' 'xy' 'long-arg'
$err | Should -BeNullOrEmpty
$opt.'long-arg' | Should -BeTrue
$opt.'x' | Should -BeNullOrEmpty
$opt.'y' | Should -BeNullOrEmpty
$rem[0] | Should -Be '-x'
$rem[1] | Should -Be '-y'
}
}

View File

@@ -1,40 +1,8 @@
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
. "$PSScriptRoot\..\lib\unix.ps1"
$isUnix = is_unix
Describe 'ensure_architecture' -Tag 'Scoop' {
It 'should keep correct architectures' {
ensure_architecture '32bit' | Should -Be '32bit'
ensure_architecture '32' | Should -Be '32bit'
ensure_architecture 'x86' | Should -Be '32bit'
ensure_architecture 'X86' | Should -Be '32bit'
ensure_architecture 'i386' | Should -Be '32bit'
ensure_architecture '386' | Should -Be '32bit'
ensure_architecture 'i686' | Should -Be '32bit'
ensure_architecture '64bit' | Should -Be '64bit'
ensure_architecture '64' | Should -Be '64bit'
ensure_architecture 'x64' | Should -Be '64bit'
ensure_architecture 'X64' | Should -Be '64bit'
ensure_architecture 'amd64' | Should -Be '64bit'
ensure_architecture 'AMD64' | Should -Be '64bit'
ensure_architecture 'x86_64' | Should -Be '64bit'
ensure_architecture 'x86-64' | Should -Be '64bit'
}
It 'should fallback to the default architecture on empty input' {
ensure_architecture '' | Should -Be $(default_architecture)
ensure_architecture $null | Should -Be $(default_architecture)
}
It 'should show an error with an invalid architecture' {
{ ensure_architecture 'PPC' } | Should -Throw
{ ensure_architecture 'PPC' } | Should -Throw "Invalid architecture: 'ppc'"
}
BeforeAll {
. "$PSScriptRoot\Scoop-TestLib.ps1"
. "$PSScriptRoot\..\lib\core.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
. "$PSScriptRoot\..\lib\install.ps1"
}
Describe 'appname_from_url' -Tag 'Scoop' {
@@ -65,8 +33,8 @@ 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
@@ -75,18 +43,20 @@ Describe 'is_in_dir' -Tag 'Scoop' {
}
}
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
# store the original path to prevent leakage of tests
$origPath = $env:PATH
}
$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 {
It 'should concat the correct path' {
Mock add_first_in_path {}
Mock remove_from_path {}
@@ -152,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

@@ -0,0 +1,86 @@
BeforeAll {
. "$PSScriptRoot\..\lib\json.ps1"
. "$PSScriptRoot\..\lib\manifest.ps1"
}
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 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
}
}
}
Describe 'Handle ARM64 and correctly fallback' -Tag 'Scoop' {
It 'Should return "arm64" if supported' {
$manifest1 = @{ url = 'test'; architecture = @{ 'arm64' = @{ pre_install = 'test' } } }
$manifest2 = @{ url = 'test'; pre_install = "'arm64'" }
$manifest3 = @{ architecture = @{ 'arm64' = @{ url = 'test' } } }
Get-SupportedArchitecture $manifest1 'arm64' | Should -Be 'arm64'
Get-SupportedArchitecture $manifest2 'arm64' | Should -Be 'arm64'
Get-SupportedArchitecture $manifest3 'arm64' | Should -Be 'arm64'
}
It 'Should return "64bit" if unsupported on Windows 11' {
$WindowsBuild = 22000
$manifest1 = @{ url = 'test' }
$manifest2 = @{ architecture = @{ '64bit' = @{ url = 'test' } } }
Get-SupportedArchitecture $manifest1 'arm64' | Should -Be '64bit'
Get-SupportedArchitecture $manifest2 'arm64' | Should -Be '64bit'
}
It 'Should return "32bit" if unsupported on Windows 10' {
$WindowsBuild = 19044
$manifest2 = @{ url = 'test' }
$manifest1 = @{ url = 'test'; architecture = @{ '64bit' = @{ pre_install = 'test' } } }
$manifest3 = @{ architecture = @{ '64bit' = @{ url = 'test' } } }
Get-SupportedArchitecture $manifest1 'arm64' | Should -Be '32bit'
Get-SupportedArchitecture $manifest2 'arm64' | Should -Be '32bit'
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 = $(Resolve-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 $(Resolve-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

Binary file not shown.