mirror of
https://github.com/ScoopInstaller/Scoop.git
synced 2025-12-11 18:46:02 +00:00
Compare commits
119 Commits
fix-bucket
...
scoop-diag
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08ff0f6ae0 | ||
|
|
148eed6a67 | ||
|
|
f8b11b1f03 | ||
|
|
164918a190 | ||
|
|
de2b42e38b | ||
|
|
a8c3582d79 | ||
|
|
631b71c581 | ||
|
|
98fd86e420 | ||
|
|
3a3f41c556 | ||
|
|
15f9bbec97 | ||
|
|
ab34b7fb61 | ||
|
|
863af42d4e | ||
|
|
b3c05e71fa | ||
|
|
acc271d115 | ||
|
|
6d79d62cc8 | ||
|
|
00c92b04d6 | ||
|
|
becc7a7b76 | ||
|
|
6898773a8d | ||
|
|
353137f0a9 | ||
|
|
43579714cc | ||
|
|
aa09601503 | ||
|
|
6a35a22b0b | ||
|
|
0b4919ca32 | ||
|
|
efdd6dd7ca | ||
|
|
3dfb4bfd97 | ||
|
|
053321c7b7 | ||
|
|
62f52d9afa | ||
|
|
dfbdb57ed9 | ||
|
|
1d140585a4 | ||
|
|
52059ca1ac | ||
|
|
cddc52e03b | ||
|
|
682a1e2c07 | ||
|
|
2accaae5d1 | ||
|
|
a20bb4f1a6 | ||
|
|
ad0f6178d0 | ||
|
|
41620bb169 | ||
|
|
559c6f9e64 | ||
|
|
7826d6fe2d | ||
|
|
8acfeeefcf | ||
|
|
c00dd42cae | ||
|
|
3f11454a3c | ||
|
|
0a39de86e2 | ||
|
|
c44e214743 | ||
|
|
7c6aeb240e | ||
|
|
32ca856f63 | ||
|
|
1d0bd434ab | ||
|
|
54e3613fca | ||
|
|
e2558ace75 | ||
|
|
68760de1e8 | ||
|
|
257304bbc7 | ||
|
|
52f9ce3a81 | ||
|
|
6369ba60ba | ||
|
|
af5ffcddab | ||
|
|
360daa706a | ||
|
|
f93028001f | ||
|
|
c60df9cdad | ||
|
|
ac71c6e1b7 | ||
|
|
0f795733d8 | ||
|
|
29ed3cb050 | ||
|
|
d7bfe52122 | ||
|
|
29e5898a45 | ||
|
|
ea6c73880a | ||
|
|
e4f9734b88 | ||
|
|
1630e5f908 | ||
|
|
2474ab73e4 | ||
|
|
d65fee6d26 | ||
|
|
01fe9ccd63 | ||
|
|
1c6ab39e90 | ||
|
|
c71376e12c | ||
|
|
c9dc41e7c4 | ||
|
|
5e6a9eeaa0 | ||
|
|
b308769b14 | ||
|
|
1eea29b0d1 | ||
|
|
7dcb7c0030 | ||
|
|
9fda5428ae | ||
|
|
d0cbc36a58 | ||
|
|
512ab44029 | ||
|
|
1f0f687a39 | ||
|
|
ec8161df6c | ||
|
|
24301ac028 | ||
|
|
8aee6f9980 | ||
|
|
9baf293ab6 | ||
|
|
7f47f662e2 | ||
|
|
7a599f062f | ||
|
|
146dab60d1 | ||
|
|
04595b417a | ||
|
|
782f3f1aa6 | ||
|
|
122fdc1a1c | ||
|
|
373007870c | ||
|
|
6fc65ed864 | ||
|
|
bfef3d8298 | ||
|
|
5ad35d6054 | ||
|
|
740322f74f | ||
|
|
e06c7f0c81 | ||
|
|
1985a05b59 | ||
|
|
dea9ebb01a | ||
|
|
08ecdd16a8 | ||
|
|
a9e5a974dd | ||
|
|
8619ee7603 | ||
|
|
bd123939dc | ||
|
|
ec04dd07bc | ||
|
|
4a31bd3302 | ||
|
|
5b5daa5ee9 | ||
|
|
9b4ee8795d | ||
|
|
7bfef4912c | ||
|
|
ca19d7b856 | ||
|
|
f945e20167 | ||
|
|
4d261e7349 | ||
|
|
c3b43625fa | ||
|
|
34da8507a8 | ||
|
|
288aee9ee9 | ||
|
|
eac520a4d8 | ||
|
|
6ae0d5eb8f | ||
|
|
9e2e2526fb | ||
|
|
664e667bed | ||
|
|
80b52e32a1 | ||
|
|
b4e0ff16a6 | ||
|
|
c5702ddd19 | ||
|
|
f992f049cc |
4
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
@@ -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)"`]
|
||||
|
||||
|
||||
8
.github/dependabot.yml
vendored
Normal file
8
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
# ~/.github/dependabot.yml
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/" # == /.github/workflows/
|
||||
schedule:
|
||||
interval: "daily"
|
||||
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -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
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
*.log
|
||||
.DS_Store
|
||||
._.DS_Store
|
||||
scoop.sublime-workspace
|
||||
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -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,
|
||||
|
||||
171
CHANGELOG.md
171
CHANGELOG.md
@@ -2,16 +2,181 @@
|
||||
|
||||
### Features
|
||||
|
||||
- **scoop-update:** Add support for parallel syncing buckets in PowerShell 7 and improve output ([#5122](https://github.com/ScoopInstaller/Scoop/issues/5122))
|
||||
- **bucket:** Switch nirsoft bucket to ScoopInstaller/Nirsoft ([#5328](https://github.com/ScoopInstaller/Scoop/issues/5328))
|
||||
- **config:** Support portable config file ([#5369](https://github.com/ScoopInstaller/Scoop/issues/5369))
|
||||
- **bucket:** Make official buckets higher priority ([#5398](https://github.com/ScoopInstaller/Scoop/issues/5398))
|
||||
- **core:** Add `-Quiet` switch for `Invoke-ExternalCommand` ([#5346](https://github.com/ScoopInstaller/Scoop/issues/5346))
|
||||
- **core:** Allow global install of PowerShell modules ([#5611](https://github.com/ScoopInstaller/Scoop/issues/5611))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **shim:** Remove console window for GUI applications ([#5559](https://github.com/ScoopInstaller/Scoop/issues/5559))
|
||||
- **decompress:** Exclude '*.nsis' that may cause error ([#5294](https://github.com/ScoopInstaller/Scoop/issues/5294))
|
||||
- **autoupdate:** Fix file hash extraction ([#5295](https://github.com/ScoopInstaller/Scoop/issues/5295))
|
||||
- **getopt:** Stop split arguments in `getopt()` and ensure array by explicit arguments type ([#5326](https://github.com/ScoopInstaller/Scoop/issues/5326))
|
||||
- **shortcuts:** Output correctly formatted path ([#5333](https://github.com/ScoopInstaller/Scoop/issues/5333))
|
||||
- **core:** Fix scripts' calling parameters ([#5365](https://github.com/ScoopInstaller/Scoop/issues/5365))
|
||||
- **core:** Fix `is_in_dir` under Unix ([#5391](https://github.com/ScoopInstaller/Scoop/issues/5391))
|
||||
- **core:** Rewrite config file when needed ([#5439](https://github.com/ScoopInstaller/Scoop/issues/5439))
|
||||
- **core:** Prevents leaking HTTP(S)_PROXY env vars to current sessions after Invoke-Git in parallel execution ([#5436](https://github.com/ScoopInstaller/Scoop/pull/5436))
|
||||
- **core:** Handle scoop aliases and broken(edited,copied) shim ([#5551](https://github.com/ScoopInstaller/Scoop/issues/5551))
|
||||
- **env:** Avoid automatic expansion of `%%` in env ([#5395](https://github.com/ScoopInstaller/Scoop/issues/5395), [#5452](https://github.com/ScoopInstaller/Scoop/pull/5452), [#5631](https://github.com/ScoopInstaller/Scoop/pull/5631))
|
||||
- **install:** Fix download from private GitHub repositories ([#5361](https://github.com/ScoopInstaller/Scoop/issues/5361))
|
||||
- **install:** Avoid error when unlinking non-existent junction/hardlink ([#5552](https://github.com/ScoopInstaller/Scoop/issues/5552))
|
||||
- **scoop-info:** Fix errors in file size collection when `--verbose` ([#5352](https://github.com/ScoopInstaller/Scoop/pull/5352))
|
||||
- **shim:** Use bash executable directly ([#5433](https://github.com/ScoopInstaller/Scoop/issues/5433))
|
||||
- **core:** Avoid error messages when deleting non-existent environment variable ([#5547](https://github.com/ScoopInstaller/Scoop/issues/5547))
|
||||
- **core:** Use relative path as fallback of `$scoopdir` ([#5544](https://github.com/ScoopInstaller/Scoop/issues/5544))
|
||||
- **core:** Fix detection of Git ([#5545](https://github.com/ScoopInstaller/Scoop/issues/5545))
|
||||
- **scoop-checkup:** Skip defender check in Windows Sandbox ([#5519](https://github.com/ScoopInstaller/Scoop/issues/5519))
|
||||
- **buckets:** Avoid error messages for unexpected dir ([#5549](https://github.com/ScoopInstaller/Scoop/issues/5549))
|
||||
- **scoop-virustotal:** Fix `scoop-virustotal` when `--all` has been passed without app ([#5593](https://github.com/ScoopInstaller/Scoop/pull/5593))
|
||||
- **scoop-checkup:** Change the message level of helpers from ERROR to WARN ([#5549](https://github.com/ScoopInstaller/Scoop/issues/5614))
|
||||
- **scoop-(un)hold:** Correct output the messages when manifest not found, (already|not) held ([#5519](https://github.com/ScoopInstaller/Scoop/issues/5519))
|
||||
- **scoop-update:** Change error message to a better instruction ([#5677](https://github.com/ScoopInstaller/Scoop/issues/5677))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
- **decompress:** Disable progress bar to improve `Expand-Archive` performance ([#5410](https://github.com/ScoopInstaller/Scoop/issues/5410))
|
||||
- **scoop-search:** Improve performance for local search ([#5324](https://github.com/ScoopInstaller/Scoop/issues/5324))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
- **git:** Use Invoke-Git() with direct path to git.exe to prevent spawning shim subprocesses ([#5122](https://github.com/ScoopInstaller/Scoop/issues/5122), [#5375](https://github.com/ScoopInstaller/Scoop/issues/5375))
|
||||
- **scoop-download:** Output more detailed manifest information ([#5277](https://github.com/ScoopInstaller/Scoop/issues/5277))
|
||||
|
||||
### Builds
|
||||
|
||||
- **checkver:** Read the private_host config variable ([#5381](https://github.com/ScoopInstaller/Scoop/issues/5381))
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
- **dependabot:** Add dependabot.yml for GitHub Actions ([#5377](https://github.com/ScoopInstaller/Scoop/pull/5377))
|
||||
|
||||
### Tests
|
||||
|
||||
- **bucket:** Skip manifest validation if no manifest changes ([#5270](https://github.com/ScoopInstaller/Scoop/issues/5270))
|
||||
|
||||
### Documentation
|
||||
|
||||
- **scoop-info:** Fix help message([#5445](https://github.com/ScoopInstaller/Scoop/issues/5445))
|
||||
- **readme:** Improve documentation language ([#5638](https://github.com/ScoopInstaller/Scoop/issues/5638))
|
||||
|
||||
## [v0.3.1](https://github.com/ScoopInstaller/Scoop/compare/v0.3.0...v0.3.1) - 2022-11-15
|
||||
|
||||
### Features
|
||||
|
||||
- **config:** Allow Scoop to check if apps versioned as 'nightly' are outdated ([#5166](https://github.com/ScoopInstaller/Scoop/issues/5166))
|
||||
- **checkup:** Add Windows Developer Mode check ([#5233](https://github.com/ScoopInstaller/Scoop/issues/5233))
|
||||
- **bucket:** Add 'sysinternals' bucket to known ([#5237](https://github.com/ScoopInstaller/Scoop/issues/5237))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **decompress:** Use PS's default 'Expand-Archive()' ([#5185](https://github.com/ScoopInstaller/Scoop/issues/5185))
|
||||
- **hash:** Fix SourceForge's hash extraction ([#5189](https://github.com/ScoopInstaller/Scoop/issues/5189))
|
||||
- **decompress:** Trim ending '/' ([#5195](https://github.com/ScoopInstaller/Scoop/issues/5195))
|
||||
- **shim:** Exit if shim creating failed 'cause no git ([#5225](https://github.com/ScoopInstaller/Scoop/issues/5225))
|
||||
- **scoop-import:** Add correct architecture argument ([#5210](https://github.com/ScoopInstaller/Scoop/issues/5210))
|
||||
- **scoop-config:** Output `[DateTime]` as `[String]` ([#5232](https://github.com/ScoopInstaller/Scoop/issues/5232))
|
||||
- **shim:** fixed shim add bug related to Resolve-Path ([#5492](https://github.com/ScoopInstaller/Scoop/issues/5492))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
- **hash:** Use `Get-FileHash()` directly ([#5177](https://github.com/ScoopInstaller/Scoop/issues/5177))
|
||||
- **installer:** Drop the old installer ([#5186](https://github.com/ScoopInstaller/Scoop/issues/5186))
|
||||
- **unix:** Remove `unix.ps1` ([#5235](https://github.com/ScoopInstaller/Scoop/issues/5235))
|
||||
|
||||
### Builds
|
||||
|
||||
- **auto-pr:** Add `CommitMessageFormat` option ([#5171](https://github.com/ScoopInstaller/Scoop/issues/5171))
|
||||
- **checkver:** Support XML default namespace ([#5191](https://github.com/ScoopInstaller/Scoop/issues/5191))
|
||||
- **pssa:** Remove unused 'ExcludeRules' ([#5201](https://github.com/ScoopInstaller/Scoop/issues/5201))
|
||||
- **schema:** Add 'installer' and 'shortcuts' to 'autoupdate' ([#5220](https://github.com/ScoopInstaller/Scoop/issues/5220))
|
||||
- **checkhashes:** Use correct version number if `UseCache` ([#5240](https://github.com/ScoopInstaller/Scoop/issues/5240))
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
- **module:** Update modules version ([#5209](https://github.com/ScoopInstaller/Scoop/issues/5209))
|
||||
|
||||
### Tests
|
||||
|
||||
- **unix:** Fix tests in Linux and macOS ([#5179](https://github.com/ScoopInstaller/Scoop/issues/5179))
|
||||
- **pester:** Update to Pester 5 ([#5222](https://github.com/ScoopInstaller/Scoop/issues/5222))
|
||||
- **bucket:** Use BuildHelpers' EnvVars ([#5226](https://github.com/ScoopInstaller/Scoop/issues/5226))
|
||||
|
||||
### Documentation
|
||||
|
||||
- **scoop-cat:** Fix help message([#5224](https://github.com/ScoopInstaller/Scoop/issues/5224))
|
||||
|
||||
## [v0.3.0](https://github.com/ScoopInstaller/Scoop/compare/v0.2.4...v0.3.0) - 2022-10-10
|
||||
|
||||
### Features
|
||||
|
||||
- **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))
|
||||
- **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))
|
||||
- **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))
|
||||
- **shortcuts:** Fix network drive shortcut creation ([#4410](https://github.com/ScoopInstaller/Scoop/issues/4410), [#5006](https://github.com/ScoopInstaller/Scoop/issues/5006))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
# The PowerShell Script Analyzer will generate a warning
|
||||
# diagnostic record for this file due to a bug -
|
||||
# https://github.com/PowerShell/PSScriptAnalyzer/issues/472
|
||||
@{
|
||||
# Only diagnostic records of the specified severity will be generated.
|
||||
# Uncomment the following line if you only want Errors and Warnings but
|
||||
@@ -26,12 +23,6 @@
|
||||
# will be excluded.
|
||||
ExcludeRules = @(
|
||||
# Currently Scoop widely uses Write-Host to output colored text.
|
||||
'PSAvoidUsingWriteHost',
|
||||
# Temporarily allow uses of Invoke-Expression,
|
||||
# this command is used by some core functions and hard to be removed.
|
||||
'PSAvoidUsingInvokeExpression',
|
||||
# PSUseDeclaredVarsMoreThanAssignments doesn't currently work due to:
|
||||
# https://github.com/PowerShell/PSScriptAnalyzer/issues/636
|
||||
'PSUseDeclaredVarsMoreThanAssignments'
|
||||
'PSAvoidUsingWriteHost'
|
||||
)
|
||||
}
|
||||
|
||||
109
README.md
109
README.md
@@ -1,17 +1,17 @@
|
||||
<p align="center">
|
||||
<h1 align="center">Scoop</h1>
|
||||
|
||||
<!--<img src="scoop.png" alt="Long live Scoop!"/>-->
|
||||
<h1 align="center">Scoop</h1>
|
||||
</p>
|
||||
<p align="center">
|
||||
<b><a href="https://github.com/ScoopInstaller/Scoop#what-does-scoop-do">Features</a></b>
|
||||
|
|
||||
<b><a href="https://github.com/ScoopInstaller/Scoop#installation">Installation</a></b>
|
||||
|
|
||||
<b><a href="https://github.com/ScoopInstaller/Scoop/wiki">Documentation</a></b>
|
||||
<a href="https://github.com/ScoopInstaller/Scoop#what-does-scoop-do">Features</a>
|
||||
|
|
||||
<a href="https://github.com/ScoopInstaller/Scoop#installation">Installation</a>
|
||||
|
|
||||
<a href="https://github.com/ScoopInstaller/Scoop/wiki">Documentation</a>
|
||||
</p>
|
||||
|
||||
- - -
|
||||
<p align="center" >
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/ScoopInstaller/Scoop">
|
||||
<img src="https://img.shields.io/github/languages/code-size/ScoopInstaller/Scoop.svg" alt="Code Size" />
|
||||
</a>
|
||||
@@ -36,43 +36,48 @@ Scoop is a command-line installer for Windows.
|
||||
|
||||
## What does Scoop do?
|
||||
|
||||
Scoop installs programs from the command line with a minimal amount of friction. It:
|
||||
Scoop installs apps from the command line with a minimal amount of friction. It:
|
||||
|
||||
- Eliminates permission popup windows
|
||||
- Hides GUI wizard-style installers
|
||||
- Prevents PATH pollution from installing lots of programs
|
||||
- Avoids unexpected side-effects from installing and uninstalling programs
|
||||
- Finds and installs dependencies automatically
|
||||
- Performs all the extra setup steps itself to get a working program
|
||||
- Eliminates [User Account Control](https://learn.microsoft.com/windows/security/application-security/application-control/user-account-control/) (UAC) prompt notifications.
|
||||
- Hides the graphical user interface (GUI) of wizard-style installers.
|
||||
- Prevents polluting the `PATH` environment variable. Normally, this variable gets cluttered as different apps are installed on the device.
|
||||
- Avoids unexpected side effects from installing and uninstalling apps.
|
||||
- Resolves and installs dependencies automatically.
|
||||
- Performs all the necessary steps to get an app to a working state.
|
||||
|
||||
Scoop is very scriptable, so you can run repeatable setups to get your environment just the way you like, e.g.:
|
||||
Scoop is quite script-friendly. Your environment can become the way you like by using repeatable setups. For example:
|
||||
|
||||
```powershell
|
||||
```console
|
||||
scoop install sudo
|
||||
sudo scoop install 7zip git openssh --global
|
||||
scoop install aria2 curl grep sed less touch
|
||||
scoop install python ruby go perl
|
||||
```
|
||||
|
||||
If you've built software that you'd like others to use, Scoop is an alternative to building an installer (e.g. MSI or InnoSetup) — you just need to zip your program and provide a JSON manifest that describes how to install it.
|
||||
If you have built software that you would like others to use, Scoop is an alternative to building an installer (like MSI or InnoSetup). You just need to compress your app to a `.zip` file and provide a JSON manifest that describes how to install it.
|
||||
|
||||
## Installation
|
||||
|
||||
Run the following command from a **non-admin** PowerShell to install scoop to its default location `C:\Users\<YOUR USERNAME>\scoop`.
|
||||
Run the following commands from a regular (non-admin) PowerShell terminal to install Scoop:
|
||||
|
||||
```powershell
|
||||
iwr -useb get.scoop.sh | iex
|
||||
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
|
||||
```
|
||||
|
||||
Advanced installation instruction and full documentation of the installer are available in [ScoopInstaller/Install](https://github.com/ScoopInstaller/Install). Please create new issues there if you have questions about the installation.
|
||||
**Note**: The first command makes your device allow running the installation and management scripts. This is necessary because Windows 10 client devices restrict execution of any PowerShell scripts by default.
|
||||
|
||||
## [Documentation](https://github.com/ScoopInstaller/Scoop/wiki)
|
||||
It will install Scoop to its default location:
|
||||
|
||||
`C:\Users\<YOUR USERNAME>\scoop`
|
||||
|
||||
You can find the complete documentation about the installer, including advanced installation configurations, in [ScoopInstaller/Install](https://github.com/ScoopInstaller/Install). Please create new issues there if you have questions about the installation.
|
||||
|
||||
## Multi-connection downloads with `aria2`
|
||||
|
||||
Scoop can utilize [`aria2`](https://github.com/aria2/aria2) to use multi-connection downloads. Simply install `aria2` through Scoop and it will be used for all downloads afterward.
|
||||
|
||||
```powershell
|
||||
```console
|
||||
scoop install aria2
|
||||
```
|
||||
|
||||
@@ -90,50 +95,54 @@ You can tweak the following `aria2` settings with the `scoop config` command:
|
||||
|
||||
## Inspiration
|
||||
|
||||
- [Homebrew](http://mxcl.github.io/homebrew/)
|
||||
- [sub](https://github.com/37signals/sub#readme)
|
||||
- [Homebrew](https://brew.sh/)
|
||||
- [Sub](https://signalvnoise.com/posts/3264-automating-with-convention-introducing-sub)
|
||||
|
||||
## What sort of apps can Scoop install?
|
||||
|
||||
The apps that install best with Scoop are commonly called "portable" apps: i.e. compressed program files that run stand-alone when extracted and don't have side-effects like changing the registry or putting files outside the program directory.
|
||||
The apps that are most likely to get installed fine with Scoop are those referred to as "portable" apps. These apps are compressed files which can run standalone after being extracted. This type of apps does not produce side effects like changing the Windows Registry or placing files outside the app directory.
|
||||
|
||||
Since installers are common, Scoop supports them too (and their uninstallers).
|
||||
|
||||
Scoop is also great at handling single-file programs and Powershell scripts. These don't even need to be compressed. See the [runat](https://github.com/ScoopInstaller/Main/blob/master/bucket/runat.json) package for an example: it's really just a GitHub gist.
|
||||
Scoop also supports installer files and their uninstallation methods. Likewise, it can handle single-file apps and PowerShell scripts. These do not even need to be compressed. See the [runat](https://github.com/ScoopInstaller/Main/blob/master/bucket/runat.json) package for an example: it is simply a GitHub gist.
|
||||
|
||||
### Contribute to this project
|
||||
|
||||
If you'd like to improve Scoop by adding features or fixing bugs, please read our [Contributing Guide](https://github.com/ScoopInstaller/.github/blob/main/.github/CONTRIBUTING.md).
|
||||
If you would like to improve Scoop by adding features or fixing bugs, please read our [Contributing Guide](https://github.com/ScoopInstaller/.github/blob/main/.github/CONTRIBUTING.md).
|
||||
|
||||
### Support this project
|
||||
|
||||
If you find Scoop useful and would like to support ongoing development and maintenance, here's how:
|
||||
If you find Scoop useful and would like to support the ongoing development and maintenance of this project, you can donate here:
|
||||
|
||||
- [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DM2SUH9EUXSKJ) (one-time donation)
|
||||
- [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DM2SUH9EUXSKJ) (one-time donations)
|
||||
|
||||
## Known application buckets
|
||||
|
||||
The following buckets are known to scoop:
|
||||
The following buckets are known to Scoop:
|
||||
|
||||
- [main](https://github.com/ScoopInstaller/Main) - Default bucket for the most common (mostly CLI) apps
|
||||
- [extras](https://github.com/ScoopInstaller/Extras) - Apps that don't fit the main bucket's [criteria](https://github.com/ScoopInstaller/Scoop/wiki/Criteria-for-including-apps-in-the-main-bucket)
|
||||
- [games](https://github.com/Calinou/scoop-games) - Open source/freeware games and game-related tools
|
||||
- [nerd-fonts](https://github.com/matthewjberger/scoop-nerd-fonts) - Nerd Fonts
|
||||
- [nirsoft](https://github.com/kodybrown/scoop-nirsoft) - Almost all of the [250+](https://rasa.github.io/scoop-directory/by-apps#kodybrown_scoop-nirsoft) apps from [Nirsoft](https://nirsoft.net)
|
||||
- [java](https://github.com/ScoopInstaller/Java) - A collection of Java development kits (JDKs), Java runtime engines (JREs), Java's virtual machine debugging tools and Java based runtime engines.
|
||||
- [nonportable](https://github.com/ScoopInstaller/Nonportable) - Non-portable apps (may require UAC)
|
||||
- [php](https://github.com/ScoopInstaller/PHP) - Installers for most versions of PHP
|
||||
- [versions](https://github.com/ScoopInstaller/Versions) - Alternative versions of apps found in other buckets
|
||||
- [main](https://github.com/ScoopInstaller/Main) - Default bucket which contains popular non-GUI apps.
|
||||
- [extras](https://github.com/ScoopInstaller/Extras) - Apps that do not fit the main bucket's [criteria](https://github.com/ScoopInstaller/Scoop/wiki/Criteria-for-including-apps-in-the-main-bucket).
|
||||
- [games](https://github.com/Calinou/scoop-games) - Open-source and freeware video games and game-related tools.
|
||||
- [nerd-fonts](https://github.com/matthewjberger/scoop-nerd-fonts) - Nerd Fonts.
|
||||
- [nirsoft](https://github.com/ScoopInstaller/Nirsoft) - A collection of over 250+ apps from [Nirsoft](https://nirsoft.net).
|
||||
- [sysinternals](https://github.com/niheaven/scoop-sysinternals) - The Sysinternals suite from [Microsoft](https://learn.microsoft.com/sysinternals/).
|
||||
- [java](https://github.com/ScoopInstaller/Java) - A collection of Java development kits (JDKs) and Java runtime engines (JREs), Java's virtual machine debugging tools and Java based runtime engines.
|
||||
- [nonportable](https://github.com/ScoopInstaller/Nonportable) - Non-portable apps (may trigger UAC prompts).
|
||||
- [php](https://github.com/ScoopInstaller/PHP) - Installers for most versions of PHP.
|
||||
- [versions](https://github.com/ScoopInstaller/Versions) - Alternative versions of apps found in other buckets.
|
||||
|
||||
The main bucket is installed by default. To add any of the other buckets, type:
|
||||
```
|
||||
scoop bucket add bucketname
|
||||
```
|
||||
For example, to add the extras bucket, type:
|
||||
The `main` bucket is installed by default. You can make use of more buckets by typing:
|
||||
|
||||
```console
|
||||
scoop bucket add <name>
|
||||
```
|
||||
|
||||
For example, to add the `extras` bucket, type:
|
||||
|
||||
```console
|
||||
scoop bucket add extras
|
||||
```
|
||||
|
||||
You would be able to install apps from the `extras` bucket now.
|
||||
|
||||
## Other application buckets
|
||||
|
||||
Many other application buckets hosted on Github can be found in the [Scoop Directory](https://rasa.github.io/scoop-directory/) or via [other search engines](https://rasa.github.io/scoop-directory/#other-search-engines).
|
||||
Many other application buckets hosted on GitHub can be found on [ScoopSearch](https://scoop.sh/) or via [other search engines](https://rasa.github.io/scoop-directory/#other-search-engines).
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
.PARAMETER App
|
||||
Manifest name to search.
|
||||
Placeholders are supported.
|
||||
.PARAMETER CommitMessageFormat
|
||||
The format of the commit message.
|
||||
<app> will be replaced with the file name of manifest.
|
||||
<version> will be replaced with the version of the latest manifest.
|
||||
.PARAMETER Dir
|
||||
The directory where to search for manifests.
|
||||
.PARAMETER Push
|
||||
@@ -43,6 +47,7 @@ param(
|
||||
[String] $Upstream,
|
||||
[String] $OriginBranch = 'master',
|
||||
[String] $App = '*',
|
||||
[String] $CommitMessageFormat = '<app>: Update to version <version>',
|
||||
[ValidateScript( {
|
||||
if (!(Test-Path $_ -Type Container)) {
|
||||
throw "$_ is not a directory!"
|
||||
@@ -61,12 +66,11 @@ param(
|
||||
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\json.ps1"
|
||||
. "$PSScriptRoot\..\lib\unix.ps1"
|
||||
|
||||
if ($App -ne '*' -and (Test-Path $App -PathType Leaf)) {
|
||||
$Dir = Split-Path $App
|
||||
} elseif ($Dir) {
|
||||
$Dir = Resolve-Path $Dir
|
||||
$Dir = Convert-Path $Dir
|
||||
} else {
|
||||
throw "'-Dir' parameter required if '-App' is not a filepath!"
|
||||
}
|
||||
@@ -87,7 +91,7 @@ Optional options:
|
||||
exit 0
|
||||
}
|
||||
|
||||
if (is_unix) {
|
||||
if ($IsLinux -or $IsMacOS) {
|
||||
if (!(which hub)) {
|
||||
Write-Host "Please install hub ('brew install hub' or visit: https://hub.github.com/)" -ForegroundColor Yellow
|
||||
exit 1
|
||||
@@ -110,7 +114,7 @@ function execute($cmd) {
|
||||
return $output
|
||||
}
|
||||
|
||||
function pull_requests($json, [String] $app, [String] $upstream, [String] $manifest) {
|
||||
function pull_requests($json, [String] $app, [String] $upstream, [String] $manifest, [String] $commitMessage) {
|
||||
$version = $json.version
|
||||
$homepage = $json.homepage
|
||||
$branch = "manifest/$app-$version"
|
||||
@@ -127,7 +131,7 @@ function pull_requests($json, [String] $app, [String] $upstream, [String] $manif
|
||||
Write-Host "Creating update $app ($version) ..." -ForegroundColor DarkCyan
|
||||
execute "hub checkout -b $branch"
|
||||
execute "hub add $manifest"
|
||||
execute "hub commit -m '${app}: Update to version $version'"
|
||||
execute "hub commit -m '$commitMessage"
|
||||
Write-Host "Pushing update $app ($version) ..." -ForegroundColor DarkCyan
|
||||
execute "hub push origin $branch"
|
||||
|
||||
@@ -142,7 +146,7 @@ function pull_requests($json, [String] $app, [String] $upstream, [String] $manif
|
||||
Write-Host "hub pull-request -m '<msg>' -b '$upstream' -h '$branch'" -ForegroundColor Green
|
||||
|
||||
$msg = @"
|
||||
$app`: Update to version $version
|
||||
$commitMessage
|
||||
|
||||
Hello lovely humans,
|
||||
a new version of [$app]($homepage) is available.
|
||||
@@ -155,7 +159,7 @@ a new version of [$app]($homepage) is available.
|
||||
hub pull-request -m "$msg" -b "$upstream" -h "$branch"
|
||||
if ($LASTEXITCODE -gt 0) {
|
||||
execute 'hub reset'
|
||||
abort "Pull Request failed! (hub pull-request -m '${app}: Update to version $version' -b '$upstream' -h '$branch')"
|
||||
abort "Pull Request failed! (hub pull-request -m '$commitMessage' -b '$upstream' -h '$branch')"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +193,7 @@ hub diff --name-only | ForEach-Object {
|
||||
return
|
||||
}
|
||||
$version = $json.version
|
||||
|
||||
$CommitMessage = $CommitMessageFormat -replace '<app>',$app -replace '<version>',$version
|
||||
if ($Push) {
|
||||
Write-Host "Creating update $app ($version) ..." -ForegroundColor DarkCyan
|
||||
execute "hub add $manifest"
|
||||
@@ -198,12 +202,12 @@ hub diff --name-only | ForEach-Object {
|
||||
$status = execute 'hub status --porcelain -uno'
|
||||
$status = $status | Where-Object { $_ -match "M\s{2}.*$app.json" }
|
||||
if ($status -and $status.StartsWith('M ') -and $status.EndsWith("$app.json")) {
|
||||
execute "hub commit -m '${app}: Update to version $version'"
|
||||
execute "hub commit -m '$commitMessage'"
|
||||
} else {
|
||||
Write-Host "Skipping $app because only LF/CRLF changes were detected ..." -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
pull_requests $json $app $Upstream $manifest
|
||||
pull_requests $json $app $Upstream $manifest $CommitMessage
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
118
bin/checkver.ps1
118
bin/checkver.ps1
@@ -74,14 +74,13 @@ param(
|
||||
. "$PSScriptRoot\..\lib\json.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1" # needed for hash generation
|
||||
. "$PSScriptRoot\..\lib\unix.ps1"
|
||||
|
||||
if ($App -ne '*' -and (Test-Path $App -PathType Leaf)) {
|
||||
$Dir = Split-Path $App
|
||||
$files = Get-ChildItem $Dir (Split-Path $App -Leaf)
|
||||
$files = Get-ChildItem $Dir -Filter (Split-Path $App -Leaf)
|
||||
} elseif ($Dir) {
|
||||
$Dir = Resolve-Path $Dir
|
||||
$files = Get-ChildItem $Dir "$App.json"
|
||||
$Dir = Convert-Path $Dir
|
||||
$files = Get-ChildItem $Dir -Filter "$App.json" -Recurse
|
||||
} else {
|
||||
throw "'-Dir' parameter required if '-App' is not a filepath!"
|
||||
}
|
||||
@@ -97,9 +96,10 @@ if ($App -eq '*' -and $Version -ne '') {
|
||||
$Queue = @()
|
||||
$json = ''
|
||||
$files | ForEach-Object {
|
||||
$json = parse_json "$Dir\$($_.Name)"
|
||||
$file = $_.FullName
|
||||
$json = parse_json $file
|
||||
if ($json.checkver) {
|
||||
$Queue += , @($_.Name, $json)
|
||||
$Queue += , @($_.BaseName, $json, $file)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ Get-EventSubscriber | Unregister-Event
|
||||
|
||||
# start all downloads
|
||||
$Queue | ForEach-Object {
|
||||
$name, $json = $_
|
||||
$name, $json, $file = $_
|
||||
|
||||
$substitutions = Get-VersionSubstitution $json.version # 'autoupdate.ps1'
|
||||
|
||||
@@ -121,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"
|
||||
@@ -148,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) {
|
||||
@@ -165,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
|
||||
}
|
||||
|
||||
@@ -185,14 +226,21 @@ $Queue | ForEach-Object {
|
||||
$url = substitute $url $substitutions
|
||||
|
||||
$state = New-Object psobject @{
|
||||
app = (strip_ext $name);
|
||||
url = $url;
|
||||
regex = $regex;
|
||||
json = $json;
|
||||
jsonpath = $jsonpath;
|
||||
xpath = $xpath;
|
||||
reverse = $reverse;
|
||||
replace = $replace;
|
||||
app = $name
|
||||
file = $file
|
||||
url = $url
|
||||
regex = $regex
|
||||
json = $json
|
||||
jsonpath = $jsonpath
|
||||
xpath = $xpath
|
||||
reverse = $reverse
|
||||
replace = $replace
|
||||
}
|
||||
|
||||
get_config PRIVATE_HOSTS | Where-Object { $_ -ne $null -and $url -match $_.match } | ForEach-Object {
|
||||
(ConvertFrom-StringData -StringData $_.Headers).GetEnumerator() | ForEach-Object {
|
||||
$wc.Headers[$_.Key] = $_.Value
|
||||
}
|
||||
}
|
||||
|
||||
$wc.Headers.Add('Referer', (strip_filename $url))
|
||||
@@ -213,11 +261,13 @@ 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
|
||||
@@ -234,11 +284,12 @@ while ($in_progress -gt 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ($json.checkver.script) {
|
||||
$page = Invoke-Command ([scriptblock]::Create($json.checkver.script -join "`r`n"))
|
||||
} else {
|
||||
if ($url) {
|
||||
$page = (Get-Encoding($wc)).GetString($ev.SourceEventArgs.Result)
|
||||
}
|
||||
if ($script) {
|
||||
$page = Invoke-Command ([scriptblock]::Create($script -join "`r`n"))
|
||||
}
|
||||
|
||||
if ($jsonpath) {
|
||||
# Return only a single value if regex is absent
|
||||
@@ -264,12 +315,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
|
||||
}
|
||||
}
|
||||
@@ -317,7 +373,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) {
|
||||
@@ -343,7 +399,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 $_
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ switch ($subCommand) {
|
||||
}
|
||||
({ $subCommand -in @('-v', '--version') }) {
|
||||
Write-Host 'Current Scoop version:'
|
||||
if ((Test-CommandAvailable git) -and (Test-Path "$PSScriptRoot\..\.git") -and (get_config SCOOP_BRANCH 'master') -ne 'master') {
|
||||
git -C "$PSScriptRoot\.." --no-pager log --oneline HEAD -n 1
|
||||
if (Test-GitAvailable -and (Test-Path "$PSScriptRoot\..\.git") -and (get_config SCOOP_BRANCH 'master') -ne 'master') {
|
||||
Invoke-Git -Path "$PSScriptRoot\.." -ArgumentList @('log', 'HEAD', '-1', '--oneline')
|
||||
} else {
|
||||
$version = Select-String -Pattern '^## \[(v[\d.]+)\].*?([\d-]+)$' -Path "$PSScriptRoot\..\CHANGELOG.md"
|
||||
Write-Host $version.Matches.Groups[1].Value -ForegroundColor Cyan -NoNewline
|
||||
@@ -31,9 +31,9 @@ switch ($subCommand) {
|
||||
|
||||
Get-LocalBucket | ForEach-Object {
|
||||
$bucketLoc = Find-BucketDirectory $_ -Root
|
||||
if ((Test-Path "$bucketLoc\.git") -and (Test-CommandAvailable git)) {
|
||||
if (Test-GitAvailable -and (Test-Path "$bucketLoc\.git")) {
|
||||
Write-Host "'$_' bucket:"
|
||||
git -C "$bucketLoc" --no-pager log --oneline HEAD -n 1
|
||||
Invoke-Git -Path $bucketLoc -ArgumentList @('log', 'HEAD', '-1', '--oneline')
|
||||
Write-Host ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ param(
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
|
||||
if ($global -and !(is_admin)) {
|
||||
if ($global -and !(Test-IsAdmin)) {
|
||||
error 'You need admin rights to uninstall globally.'
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"main": "https://github.com/ScoopInstaller/Main",
|
||||
"extras": "https://github.com/ScoopInstaller/Extras",
|
||||
"versions": "https://github.com/ScoopInstaller/Versions",
|
||||
"nirsoft": "https://github.com/kodybrown/scoop-nirsoft",
|
||||
"nirsoft": "https://github.com/ScoopInstaller/Nirsoft",
|
||||
"sysinternals": "https://github.com/niheaven/scoop-sysinternals",
|
||||
"php": "https://github.com/ScoopInstaller/PHP",
|
||||
"nerd-fonts": "https://github.com/matthewjberger/scoop-nerd-fonts",
|
||||
"nonportable": "https://github.com/ScoopInstaller/Nonportable",
|
||||
|
||||
@@ -8,9 +8,9 @@ function find_hash_in_rdf([String] $url, [String] $basename) {
|
||||
$wc.Headers.Add('User-Agent', (Get-UserAgent))
|
||||
$data = $wc.DownloadData($url)
|
||||
[xml]$xml = (Get-Encoding($wc)).GetString($data)
|
||||
} catch [system.net.webexception] {
|
||||
write-host -f darkred $_
|
||||
write-host -f darkred "URL $url is not valid"
|
||||
} catch [System.Net.WebException] {
|
||||
Write-Host $_ -ForegroundColor DarkRed
|
||||
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
|
||||
return $null
|
||||
}
|
||||
|
||||
@@ -24,12 +24,12 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
|
||||
$hashfile = $null
|
||||
|
||||
$templates = @{
|
||||
'$md5' = '([a-fA-F0-9]{32})';
|
||||
'$sha1' = '([a-fA-F0-9]{40})';
|
||||
'$sha256' = '([a-fA-F0-9]{64})';
|
||||
'$sha512' = '([a-fA-F0-9]{128})';
|
||||
'$checksum' = '([a-fA-F0-9]{32,128})';
|
||||
'$base64' = '([a-zA-Z0-9+\/=]{24,88})';
|
||||
'$md5' = '([a-fA-F0-9]{32})'
|
||||
'$sha1' = '([a-fA-F0-9]{40})'
|
||||
'$sha256' = '([a-fA-F0-9]{64})'
|
||||
'$sha512' = '([a-fA-F0-9]{128})'
|
||||
'$checksum' = '([a-fA-F0-9]{32,128})'
|
||||
'$base64' = '([a-zA-Z0-9+\/=]{24,88})'
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -39,8 +39,8 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
|
||||
$data = $wc.DownloadData($url)
|
||||
$hashfile = (Get-Encoding($wc)).GetString($data)
|
||||
} catch [system.net.webexception] {
|
||||
write-host -f darkred $_
|
||||
write-host -f darkred "URL $url is not valid"
|
||||
Write-Host $_ -ForegroundColor DarkRed
|
||||
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
|
||||
return
|
||||
}
|
||||
|
||||
@@ -50,15 +50,15 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
|
||||
|
||||
$regex = substitute $regex $templates $false
|
||||
$regex = substitute $regex $substitutions $true
|
||||
debug $regex
|
||||
if ($hashfile -match $regex) {
|
||||
$hash = $matches[1] -replace '\s',''
|
||||
debug $regex
|
||||
$hash = $matches[1] -replace '\s', ''
|
||||
}
|
||||
|
||||
# convert base64 encoded hash values
|
||||
if ($hash -match '^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$') {
|
||||
$base64 = $matches[0]
|
||||
if(!($hash -match '^[a-fA-F0-9]+$') -and $hash.length -notin @(32, 40, 64, 128)) {
|
||||
if (!($hash -match '^[a-fA-F0-9]+$') -and $hash.Length -notin @(32, 40, 64, 128)) {
|
||||
try {
|
||||
$hash = ([System.Convert]::FromBase64String($base64) | ForEach-Object { $_.ToString('x2') }) -join ''
|
||||
} catch {
|
||||
@@ -69,13 +69,15 @@ function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [Strin
|
||||
|
||||
# find hash with filename in $hashfile
|
||||
if ($hash.Length -eq 0) {
|
||||
$filenameRegex = "([a-fA-F0-9]{32,128})[\x20\t]+.*`$basename(?:[\x20\t]+\d+)?"
|
||||
$filenameRegex = "([a-fA-F0-9]{32,128})[\x20\t]+.*`$basename(?:\s|$)|`$basename[\x20\t]+.*?([a-fA-F0-9]{32,128})"
|
||||
$filenameRegex = substitute $filenameRegex $substitutions $true
|
||||
if ($hashfile -match $filenameRegex) {
|
||||
debug $filenameRegex
|
||||
$hash = $matches[1]
|
||||
}
|
||||
$metalinkRegex = "<hash[^>]+>([a-fA-F0-9]{64})"
|
||||
$metalinkRegex = '<hash[^>]+>([a-fA-F0-9]{64})'
|
||||
if ($hashfile -match $metalinkRegex) {
|
||||
debug $metalinkRegex
|
||||
$hash = $matches[1]
|
||||
}
|
||||
}
|
||||
@@ -92,13 +94,14 @@ function find_hash_in_json([String] $url, [Hashtable] $substitutions, [String] $
|
||||
$wc.Headers.Add('User-Agent', (Get-UserAgent))
|
||||
$data = $wc.DownloadData($url)
|
||||
$json = (Get-Encoding($wc)).GetString($data)
|
||||
} catch [system.net.webexception] {
|
||||
write-host -f darkred $_
|
||||
write-host -f darkred "URL $url is not valid"
|
||||
} catch [System.Net.WebException] {
|
||||
Write-Host $_ -ForegroundColor DarkRed
|
||||
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
|
||||
return
|
||||
}
|
||||
debug $jsonpath
|
||||
$hash = json_path $json $jsonpath $substitutions
|
||||
if(!$hash) {
|
||||
if (!$hash) {
|
||||
$hash = json_path_legacy $json $jsonpath $substitutions
|
||||
}
|
||||
return format_hash $hash
|
||||
@@ -114,8 +117,8 @@ function find_hash_in_xml([String] $url, [Hashtable] $substitutions, [String] $x
|
||||
$data = $wc.DownloadData($url)
|
||||
$xml = [xml]((Get-Encoding($wc)).GetString($data))
|
||||
} catch [system.net.webexception] {
|
||||
write-host -f darkred $_
|
||||
write-host -f darkred "URL $url is not valid"
|
||||
Write-Host $_ -ForegroundColor DarkRed
|
||||
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
|
||||
return
|
||||
}
|
||||
|
||||
@@ -125,13 +128,15 @@ function find_hash_in_xml([String] $url, [Hashtable] $substitutions, [String] $x
|
||||
}
|
||||
|
||||
# Find all `significant namespace declarations` from the XML file
|
||||
$nsList = $xml.SelectNodes("//namespace::*[not(. = ../../namespace::*)]")
|
||||
$nsList = $xml.SelectNodes('//namespace::*[not(. = ../../namespace::*)]')
|
||||
# Then add them into the NamespaceManager
|
||||
$nsmgr = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
|
||||
$nsList | ForEach-Object {
|
||||
$nsmgr.AddNamespace($_.LocalName, $_.Value)
|
||||
}
|
||||
|
||||
debug $xpath
|
||||
debug $nsmgr
|
||||
# Getting hash from XML, using XPath
|
||||
$hash = $xml.SelectSingleNode($xpath, $nsmgr).'#text'
|
||||
return format_hash $hash
|
||||
@@ -148,16 +153,16 @@ function find_hash_in_headers([String] $url) {
|
||||
$req.Timeout = 2000
|
||||
$req.Method = 'HEAD'
|
||||
$res = $req.GetResponse()
|
||||
if(([int]$res.StatusCode -ge 300) -and ([int]$res.StatusCode -lt 400)) {
|
||||
if($res.Headers['Digest'] -match 'SHA-256=([^,]+)' -or $res.Headers['Digest'] -match 'SHA=([^,]+)' -or $res.Headers['Digest'] -match 'MD5=([^,]+)') {
|
||||
if (([int]$res.StatusCode -ge 300) -and ([int]$res.StatusCode -lt 400)) {
|
||||
if ($res.Headers['Digest'] -match 'SHA-256=([^,]+)' -or $res.Headers['Digest'] -match 'SHA=([^,]+)' -or $res.Headers['Digest'] -match 'MD5=([^,]+)') {
|
||||
$hash = ([System.Convert]::FromBase64String($matches[1]) | ForEach-Object { $_.ToString('x2') }) -join ''
|
||||
debug $hash
|
||||
}
|
||||
}
|
||||
$res.Close()
|
||||
} catch [system.net.webexception] {
|
||||
write-host -f darkred $_
|
||||
write-host -f darkred "URL $url is not valid"
|
||||
} catch [System.Net.WebException] {
|
||||
Write-Host $_ -ForegroundColor DarkRed
|
||||
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
|
||||
return
|
||||
}
|
||||
|
||||
@@ -182,10 +187,10 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
|
||||
$hashfile_url = substitute $config.url $substitutions
|
||||
debug $hashfile_url
|
||||
if ($hashfile_url) {
|
||||
write-host -f DarkYellow 'Searching hash for ' -NoNewline
|
||||
write-host -f Green $basename -NoNewline
|
||||
write-host -f DarkYellow ' in ' -NoNewline
|
||||
write-host -f Green $hashfile_url
|
||||
Write-Host 'Searching hash for ' -ForegroundColor DarkYellow -NoNewline
|
||||
Write-Host $basename -ForegroundColor Green -NoNewline
|
||||
Write-Host ' in ' -ForegroundColor DarkYellow -NoNewline
|
||||
Write-Host $hashfile_url -ForegroundColor Green
|
||||
}
|
||||
|
||||
if ($hashmode.Length -eq 0 -and $config.url.Length -ne 0) {
|
||||
@@ -215,11 +220,11 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
|
||||
$hashmode = 'xpath'
|
||||
}
|
||||
|
||||
if (!$hashfile_url -and $url -match "^(?:.*fosshub.com\/).*(?:\/|\?dwl=)(?<filename>.*)$") {
|
||||
if (!$hashfile_url -and $url -match '^(?:.*fosshub.com\/).*(?:\/|\?dwl=)(?<filename>.*)$') {
|
||||
$hashmode = 'fosshub'
|
||||
}
|
||||
|
||||
if (!$hashfile_url -and $url -match "(?:downloads\.)?sourceforge.net\/projects?\/(?<project>[^\/]+)\/(?:files\/)?(?<file>.*)") {
|
||||
if (!$hashfile_url -and $url -match '(?:downloads\.)?sourceforge.net\/projects?\/(?<project>[^\/]+)\/(?:files\/)?(?<file>.*)') {
|
||||
$hashmode = 'sourceforge'
|
||||
}
|
||||
|
||||
@@ -243,40 +248,40 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
|
||||
}
|
||||
}
|
||||
'fosshub' {
|
||||
$hash = find_hash_in_textfile $url $substitutions ($Matches.filename+'.*?"sha256":"([a-fA-F0-9]{64})"')
|
||||
$hash = find_hash_in_textfile $url $substitutions ($matches.filename + '.*?"sha256":"([a-fA-F0-9]{64})"')
|
||||
}
|
||||
'sourceforge' {
|
||||
# change the URL because downloads.sourceforge.net doesn't have checksums
|
||||
$hashfile_url = (strip_filename (strip_fragment "https://sourceforge.net/projects/$($matches['project'])/files/$($matches['file'])")).TrimEnd('/')
|
||||
$hash = find_hash_in_textfile $hashfile_url $substitutions '"$basename":.*?"sha1":\s"([a-fA-F0-9]{40})"'
|
||||
$hash = find_hash_in_textfile $hashfile_url $substitutions '"$basename":.*?"sha1":\s*"([a-fA-F0-9]{40})"'
|
||||
}
|
||||
}
|
||||
|
||||
if ($hash) {
|
||||
# got one!
|
||||
write-host -f DarkYellow 'Found: ' -NoNewline
|
||||
write-host -f Green $hash -NoNewline
|
||||
write-host -f DarkYellow ' using ' -NoNewline
|
||||
write-host -f Green "$((Get-Culture).TextInfo.ToTitleCase($hashmode)) Mode"
|
||||
Write-Host 'Found: ' -ForegroundColor DarkYellow -NoNewline
|
||||
Write-Host $hash -ForegroundColor Green -NoNewline
|
||||
Write-Host ' using ' -ForegroundColor DarkYellow -NoNewline
|
||||
Write-Host "$((Get-Culture).TextInfo.ToTitleCase($hashmode)) Mode" -ForegroundColor Green
|
||||
return $hash
|
||||
} elseif ($hashfile_url) {
|
||||
write-host -f DarkYellow "Could not find hash in $hashfile_url"
|
||||
Write-Host -f DarkYellow "Could not find hash in $hashfile_url"
|
||||
}
|
||||
|
||||
write-host -f DarkYellow 'Downloading ' -NoNewline
|
||||
write-host -f Green $basename -NoNewline
|
||||
write-host -f DarkYellow ' to compute hashes!'
|
||||
Write-Host 'Downloading ' -ForegroundColor DarkYellow -NoNewline
|
||||
Write-Host $basename -ForegroundColor Green -NoNewline
|
||||
Write-Host ' to compute hashes!' -ForegroundColor DarkYellow
|
||||
try {
|
||||
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"
|
||||
Write-Host $_ -ForegroundColor DarkRed
|
||||
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
|
||||
return $null
|
||||
}
|
||||
$file = fullpath (cache_path $app $version $url)
|
||||
$hash = compute_hash $file 'sha256'
|
||||
write-host -f DarkYellow 'Computed hash: ' -NoNewline
|
||||
write-host -f Green $hash
|
||||
$hash = (Get-FileHash -Path $file -Algorithm SHA256).Hash.ToLower()
|
||||
Write-Host 'Computed hash: ' -ForegroundColor DarkYellow -NoNewline
|
||||
Write-Host $hash -ForegroundColor Green
|
||||
return $hash
|
||||
}
|
||||
|
||||
@@ -346,7 +351,7 @@ function Update-ManifestProperty {
|
||||
$newValue = substitute $autoupdateProperty $Substitutions
|
||||
if (($autoupdateProperty.GetType().Name -eq 'Object[]') -and ($autoupdateProperty.Length -eq 1)) {
|
||||
# Make sure it's an array
|
||||
$newValue = ,$newValue
|
||||
$newValue = , $newValue
|
||||
}
|
||||
$Manifest.$currentProperty, $hasPropertyChanged = PropertyHelper -Property $Manifest.$currentProperty -Value $newValue
|
||||
$hasManifestChanged = $hasManifestChanged -or $hasPropertyChanged
|
||||
@@ -359,7 +364,7 @@ function Update-ManifestProperty {
|
||||
$newValue = substitute $autoupdateProperty $Substitutions
|
||||
if (($autoupdateProperty.GetType().Name -eq 'Object[]') -and ($autoupdateProperty.Length -eq 1)) {
|
||||
# Make sure it's an array
|
||||
$newValue = ,$newValue
|
||||
$newValue = , $newValue
|
||||
}
|
||||
$Manifest.architecture.$arch.$currentProperty, $hasPropertyChanged = PropertyHelper -Property $Manifest.architecture.$arch.$currentProperty -Value $newValue
|
||||
$hasManifestChanged = $hasManifestChanged -or $hasPropertyChanged
|
||||
@@ -388,25 +393,25 @@ function Get-VersionSubstitution {
|
||||
$firstPart = $Version.Split('-') | Select-Object -First 1
|
||||
$lastPart = $Version.Split('-') | Select-Object -Last 1
|
||||
$versionVariables = @{
|
||||
'$version' = $Version;
|
||||
'$dotVersion' = ($Version -replace '[._-]', '.');
|
||||
'$underscoreVersion' = ($Version -replace '[._-]', '_');
|
||||
'$dashVersion' = ($Version -replace '[._-]', '-');
|
||||
'$cleanVersion' = ($Version -replace '[._-]', '');
|
||||
'$majorVersion' = $firstPart.Split('.') | Select-Object -First 1;
|
||||
'$minorVersion' = $firstPart.Split('.') | Select-Object -Skip 1 -First 1;
|
||||
'$patchVersion' = $firstPart.Split('.') | Select-Object -Skip 2 -First 1;
|
||||
'$buildVersion' = $firstPart.Split('.') | Select-Object -Skip 3 -First 1;
|
||||
'$preReleaseVersion' = $lastPart;
|
||||
'$version' = $Version
|
||||
'$dotVersion' = ($Version -replace '[._-]', '.')
|
||||
'$underscoreVersion' = ($Version -replace '[._-]', '_')
|
||||
'$dashVersion' = ($Version -replace '[._-]', '-')
|
||||
'$cleanVersion' = ($Version -replace '[._-]', '')
|
||||
'$majorVersion' = $firstPart.Split('.') | Select-Object -First 1
|
||||
'$minorVersion' = $firstPart.Split('.') | Select-Object -Skip 1 -First 1
|
||||
'$patchVersion' = $firstPart.Split('.') | Select-Object -Skip 2 -First 1
|
||||
'$buildVersion' = $firstPart.Split('.') | Select-Object -Skip 3 -First 1
|
||||
'$preReleaseVersion' = $lastPart
|
||||
}
|
||||
if($Version -match "(?<head>\d+\.\d+(?:\.\d+)?)(?<tail>.*)") {
|
||||
$versionVariables.Set_Item('$matchHead', $Matches['head'])
|
||||
$versionVariables.Set_Item('$matchTail', $Matches['tail'])
|
||||
if ($Version -match '(?<head>\d+\.\d+(?:\.\d+)?)(?<tail>.*)') {
|
||||
$versionVariables.Add('$matchHead', $Matches['head'])
|
||||
$versionVariables.Add('$matchTail', $Matches['tail'])
|
||||
}
|
||||
if($CustomMatches) {
|
||||
if ($CustomMatches) {
|
||||
$CustomMatches.GetEnumerator() | ForEach-Object {
|
||||
if($_.Name -ne "0") {
|
||||
$versionVariables.Set_Item('$match' + (Get-Culture).TextInfo.ToTitleCase($_.Name), $_.Value)
|
||||
if ($_.Name -ne '0') {
|
||||
$versionVariables.Add('$match' + (Get-Culture).TextInfo.ToTitleCase($_.Name), $_.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,7 +454,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 +462,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
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
$bucketsdir = "$scoopdir\buckets"
|
||||
if (!(Test-Path $bucketsdir)) {
|
||||
$bucketsdir = "$globaldir\buckets"
|
||||
}
|
||||
|
||||
function Find-BucketDirectory {
|
||||
<#
|
||||
@@ -53,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 {
|
||||
@@ -61,10 +58,18 @@ function Get-LocalBucket {
|
||||
.SYNOPSIS
|
||||
List all local buckets.
|
||||
#>
|
||||
$bucketNames = (Get-ChildItem -Path $bucketsdir -Directory).Name
|
||||
$bucketNames = [System.Collections.Generic.List[String]](Get-ChildItem -Path $bucketsdir -Directory).Name
|
||||
if ($null -eq $bucketNames) {
|
||||
return @() # Return a zero-length list instead of $null.
|
||||
} else {
|
||||
$knownBuckets = known_buckets
|
||||
for ($i = $knownBuckets.Count - 1; $i -ge 0 ; $i--) {
|
||||
$name = $knownBuckets[$i]
|
||||
if ($bucketNames.Contains($name)) {
|
||||
[void]$bucketNames.Remove($name)
|
||||
$bucketNames.Insert(0, $name)
|
||||
}
|
||||
}
|
||||
return $bucketNames
|
||||
}
|
||||
}
|
||||
@@ -102,11 +107,11 @@ function list_buckets {
|
||||
$bucket = [Ordered]@{ Name = $_ }
|
||||
$path = Find-BucketDirectory $_ -Root
|
||||
if ((Test-Path (Join-Path $path '.git')) -and (Get-Command git -ErrorAction SilentlyContinue)) {
|
||||
$bucket.Source = git -C $path config remote.origin.url
|
||||
$bucket.Updated = git -C $path log --format='%aD' -n 1 | Get-Date
|
||||
$bucket.Source = Invoke-Git -Path $path -ArgumentList @('config', 'remote.origin.url')
|
||||
$bucket.Updated = Invoke-Git -Path $path -ArgumentList @('log', '--format=%aD', '-n', '1') | Get-Date
|
||||
} else {
|
||||
$bucket.Source = friendly_path $path
|
||||
$bucket.Updated = (Get-Item "$path\bucket").LastWriteTime
|
||||
$bucket.Updated = (Get-Item "$path\bucket" -ErrorAction SilentlyContinue).LastWriteTime
|
||||
}
|
||||
$bucket.Manifests = Get-ChildItem "$path\bucket" -Force -Recurse -ErrorAction SilentlyContinue |
|
||||
Measure-Object | Select-Object -ExpandProperty Count
|
||||
@@ -116,14 +121,14 @@ function list_buckets {
|
||||
}
|
||||
|
||||
function add_bucket($name, $repo) {
|
||||
if (!(Test-CommandAvailable git)) {
|
||||
if (!(Test-GitAvailable)) {
|
||||
error "Git is required for buckets. Run 'scoop install git' and try again."
|
||||
return 1
|
||||
}
|
||||
|
||||
$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
|
||||
}
|
||||
|
||||
@@ -133,7 +138,7 @@ function add_bucket($name, $repo) {
|
||||
}
|
||||
foreach ($bucket in Get-LocalBucket) {
|
||||
if (Test-Path -Path "$bucketsdir\$bucket\.git") {
|
||||
$remote = git -C "$bucketsdir\$bucket" config --get remote.origin.url
|
||||
$remote = Invoke-Git -Path "$bucketsdir\$bucket" -ArgumentList @('config', '--get', 'remote.origin.url')
|
||||
if ((Convert-RepositoryUri -Uri $remote) -eq $uni_repo) {
|
||||
warn "Bucket $bucket already exists for $repo"
|
||||
return 2
|
||||
@@ -142,14 +147,14 @@ function add_bucket($name, $repo) {
|
||||
}
|
||||
|
||||
Write-Host 'Checking repo... ' -NoNewline
|
||||
$out = git_cmd ls-remote $repo 2>&1
|
||||
$out = Invoke-Git -ArgumentList @('ls-remote', $repo) 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
error "'$repo' doesn't look like a valid git repository`n`nError given:`n$out"
|
||||
return 1
|
||||
}
|
||||
ensure $bucketsdir | Out-Null
|
||||
$dir = ensure $dir
|
||||
git_cmd clone "$repo" "`"$dir`"" -q
|
||||
Invoke-Git -ArgumentList @('clone', $repo, $dir, '-q')
|
||||
Write-Host 'OK'
|
||||
success "The $name bucket was added successfully."
|
||||
return 0
|
||||
@@ -172,7 +177,7 @@ function new_issue_msg($app, $bucket, $title, $body) {
|
||||
$bucket_path = "$bucketsdir\$bucket"
|
||||
|
||||
if (Test-Path $bucket_path) {
|
||||
$remote = git -C "$bucket_path" config --get remote.origin.url
|
||||
$remote = Invoke-Git -Path $bucket_path -ArgumentList @('config', '--get', 'remote.origin.url')
|
||||
# Support ssh and http syntax
|
||||
# git@PROVIDER:USER/REPO.git
|
||||
# https://PROVIDER/USER/REPO.git
|
||||
|
||||
452
lib/core.ps1
452
lib/core.ps1
@@ -1,3 +1,56 @@
|
||||
# Returns the subsystem of the EXE
|
||||
function Get-Subsystem($filePath) {
|
||||
try {
|
||||
$fileStream = [System.IO.FileStream]::new($filePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
|
||||
$binaryReader = [System.IO.BinaryReader]::new($fileStream)
|
||||
} catch {
|
||||
return -1 # leave the subsystem part silently
|
||||
}
|
||||
|
||||
try {
|
||||
$fileStream.Seek(0x3C, [System.IO.SeekOrigin]::Begin) | Out-Null
|
||||
$peOffset = $binaryReader.ReadInt32()
|
||||
|
||||
$fileStream.Seek($peOffset, [System.IO.SeekOrigin]::Begin) | Out-Null
|
||||
$fileHeaderOffset = $fileStream.Position
|
||||
|
||||
$fileStream.Seek(18, [System.IO.SeekOrigin]::Current) | Out-Null
|
||||
$fileStream.Seek($fileHeaderOffset + 0x5C, [System.IO.SeekOrigin]::Begin) | Out-Null
|
||||
|
||||
return $binaryReader.ReadInt16()
|
||||
} finally {
|
||||
$binaryReader.Close()
|
||||
$fileStream.Close()
|
||||
}
|
||||
}
|
||||
|
||||
function Change-Subsystem($filePath, $targetSubsystem) {
|
||||
try {
|
||||
$fileStream = [System.IO.FileStream]::new($filePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite)
|
||||
$binaryReader = [System.IO.BinaryReader]::new($fileStream)
|
||||
$binaryWriter = [System.IO.BinaryWriter]::new($fileStream)
|
||||
} catch {
|
||||
Write-Output "Error opening File:'$filePath'"
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
$fileStream.Seek(0x3C, [System.IO.SeekOrigin]::Begin) | Out-Null
|
||||
$peOffset = $binaryReader.ReadInt32()
|
||||
|
||||
$fileStream.Seek($peOffset, [System.IO.SeekOrigin]::Begin) | Out-Null
|
||||
$fileHeaderOffset = $fileStream.Position
|
||||
|
||||
$fileStream.Seek(18, [System.IO.SeekOrigin]::Current) | Out-Null
|
||||
$fileStream.Seek($fileHeaderOffset + 0x5C, [System.IO.SeekOrigin]::Begin) | Out-Null
|
||||
|
||||
$binaryWriter.Write([System.Int16] $targetSubsystem)
|
||||
} finally {
|
||||
$binaryReader.Close()
|
||||
$fileStream.Close()
|
||||
}
|
||||
}
|
||||
|
||||
function Optimize-SecurityProtocol {
|
||||
# .NET Framework 4.7+ has a default security protocol called 'SystemDefault',
|
||||
# which allows the operating system to choose the best protocol to use.
|
||||
@@ -24,7 +77,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 +112,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 +126,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 +154,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
|
||||
}
|
||||
@@ -125,13 +181,68 @@ function setup_proxy() {
|
||||
}
|
||||
}
|
||||
|
||||
function git_cmd {
|
||||
$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"
|
||||
function Invoke-Git {
|
||||
[CmdletBinding()]
|
||||
[OutputType([String])]
|
||||
param(
|
||||
[Parameter(Mandatory = $false, Position = 0)]
|
||||
[Alias('PSPath', 'Path')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[String]
|
||||
$WorkingDirectory,
|
||||
[Parameter(Mandatory = $true, Position = 1)]
|
||||
[Alias('Args')]
|
||||
[String[]]
|
||||
$ArgumentList
|
||||
)
|
||||
|
||||
$proxy = get_config PROXY
|
||||
$git = Get-HelperPath -Helper Git
|
||||
|
||||
if ($WorkingDirectory) {
|
||||
$ArgumentList = @('-C', $WorkingDirectory) + $ArgumentList
|
||||
}
|
||||
|
||||
if([String]::IsNullOrEmpty($proxy) -or $proxy -eq 'none') {
|
||||
return & $git @ArgumentList
|
||||
}
|
||||
|
||||
if($ArgumentList -Match '\b(clone|checkout|pull|fetch|ls-remote)\b') {
|
||||
$j = Start-Job -ScriptBlock {
|
||||
# convert proxy setting for git
|
||||
$proxy = $using:proxy
|
||||
if ($proxy -and $proxy.StartsWith('currentuser@')) {
|
||||
$proxy = $proxy.Replace('currentuser@', ':@')
|
||||
}
|
||||
$env:HTTPS_PROXY = $proxy
|
||||
$env:HTTP_PROXY = $proxy
|
||||
& $using:git @using:ArgumentList
|
||||
}
|
||||
$o = $j | Receive-Job -Wait -AutoRemoveJob
|
||||
return $o
|
||||
}
|
||||
|
||||
return & $git @ArgumentList
|
||||
}
|
||||
|
||||
function Invoke-GitLog {
|
||||
[CmdletBinding()]
|
||||
Param (
|
||||
[Parameter(Mandatory, ValueFromPipeline)]
|
||||
[String]$Path,
|
||||
[Parameter(Mandatory, ValueFromPipeline)]
|
||||
[String]$CommitHash,
|
||||
[String]$Name = ''
|
||||
)
|
||||
Process {
|
||||
if ($Name) {
|
||||
if ($Name.Length -gt 12) {
|
||||
$Name = "$($Name.Substring(0, 10)).."
|
||||
}
|
||||
$Name = "%Cgreen$($Name.PadRight(12, ' ').Substring(0, 12))%Creset "
|
||||
}
|
||||
Invoke-Git -Path $Path -ArgumentList @('--no-pager', 'log', '--color', '--no-decorate', "--grep='^(chore)'", '--invert-grep', '--abbrev=12', "--format=tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s $Name%C(cyan)%cr%Creset", "$CommitHash..HEAD")
|
||||
}
|
||||
cmd.exe /d /c $cmd
|
||||
}
|
||||
|
||||
# helper functions
|
||||
@@ -142,18 +253,23 @@ function format($str, $hash) {
|
||||
$executionContext.invokeCommand.expandString($str)
|
||||
}
|
||||
function is_admin {
|
||||
Show-DeprecatedWarning $MyInvocation 'Test-IsAdmin'
|
||||
$admin = [security.principal.windowsbuiltinrole]::administrator
|
||||
$id = [security.principal.windowsidentity]::getcurrent()
|
||||
([security.principal.windowsprincipal]($id)).isinrole($admin)
|
||||
}
|
||||
|
||||
function Test-IsAdmin {
|
||||
return ([System.Security.Principal.WindowsPrincipal] [System.Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
}
|
||||
|
||||
# messages
|
||||
function abort($msg, [int] $exit_code=1) { write-host $msg -f red; exit $exit_code }
|
||||
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
|
||||
}
|
||||
|
||||
@@ -206,11 +322,12 @@ function filesize($length) {
|
||||
function basedir($global) { if($global) { return $globaldir } $scoopdir }
|
||||
function appsdir($global) { "$(basedir $global)\apps" }
|
||||
function shimdir($global) { "$(basedir $global)\shims" }
|
||||
function modulesdir($global) { "$(basedir $global)\modules" }
|
||||
function appdir($app, $global) { "$(appsdir $global)\$app" }
|
||||
function versiondir($app, $version, $global) { "$(appdir $app $global)\$version" }
|
||||
|
||||
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 +342,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 +363,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))
|
||||
}
|
||||
|
||||
@@ -290,12 +407,16 @@ Function Test-CommandAvailable {
|
||||
Return [Boolean](Get-Command $Name -ErrorAction Ignore)
|
||||
}
|
||||
|
||||
Function Test-GitAvailable {
|
||||
return [Boolean](Get-HelperPath -Helper Git)
|
||||
}
|
||||
|
||||
function Get-HelperPath {
|
||||
[CmdletBinding()]
|
||||
[OutputType([String])]
|
||||
param(
|
||||
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
|
||||
[ValidateSet('7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2', 'Zstd')]
|
||||
[ValidateSet('Git', '7zip', 'Lessmsi', 'Innounp', 'Dark', 'Aria2', 'Zstd')]
|
||||
[String]
|
||||
$Helper
|
||||
)
|
||||
@@ -304,6 +425,14 @@ function Get-HelperPath {
|
||||
}
|
||||
process {
|
||||
switch ($Helper) {
|
||||
'Git' {
|
||||
$internalgit = (Get-AppFilePath 'git' 'mingw64\bin\git.exe'), (Get-AppFilePath 'git' 'mingw32\bin\git.exe') | Where-Object { $_ -ne $null }
|
||||
if ($internalgit) {
|
||||
$HelperPath = $internalgit
|
||||
} else {
|
||||
$HelperPath = (Get-Command git -ErrorAction Ignore).Source
|
||||
}
|
||||
}
|
||||
'7zip' {
|
||||
$HelperPath = Get-AppFilePath '7zip' '7z.exe'
|
||||
if ([String]::IsNullOrEmpty($HelperPath)) {
|
||||
@@ -346,7 +475,10 @@ function Get-CommandPath {
|
||||
} catch {
|
||||
return $null
|
||||
}
|
||||
$commandPath = if ($comm.Path -like "$userShims*" -or $comm.Path -like "$globalShims*") {
|
||||
$commandPath = if ($comm.Path -like "$userShims\scoop-*.ps1") {
|
||||
# Scoop aliases
|
||||
$comm.Source
|
||||
} elseif ($comm.Path -like "$userShims*" -or $comm.Path -like "$globalShims*") {
|
||||
Get-ShimTarget ($comm.Path -replace '\.exe$', '.shim')
|
||||
} elseif ($comm.CommandType -eq 'Application') {
|
||||
$comm.Source
|
||||
@@ -394,7 +526,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)
|
||||
@@ -491,6 +623,9 @@ function Invoke-ExternalCommand {
|
||||
[Parameter(ParameterSetName = "UseShellExecute")]
|
||||
[Switch]
|
||||
$RunAs,
|
||||
[Parameter(ParameterSetName = "UseShellExecute")]
|
||||
[Switch]
|
||||
$Quiet,
|
||||
[Alias("Msg")]
|
||||
[String]
|
||||
$Activity,
|
||||
@@ -507,12 +642,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
|
||||
}
|
||||
@@ -521,8 +656,35 @@ function Invoke-ExternalCommand {
|
||||
$Process.StartInfo.UseShellExecute = $true
|
||||
$Process.StartInfo.Verb = 'RunAs'
|
||||
}
|
||||
if ($Quiet) {
|
||||
$Process.StartInfo.UseShellExecute = $true
|
||||
$Process.StartInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
|
||||
}
|
||||
if ($ArgumentList.Length -gt 0) {
|
||||
if ($FilePath -match '^((cmd|cscript|wscript|msiexec)(\.exe)?|.*\.(bat|cmd|js|vbs|wsf))$') {
|
||||
$Process.StartInfo.Arguments = $ArgumentList -join ' '
|
||||
} elseif ($Process.StartInfo.ArgumentList.Add) {
|
||||
# ArgumentList 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 +692,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,17 +724,55 @@ 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 Publish-Env {
|
||||
if (-not ("Win32.NativeMethods" -as [Type])) {
|
||||
Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @"
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr SendMessageTimeout(
|
||||
IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
|
||||
uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
|
||||
"@
|
||||
}
|
||||
|
||||
$HWND_BROADCAST = [IntPtr] 0xffff;
|
||||
$WM_SETTINGCHANGE = 0x1a;
|
||||
$result = [UIntPtr]::Zero
|
||||
|
||||
[Win32.Nativemethods]::SendMessageTimeout($HWND_BROADCAST,
|
||||
$WM_SETTINGCHANGE,
|
||||
[UIntPtr]::Zero,
|
||||
"Environment",
|
||||
2,
|
||||
5000,
|
||||
[ref] $result
|
||||
) | Out-Null
|
||||
}
|
||||
|
||||
function env($name,$global,$val='__get') {
|
||||
$target = 'User'; if($global) {$target = 'Machine'}
|
||||
if($val -eq '__get') { [environment]::getEnvironmentVariable($name,$target) }
|
||||
else { [environment]::setEnvironmentVariable($name,$val,$target) }
|
||||
function env($name, $global, $val = '__get') {
|
||||
$RegisterKey = if ($global) {
|
||||
Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
|
||||
} else {
|
||||
Get-Item -Path 'HKCU:'
|
||||
}
|
||||
$EnvRegisterKey = $RegisterKey.OpenSubKey('Environment', $val -ne '__get')
|
||||
|
||||
if ($val -eq '__get') {
|
||||
$RegistryValueOption = [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames
|
||||
$EnvRegisterKey.GetValue($name, $null, $RegistryValueOption)
|
||||
} elseif ($val -eq $null) {
|
||||
try { $EnvRegisterKey.DeleteValue($name) } catch { }
|
||||
Publish-Env
|
||||
} else {
|
||||
$RegistryValueKind = if ($val.Contains('%')) {
|
||||
[Microsoft.Win32.RegistryValueKind]::ExpandString
|
||||
} elseif ($EnvRegisterKey.GetValue($name)) {
|
||||
$EnvRegisterKey.GetValueKind($name)
|
||||
} else {
|
||||
[Microsoft.Win32.RegistryValueKind]::String
|
||||
}
|
||||
$EnvRegisterKey.SetValue($name, $val, $RegistryValueKind)
|
||||
Publish-Env
|
||||
}
|
||||
}
|
||||
|
||||
function isFileLocked([string]$path) {
|
||||
@@ -604,12 +810,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))"
|
||||
}
|
||||
|
||||
@@ -650,7 +856,7 @@ function Get-ShimTarget($ShimPath) {
|
||||
if (!$shimTarget) {
|
||||
$shimTarget = ((Select-String -Path $ShimPath -Pattern '[''"]([^@&]*?)[''"]' -AllMatches).Matches.Groups | Select-Object -Last 1).Value
|
||||
}
|
||||
$shimTarget | Convert-Path
|
||||
$shimTarget | Convert-Path -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,10 +888,10 @@ function shim($path, $global, $name, $arg) {
|
||||
$shim = "$abs_shimdir\$($name.tolower())"
|
||||
|
||||
# convert to relative path
|
||||
$resolved_path = Convert-Path $path
|
||||
Push-Location $abs_shimdir
|
||||
$relative_path = Resolve-Path -Relative $path
|
||||
$relative_path = Resolve-Path -Relative $resolved_path
|
||||
Pop-Location
|
||||
$resolved_path = Resolve-Path $path
|
||||
|
||||
if ($path -match '\.(exe|com)$') {
|
||||
# for programs with no awareness of any shell
|
||||
@@ -695,6 +901,13 @@ function shim($path, $global, $name, $arg) {
|
||||
if ($arg) {
|
||||
Write-Output "args = $arg" | Out-UTF8File "$shim.shim" -Append
|
||||
}
|
||||
|
||||
$target_subsystem = Get-Subsystem $resolved_path
|
||||
|
||||
if (($target_subsystem -ne 3) -and ($target_subsystem -ge 0)) { # Subsystem -eq 3 means `Console`, -ge 0 to ignore
|
||||
Write-Output "Making $shim.exe a GUI binary."
|
||||
Change-Subsystem "$shim.exe" $target_subsystem
|
||||
}
|
||||
} elseif ($path -match '\.(bat|cmd)$') {
|
||||
# shim .bat, .cmd so they can be used by programs with no awareness of PSH
|
||||
warn_on_overwrite "$shim.cmd" $path
|
||||
@@ -780,14 +993,9 @@ function shim($path, $global, $name, $arg) {
|
||||
) -join "`n" | Out-UTF8File $shim -NoNewLine
|
||||
} else {
|
||||
warn_on_overwrite "$shim.cmd" $path
|
||||
# find path to Git's bash so that batch scripts can run bash scripts
|
||||
$gitdir = (Get-Item (Get-CommandPath git) -ErrorAction:Stop).Directory.Parent
|
||||
if ($gitdir.FullName -imatch 'mingw') {
|
||||
$gitdir = $gitdir.Parent
|
||||
}
|
||||
@(
|
||||
"@rem $resolved_path",
|
||||
"@`"$(Join-Path (Join-Path $gitdir.FullName 'bin') 'bash.exe')`" `"$resolved_path`" $arg %*"
|
||||
"@bash `"$resolved_path`" $arg %*"
|
||||
) -join "`r`n" | Out-UTF8File "$shim.cmd"
|
||||
|
||||
warn_on_overwrite $shim $path
|
||||
@@ -801,7 +1009,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 +1040,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 +1198,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 +1298,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 +1398,74 @@ 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
|
||||
# Check if it's the expected install path for scoop: <root>/apps/scoop/current
|
||||
$coreRoot = Split-Path $PSScriptRoot
|
||||
$pathExpected = ($coreRoot -replace '\\','/') -like '*apps/scoop/current*'
|
||||
if ($pathExpected) {
|
||||
# Portable config is located in root directory:
|
||||
# .\current\scoop\apps\<root>\config.json <- a reversed path
|
||||
# Imagine `<root>/apps/scoop/current/` in a reversed format,
|
||||
# and the directory tree:
|
||||
#
|
||||
# ```
|
||||
# <root>:
|
||||
# ├─apps
|
||||
# ├─buckets
|
||||
# ├─cache
|
||||
# ├─persist
|
||||
# ├─shims
|
||||
# ├─config.json
|
||||
# ```
|
||||
$configPortablePath = fullpath "$coreRoot\..\..\..\config.json"
|
||||
if (Test-Path $configPortablePath) {
|
||||
$configFile = $configPortablePath
|
||||
}
|
||||
}
|
||||
|
||||
# Load Scoop config
|
||||
$scoopConfig = load_cfg $configFile
|
||||
|
||||
# NOTE Scoop config file migration. Remove this after 2023/6/30
|
||||
if ($scoopConfig -and $scoopConfig.PSObject.Properties.Name -contains 'lastUpdate') {
|
||||
$newConfigNames = @{
|
||||
'lastUpdate' = 'last_update'
|
||||
'SCOOP_REPO' = 'scoop_repo'
|
||||
'SCOOP_BRANCH' = 'scoop_branch'
|
||||
'7ZIPEXTRACT_USE_EXTERNAL' = 'use_external_7zip'
|
||||
'MSIEXTRACT_USE_LESSMSI' = 'use_lessmsi'
|
||||
'NO_JUNCTIONS' = 'no_junction'
|
||||
'manifest_review' = 'show_manifest'
|
||||
'rootPath' = 'root_path'
|
||||
'globalPath' = 'global_path'
|
||||
'cachePath' = 'cache_path'
|
||||
}
|
||||
$newConfigNames.GetEnumerator() | ForEach-Object {
|
||||
if ($null -ne $scoopConfig.$($_.Key)) {
|
||||
$value = $scoopConfig.$($_.Key)
|
||||
$scoopConfig.PSObject.Properties.Remove($_.Key)
|
||||
$scoopConfig | Add-Member -MemberType NoteProperty -Name $_.Value -Value $value
|
||||
}
|
||||
}
|
||||
ConvertTo-Json $scoopConfig | Out-UTF8File -FilePath $configFile
|
||||
}
|
||||
# END NOTE
|
||||
|
||||
# Scoop root directory
|
||||
$scoopdir = $env:SCOOP, (get_config 'rootPath'), "$env:USERPROFILE\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
|
||||
$scoopdir = $env:SCOOP, (get_config ROOT_PATH), (Resolve-Path "$PSScriptRoot\..\..\..\.."), "$([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
|
||||
|
||||
@@ -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", '-xr!*.nsis', '-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
|
||||
@@ -97,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)
|
||||
@@ -146,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) {
|
||||
@@ -200,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" }
|
||||
@@ -240,7 +241,14 @@ function Expand-ZipArchive {
|
||||
$OriDestinationPath = $DestinationPath
|
||||
$DestinationPath = "$DestinationPath\_tmp"
|
||||
}
|
||||
Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force
|
||||
# Disable progress bar to gain performance
|
||||
$oldProgressPreference = $ProgressPreference
|
||||
$global:ProgressPreference = 'SilentlyContinue'
|
||||
|
||||
# Compatible with Pscx v3 (https://github.com/Pscx/Pscx) ('Microsoft.PowerShell.Archive' is not needed for Pscx v4)
|
||||
Microsoft.PowerShell.Archive\Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force
|
||||
|
||||
$global:ProgressPreference = $oldProgressPreference
|
||||
if ($ExtractDir) {
|
||||
movedir "$DestinationPath\$ExtractDir" $OriDestinationPath | Out-Null
|
||||
Remove-Item $DestinationPath -Recurse -Force
|
||||
@@ -267,7 +275,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)
|
||||
}
|
||||
|
||||
@@ -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 *')) {
|
||||
|
||||
@@ -3,7 +3,7 @@ Diagnostic tests.
|
||||
Return $true if the test passed, otherwise $false.
|
||||
Use 'warn' to highlight the issue, and follow up with the recommended actions to rectify.
|
||||
#>
|
||||
function check_windows_defender($global) {
|
||||
function Invoke-WindowsDefenderCheck($global) {
|
||||
$defender = Get-Service -Name WinDefend -ErrorAction SilentlyContinue
|
||||
if (Test-CommandAvailable Get-MpPreference) {
|
||||
if ((Get-MpPreference).DisableRealtimeMonitoring) { return $true }
|
||||
@@ -26,8 +26,12 @@ function check_windows_defender($global) {
|
||||
return $true
|
||||
}
|
||||
|
||||
function check_main_bucket {
|
||||
if ((Get-LocalBucket) -notcontains 'main') {
|
||||
function Test-MainBucketAvailable {
|
||||
return ((Get-LocalBucket) -contains 'main')
|
||||
}
|
||||
|
||||
function Invoke-MainBucketCheck {
|
||||
if (!(Test-MainBucketAvailable)) {
|
||||
warn 'Main bucket is not added.'
|
||||
Write-Host " run 'scoop bucket add main'"
|
||||
|
||||
@@ -37,13 +41,25 @@ function check_main_bucket {
|
||||
return $true
|
||||
}
|
||||
|
||||
function check_long_paths {
|
||||
if ([System.Environment]::OSVersion.Version.Major -lt 10 -or [System.Environment]::OSVersion.Version.Build -lt 1607) {
|
||||
function Test-WindowsLongPathsSupported {
|
||||
return !([System.Environment]::OSVersion.Version.Major -lt 10 -or [System.Environment]::OSVersion.Version.Build -lt 1607)
|
||||
}
|
||||
|
||||
function Test-WindowsLongPathsEnabled {
|
||||
return ((Get-ItemProperty `
|
||||
-Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' `
|
||||
-Name 'LongPathsEnabled' `
|
||||
-ErrorAction SilentlyContinue
|
||||
).LongPathsEnabled -eq 1)
|
||||
}
|
||||
|
||||
function Invoke-LongPathsCheck {
|
||||
if (!(Test-WindowsLongPathsSupported)) {
|
||||
warn 'This version of Windows does not support configuration of LongPaths.'
|
||||
return $false
|
||||
}
|
||||
$key = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -ErrorAction SilentlyContinue -Name 'LongPathsEnabled'
|
||||
if (!$key -or ($key.LongPathsEnabled -eq 0)) {
|
||||
|
||||
if (!(Test-WindowsLongPathsEnabled)) {
|
||||
warn 'LongPaths support is not enabled.'
|
||||
Write-Host " You can enable it by running:"
|
||||
Write-Host " sudo Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1"
|
||||
@@ -53,3 +69,174 @@ function check_long_paths {
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
function Test-WindowsDeveloperModeEnabled {
|
||||
return ((Get-ItemProperty `
|
||||
-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock' `
|
||||
-Name 'AllowDevelopmentWithoutDevLicense' `
|
||||
-ErrorAction SilentlyContinue).AllowDevelopmentWithoutDevLicense -eq 1)
|
||||
}
|
||||
|
||||
function Invoke-WindowsDeveloperModeCheck {
|
||||
if (!(Test-WindowsDeveloperModeEnabled)) {
|
||||
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
|
||||
}
|
||||
|
||||
function Show-Value {
|
||||
[CmdletBinding()]
|
||||
[OutputType([String])]
|
||||
param(
|
||||
[Parameter(Mandatory = $true, Position = 0)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[String]
|
||||
$Name,
|
||||
[String]
|
||||
$Value,
|
||||
[Switch]
|
||||
$Redacted,
|
||||
[Switch]
|
||||
$Color,
|
||||
[Int]
|
||||
$PadRight = 12
|
||||
)
|
||||
|
||||
if ([String]::IsNullOrEmpty($Value)) {
|
||||
return
|
||||
}
|
||||
|
||||
$Red = "`e[31m"
|
||||
$Green = "`e[32m"
|
||||
$Yellow = "`e[33m"
|
||||
$Cyan = "`e[36m"
|
||||
$End = "`e[0m"
|
||||
if (!$Color) {
|
||||
$Red, $Green, $Yellow, $Cyan, $End = '', '', '', '', ''
|
||||
}
|
||||
|
||||
if ($Redacted) {
|
||||
$Value = "$Red<redacted>$End"
|
||||
}
|
||||
|
||||
$Value = "$Value".Replace($env:USERPROFILE, "$Green`$env:USERPROFILE$End")
|
||||
$Value = "$Value".Replace($env:USERNAME, "$Green<username>$End")
|
||||
|
||||
$Name = $Name.PadRight($PadRight, ' ')
|
||||
|
||||
if ($Value -eq $True) {
|
||||
$Value = "$Green$Value$End"
|
||||
} elseif ($Value -eq $False) {
|
||||
$Value = "$Yellow$Value$End"
|
||||
}
|
||||
Write-Output "$Cyan$Name$End = $Value"
|
||||
}
|
||||
|
||||
function Show-Header {
|
||||
[CmdletBinding()]
|
||||
[OutputType([String])]
|
||||
param(
|
||||
[Parameter(Mandatory = $true, Position = 0)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[String]
|
||||
$Value,
|
||||
[Switch]
|
||||
$Color
|
||||
)
|
||||
|
||||
if ($Color) {
|
||||
Write-Output "`e[35m[$Value]`e[0m"
|
||||
} else {
|
||||
Write-Output "[$Value]"
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ParentProcess {
|
||||
$parent = [System.Diagnostics.Process]::GetCurrentProcess()
|
||||
while ($parent.MainModule.ModuleName -ieq 'pwsh.exe' -or $parent.MainModule.ModuleName -ieq 'powershell.exe') {
|
||||
$parent = $parent.Parent
|
||||
}
|
||||
return $parent.MainModule.ModuleName
|
||||
}
|
||||
|
||||
function Show-Diag {
|
||||
[CmdletBinding()]
|
||||
[OutputType([String])]
|
||||
param(
|
||||
[Switch]
|
||||
$Markdown,
|
||||
[Switch]
|
||||
$Color
|
||||
)
|
||||
|
||||
$redactedConfigValues = @(
|
||||
'virustotal_api_key'
|
||||
'private_hosts'
|
||||
'gh_token'
|
||||
'proxy'
|
||||
'analytics_id'
|
||||
'alias'
|
||||
)
|
||||
|
||||
if ($Markdown) {
|
||||
Write-Output "`n"
|
||||
Write-Output '```ini'
|
||||
}
|
||||
|
||||
Show-Header -Color:$Color -Value 'PowerShell'
|
||||
Show-Value -Color:$Color -Name 'Path' -Value ([System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName)
|
||||
Show-Value -Color:$Color -Name 'Version' -Value $PSversionTable.PSVersion.ToString()
|
||||
Show-Value -Color:$Color -Name 'Edition' -Value $PSversionTable.PSEdition
|
||||
Show-Value -Color:$Color -Name 'Architecture' -Value (Get-DefaultArchitecture)
|
||||
Show-Value -Color:$Color -Name 'RunAsAdmin' -Value (Test-IsAdmin)
|
||||
Show-Value -Color:$Color -Name 'Parent' -Value (Get-ParentProcess)
|
||||
|
||||
Show-Header -Color:$Color -Value 'Helpers'
|
||||
Show-Value -Color:$Color -Name 'GitPath' -Value (Get-HelperPath -Helper Git)
|
||||
Show-Value -Color:$Color -Name 'GitVersion' -Value (Invoke-Git -Path $PSScriptRoot -ArgumentList 'version')
|
||||
Show-Value -Color:$Color -Name 'Zip' -Value (Test-HelperInstalled -Helper '7zip')
|
||||
Show-Value -Color:$Color -Name 'Lessmsi' -Value (Test-HelperInstalled -Helper 'Lessmsi')
|
||||
Show-Value -Color:$Color -Name 'Innounp' -Value (Test-HelperInstalled -Helper 'Innounp')
|
||||
Show-Value -Color:$Color -Name 'Dark' -Value (Test-HelperInstalled -Helper 'Dark')
|
||||
Show-Value -Color:$Color -Name 'Aria2' -Value (Test-HelperInstalled -Helper 'Aria2')
|
||||
Show-Value -Color:$Color -Name 'Zstd' -Value (Test-HelperInstalled -Helper 'Zstd')
|
||||
|
||||
Show-Header -Color:$Color -Value 'Environment'
|
||||
Show-Value -Color:$Color -Name 'SCOOP' -Value $env:SCOOP
|
||||
Show-Value -Color:$Color -Name 'SCOOP_GLOBAL' -Value $env:SCOOP_GLOBAL
|
||||
Show-Value -Color:$Color -Name 'SCOOP_CACHE' -Value $env:SCOOP_CACHE
|
||||
Show-Value -Color:$Color -Name 'HTTPS_PROXY' -Value $env:HTTPS_PROXY -Redacted
|
||||
Show-Value -Color:$Color -Name 'HTTP_PROXY' -Value $env:HTTP_PROXY -Redacted
|
||||
|
||||
Show-Header -Color:$Color -Value 'Scoop'
|
||||
Show-Value -Color:$Color -Name 'Outdated' -Value (is_scoop_outdated)
|
||||
Show-Value -Color:$Color -Name 'OnHold' -Value (Test-ScoopCoreOnHold)
|
||||
Show-Value -Color:$Color -Name 'Config' -Value $configFile
|
||||
Show-Value -Color:$Color -Name 'CoreRoot' -Value $coreRoot
|
||||
Show-Value -Color:$Color -Name 'ScoopDir' -Value $scoopdir
|
||||
Show-Value -Color:$Color -Name 'CacheDir' -Value $cachedir
|
||||
Show-Value -Color:$Color -Name 'GlobalDir' -Value $globaldir
|
||||
|
||||
Show-Header -Color:$Color -Value 'Config'
|
||||
$pad = ($scoopConfig.PSObject.Properties.Name | Measure-Object -Maximum -Property Length).Maximum
|
||||
$scoopConfig.PSObject.Properties | ForEach-Object {
|
||||
Show-Value -Color:$Color -Name $_.Name -Value $_.Value -PadRight $pad -Redacted:($redactedConfigValues.Contains($_.Name))
|
||||
}
|
||||
|
||||
Show-Header -Color:$Color -Value 'Windows'
|
||||
$pad = 16
|
||||
Show-Value -Color:$Color -Name 'DeveloperMode' -PadRight $pad -Value (Test-WindowsDeveloperModeEnabled)
|
||||
Show-Value -Color:$Color -Name 'LongPathsEnabled' -PadRight $pad -Value (Test-WindowsLongPathsEnabled)
|
||||
Show-Value -Color:$Color -Name 'ScoopDirFormat' -PadRight $pad -Value ((New-Object System.IO.DriveInfo($scoopdir)).DriveFormat)
|
||||
Show-Value -Color:$Color -Name 'GlobalDirFormat' -PadRight $pad -Value ((New-Object System.IO.DriveInfo($globaldir)).DriveFormat)
|
||||
Show-Value -Color:$Color -Name 'WindowsDefender' -PadRight $pad -Value ((Get-Service -Name WinDefend -ErrorAction SilentlyContinue).Status)
|
||||
|
||||
if ($Markdown) {
|
||||
Write-Output '```'
|
||||
Write-Output "`n"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,12 @@
|
||||
# array of strings that are long-form options. options that take
|
||||
# a parameter should end with '='
|
||||
# returns @(opts hash, remaining_args array, error string)
|
||||
function getopt($argv, $shortopts, $longopts) {
|
||||
# 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([String[]]$argv, [String]$shortopts, [String[]]$longopts) {
|
||||
$opts = @{}; $rem = @()
|
||||
|
||||
function err($msg) {
|
||||
@@ -16,29 +21,31 @@ function getopt($argv, $shortopts, $longopts) {
|
||||
}
|
||||
|
||||
function regex_escape($str) {
|
||||
return [regex]::escape($str)
|
||||
return [Regex]::Escape($str)
|
||||
}
|
||||
|
||||
# ensure these are arrays
|
||||
$argv = @($argv)
|
||||
$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 +55,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]
|
||||
@@ -70,6 +77,5 @@ function getopt($argv, $shortopts, $longopts) {
|
||||
$rem += $arg
|
||||
}
|
||||
}
|
||||
|
||||
$opts, $rem
|
||||
}
|
||||
|
||||
129
lib/install.ps1
129
lib/install.ps1
@@ -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]) {
|
||||
@@ -361,13 +365,14 @@ function dl($url, $to, $cookies, $progress) {
|
||||
}
|
||||
if ($url -match 'api\.github\.com/repos') {
|
||||
$wreq.Accept = 'application/octet-stream'
|
||||
$wreq.Headers['Authorization'] = "token $(Get-GitHubToken)"
|
||||
$wreq.Headers['Authorization'] = "Bearer $(Get-GitHubToken)"
|
||||
$wreq.Headers['X-GitHub-Api-Version'] = "2022-11-28"
|
||||
}
|
||||
if ($cookies) {
|
||||
$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 +410,7 @@ function dl($url, $to, $cookies, $progress) {
|
||||
$newUrl = "$newUrl#/$postfix"
|
||||
}
|
||||
|
||||
dl $newUrl $to $cookies $progress
|
||||
Invoke-Download $newUrl $to $cookies $progress
|
||||
return
|
||||
}
|
||||
|
||||
@@ -416,12 +421,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 +438,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 +464,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 +503,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 +521,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 +546,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 +589,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'
|
||||
@@ -628,7 +633,7 @@ function cookie_header($cookies) {
|
||||
function is_in_dir($dir, $check) {
|
||||
$check = "$(fullpath $check)"
|
||||
$dir = "$(fullpath $dir)"
|
||||
$check -match "^$([regex]::escape("$dir"))(\\|`$)"
|
||||
$check -match "^$([regex]::Escape("$dir"))([/\\]|`$)"
|
||||
}
|
||||
|
||||
function ftp_file_size($url) {
|
||||
@@ -655,7 +660,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 +672,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 +692,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 +891,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 +918,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 +1028,7 @@ function Invoke-HookScript {
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[PSCustomObject] $Manifest,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('32bit', '64bit')]
|
||||
[ValidateSet('32bit', '64bit', 'arm64')]
|
||||
[String] $Arch
|
||||
)
|
||||
|
||||
@@ -1081,13 +1067,18 @@ function ensure_none_failed($apps) {
|
||||
foreach ($app in $apps) {
|
||||
$app = ($app -split '/|\\')[-1] -replace '\.json$', ''
|
||||
foreach ($global in $true, $false) {
|
||||
if ($global) {
|
||||
$instArgs = @('--global')
|
||||
} else {
|
||||
$instArgs = @()
|
||||
}
|
||||
if (failed $app $global) {
|
||||
if (installed $app $global) {
|
||||
info "Repair previous failed installation of $app."
|
||||
& "$PSScriptRoot\..\libexec\scoop-reset.ps1" $app$(if ($global) { ' --global' })
|
||||
& "$PSScriptRoot\..\libexec\scoop-reset.ps1" $app @instArgs
|
||||
} else {
|
||||
warn "Purging previous failed installation of $app."
|
||||
& "$PSScriptRoot\..\libexec\scoop-uninstall.ps1" $app$(if ($global) { ' --global' })
|
||||
& "$PSScriptRoot\..\libexec\scoop-uninstall.ps1" $app @instArgs
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1194,7 +1185,7 @@ function unlink_persist_data($manifest, $dir) {
|
||||
if ($persist) {
|
||||
@($persist) | ForEach-Object {
|
||||
$source, $null = persist_def $_
|
||||
$source = Get-Item "$dir\$source"
|
||||
$source = Get-Item "$dir\$source" -ErrorAction SilentlyContinue
|
||||
if ($source.LinkType) {
|
||||
$source_path = $source.FullName
|
||||
# directory (junction)
|
||||
@@ -1214,7 +1205,7 @@ function unlink_persist_data($manifest, $dir) {
|
||||
|
||||
# check whether write permission for Users usergroup is set to global persist dir, if not then set
|
||||
function persist_permission($manifest, $global) {
|
||||
if($global -and $manifest.persist -and (is_admin)) {
|
||||
if($global -and $manifest.persist -and (Test-IsAdmin)) {
|
||||
$path = persistdir $null $global
|
||||
$user = New-Object System.Security.Principal.SecurityIdentifier 'S-1-5-32-545'
|
||||
$target_rule = New-Object System.Security.AccessControl.FileSystemAccessRule($user, 'Write', 'ObjectInherit', 'none', 'Allow')
|
||||
@@ -1227,14 +1218,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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
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 }
|
||||
if ($null -eq $path -or !(Test-Path $path)) { return $null }
|
||||
try {
|
||||
Get-Content $path -Raw -Encoding UTF8 | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
@@ -102,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
|
||||
@@ -128,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) {
|
||||
@@ -145,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"
|
||||
}
|
||||
|
||||
@@ -1,29 +1,24 @@
|
||||
$modulesdir = "$scoopdir\modules"
|
||||
|
||||
function install_psmodule($manifest, $dir, $global) {
|
||||
$psmodule = $manifest.psmodule
|
||||
if (!$psmodule) { return }
|
||||
|
||||
if ($global) {
|
||||
abort 'Installing PowerShell modules globally is not implemented!'
|
||||
}
|
||||
$targetdir = ensure (modulesdir $global)
|
||||
|
||||
$modulesdir = ensure $modulesdir
|
||||
ensure_in_psmodulepath $modulesdir $global
|
||||
ensure_in_psmodulepath $targetdir $global
|
||||
|
||||
$module_name = $psmodule.name
|
||||
if (!$module_name) {
|
||||
abort "Invalid manifest: The 'name' property is missing from 'psmodule'."
|
||||
}
|
||||
|
||||
$linkfrom = "$modulesdir\$module_name"
|
||||
$linkfrom = "$targetdir\$module_name"
|
||||
Write-Host "Installing PowerShell module '$module_name'"
|
||||
|
||||
Write-Host "Linking $(friendly_path $linkfrom) => $(friendly_path $dir)"
|
||||
|
||||
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
|
||||
@@ -36,11 +31,13 @@ function uninstall_psmodule($manifest, $dir, $global) {
|
||||
$module_name = $psmodule.name
|
||||
Write-Host "Uninstalling PowerShell module '$module_name'."
|
||||
|
||||
$linkfrom = "$modulesdir\$module_name"
|
||||
$targetdir = modulesdir $global
|
||||
|
||||
$linkfrom = "$targetdir\$module_name"
|
||||
if (Test-Path $linkfrom) {
|
||||
Write-Host "Removing $(friendly_path $linkfrom)"
|
||||
$linkfrom = Resolve-Path $linkfrom
|
||||
Remove-Item -Path $linkfrom -Force -ErrorAction SilentlyContinue
|
||||
$linkfrom = Convert-Path $linkfrom
|
||||
Remove-Item -Path $linkfrom -Force -Recurse -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,16 +5,16 @@ function create_startmenu_shortcuts($manifest, $dir, $global, $arch) {
|
||||
$target = [System.IO.Path]::Combine($dir, $_.item(0))
|
||||
$target = New-Object System.IO.FileInfo($target)
|
||||
$name = $_.item(1)
|
||||
$arguments = ""
|
||||
$arguments = ''
|
||||
$icon = $null
|
||||
if($_.length -ge 3) {
|
||||
if ($_.length -ge 3) {
|
||||
$arguments = $_.item(2)
|
||||
}
|
||||
if($_.length -ge 4) {
|
||||
if ($_.length -ge 4) {
|
||||
$icon = [System.IO.Path]::Combine($dir, $_.item(3))
|
||||
$icon = New-Object System.IO.FileInfo($icon)
|
||||
}
|
||||
$arguments = (substitute $arguments @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir})
|
||||
$arguments = (substitute $arguments @{ '$dir' = $dir; '$original_dir' = $original_dir; '$persist_dir' = $persist_dir })
|
||||
startmenu_shortcut $target $name $arguments $icon $global
|
||||
}
|
||||
}
|
||||
@@ -29,11 +29,11 @@ function shortcut_folder($global) {
|
||||
}
|
||||
|
||||
function startmenu_shortcut([System.IO.FileInfo] $target, $shortcutName, $arguments, [System.IO.FileInfo]$icon, $global) {
|
||||
if(!$target.Exists) {
|
||||
if (!$target.Exists) {
|
||||
Write-Host -f DarkRed "Creating shortcut for $shortcutName ($(fname $target)) failed: Couldn't find $target"
|
||||
return
|
||||
}
|
||||
if($icon -and !$icon.Exists) {
|
||||
if ($icon -and !$icon.Exists) {
|
||||
Write-Host -f DarkRed "Creating shortcut for $shortcutName ($(fname $target)) failed: Couldn't find icon $icon"
|
||||
return
|
||||
}
|
||||
@@ -51,11 +51,11 @@ function startmenu_shortcut([System.IO.FileInfo] $target, $shortcutName, $argume
|
||||
if ($arguments) {
|
||||
$wsShell.Arguments = $arguments
|
||||
}
|
||||
if($icon -and $icon.Exists) {
|
||||
if ($icon -and $icon.Exists) {
|
||||
$wsShell.IconLocation = $icon.FullName
|
||||
}
|
||||
$wsShell.Save()
|
||||
write-host "Creating shortcut for $shortcutName ($(fname $target))"
|
||||
Write-Host "Creating shortcut for $shortcutName ($(fname $target))"
|
||||
}
|
||||
|
||||
# Removes the Startmenu shortcut if it exists
|
||||
@@ -63,10 +63,10 @@ function rm_startmenu_shortcuts($manifest, $global, $arch) {
|
||||
$shortcuts = @(arch_specific 'shortcuts' $manifest $arch)
|
||||
$shortcuts | Where-Object { $_ -ne $null } | ForEach-Object {
|
||||
$name = $_.item(1)
|
||||
$shortcut = "$(shortcut_folder $global)\$name.lnk"
|
||||
write-host "Removing shortcut $(friendly_path $shortcut)"
|
||||
if(Test-Path -Path $shortcut) {
|
||||
Remove-Item $shortcut
|
||||
$shortcut = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$(shortcut_folder $global)\$name.lnk")
|
||||
Write-Host "Removing shortcut $(friendly_path $shortcut)"
|
||||
if (Test-Path -Path $shortcut) {
|
||||
Remove-Item $shortcut
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
lib/unix.ps1
45
lib/unix.ps1
@@ -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
|
||||
}
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -8,40 +8,40 @@
|
||||
$issues = 0
|
||||
$defenderIssues = 0
|
||||
|
||||
$adminPrivileges = ([System.Security.Principal.WindowsPrincipal] [System.Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
|
||||
if ($adminPrivileges) {
|
||||
$defenderIssues += !(check_windows_defender $false)
|
||||
$defenderIssues += !(check_windows_defender $true)
|
||||
if (Test-IsAdmin -and $env:USERNAME -ne 'WDAGUtilityAccount') {
|
||||
$defenderIssues += !(Invoke-WindowsDefenderCheck $false)
|
||||
$defenderIssues += !(Invoke-WindowsDefenderCheck $true)
|
||||
}
|
||||
|
||||
$issues += !(check_main_bucket)
|
||||
$issues += !(check_long_paths)
|
||||
$issues += !(Invoke-MainBucketCheck)
|
||||
$issues += !(Invoke-LongPathsCheck)
|
||||
$issues += !(Invoke-WindowsDeveloperModeCheck)
|
||||
|
||||
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'."
|
||||
warn "'7-Zip' is not installed! It's required for unpacking most programs. Please Run 'scoop install 7zip' or 'scoop install 7zip-zstd'."
|
||||
$issues++
|
||||
}
|
||||
|
||||
if (!(Test-HelperInstalled -Helper Innounp)) {
|
||||
error "'Inno Setup Unpacker' is not installed! It's required for unpacking InnoSetup files. Please run 'scoop install innounp'."
|
||||
warn "'Inno Setup Unpacker' is not installed! It's required for unpacking InnoSetup files. Please run 'scoop install innounp'."
|
||||
$issues++
|
||||
}
|
||||
|
||||
if (!(Test-HelperInstalled -Helper Dark)) {
|
||||
error "'dark' is not installed! It's required for unpacking installers created with the WiX Toolset. Please run 'scoop install dark' or 'scoop install wixtoolset'."
|
||||
warn "'dark' is not installed! It's required for unpacking installers created with the WiX Toolset. Please run 'scoop install dark' or 'scoop install wixtoolset'."
|
||||
$issues++
|
||||
}
|
||||
|
||||
$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++
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ $all = $opt.a -or $opt.all
|
||||
|
||||
if (!$apps -and !$all) { 'ERROR: <app> missing'; my_usage; exit 1 }
|
||||
|
||||
if ($global -and !(is_admin)) {
|
||||
if ($global -and !(Test-IsAdmin)) {
|
||||
'ERROR: you need admin rights to cleanup global apps'; exit 1
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
|
||||
. "$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: $_"
|
||||
}
|
||||
|
||||
8
libexec/scoop-diag.ps1
Normal file
8
libexec/scoop-diag.ps1
Normal file
@@ -0,0 +1,8 @@
|
||||
# Usage: scoop diag
|
||||
# Summary: Returns information about the Scoop environment that can be posted on a GitHub issue
|
||||
|
||||
. "$PSScriptRoot\..\lib\diagnostic.ps1"
|
||||
|
||||
Show-Diag -Markdown -Color
|
||||
|
||||
exit 0
|
||||
@@ -14,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='
|
||||
@@ -30,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: $_"
|
||||
}
|
||||
@@ -57,7 +57,7 @@ foreach ($curr_app in $apps) {
|
||||
$app, $bucket, $version = parse_app $curr_app
|
||||
$app, $manifest, $bucket, $url = Get-Manifest "$bucket/$app"
|
||||
|
||||
info "Starting download for $app..."
|
||||
info "Downloading '$app'$(if ($version) { " ($version)" }) [$architecture]$(if ($bucket) { " from $bucket bucket" })"
|
||||
|
||||
# Generate manifest if there is different version in manifest
|
||||
if (($null -ne $version) -and ($manifest.version -ne $version)) {
|
||||
@@ -85,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"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Usage: scoop export > scoopfile.json
|
||||
# Summary: Exports installed apps, buckets (and optionally configs) in JSON format
|
||||
# Options:
|
||||
# Help: Options:
|
||||
# -c, --config Export the Scoop configuration file too
|
||||
|
||||
. "$PSScriptRoot\..\lib\json.ps1" # 'ConvertToPrettyJson'
|
||||
@@ -10,7 +10,7 @@ $export = @{}
|
||||
if ($args[0] -eq '-c' -or $args[0] -eq '--config') {
|
||||
$export.config = $scoopConfig
|
||||
# Remove machine-specific properties
|
||||
foreach ($prop in 'lastUpdate', 'rootPath', 'globalPath', 'cachePath', 'alias') {
|
||||
foreach ($prop in 'last_update', 'root_path', 'global_path', 'cache_path', 'alias') {
|
||||
$export.config.PSObject.Properties.Remove($prop)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ if (!$apps) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($global -and !(is_admin)) {
|
||||
if ($global -and !(Test-IsAdmin)) {
|
||||
error 'You need admin rights to hold a global app.'
|
||||
exit 1
|
||||
}
|
||||
@@ -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,15 +47,23 @@ $apps | ForEach-Object {
|
||||
return
|
||||
}
|
||||
|
||||
if (get_config NO_JUNCTIONS) {
|
||||
if (get_config NO_JUNCTION) {
|
||||
$version = Select-CurrentVersion -App $app -Global:$global
|
||||
} else {
|
||||
$version = 'current'
|
||||
}
|
||||
$dir = versiondir $app $version $global
|
||||
$json = install_info $app $version $global
|
||||
if (!$json) {
|
||||
error "Failed to hold '$app'."
|
||||
continue
|
||||
}
|
||||
$install = @{}
|
||||
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }
|
||||
if ($install.hold) {
|
||||
info "'$app' is already held."
|
||||
continue
|
||||
}
|
||||
$install.hold = $true
|
||||
save_install_info $install $dir
|
||||
success "$app is now held and can not be updated anymore."
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 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)]
|
||||
@@ -11,7 +13,7 @@ param(
|
||||
|
||||
$import = $null
|
||||
$bucket_names = @()
|
||||
$def_arch = default_architecture
|
||||
$def_arch = Get-DefaultArchitecture
|
||||
|
||||
if (Test-Path $scoopfile) {
|
||||
$import = parse_json $scoopfile
|
||||
@@ -21,42 +23,43 @@ if (Test-Path $scoopfile) {
|
||||
|
||||
if (!$import) { abort 'Input file not a valid JSON.' }
|
||||
|
||||
$import.config.PSObject.Properties | ForEach-Object {
|
||||
set_config $_.Name $_.Value | Out-Null
|
||||
Write-Host "'$($_.Name)' has been set to '$($_.Value)'"
|
||||
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)'"
|
||||
}
|
||||
|
||||
$import.buckets | ForEach-Object {
|
||||
add_bucket $_.Name $_.Source | Out-Null
|
||||
$bucket_names += $_.Name
|
||||
foreach ($item in $import.buckets) {
|
||||
add_bucket $item.Name $item.Source | Out-Null
|
||||
$bucket_names += $item.Name
|
||||
}
|
||||
|
||||
$import.apps | ForEach-Object {
|
||||
$info = $_.Info -Split ', '
|
||||
$global = if ('Global install' -in $info) {
|
||||
' --global'
|
||||
} else {
|
||||
''
|
||||
foreach ($item in $import.apps) {
|
||||
$instArgs = @()
|
||||
$holdArgs = @()
|
||||
$info = $item.Info -Split ', '
|
||||
if ('Global install' -in $info) {
|
||||
$instArgs += '--global'
|
||||
$holdArgs += '--global'
|
||||
}
|
||||
$arch = if ('64bit' -in $info -and '32bit' -eq $def_arch) {
|
||||
' --arch 64bit'
|
||||
} elseif ('32bit' -in $info -and '64bit' -eq $def_arch) {
|
||||
' --arch 32bit'
|
||||
} else {
|
||||
''
|
||||
if ('64bit' -in $info -and '64bit' -ne $def_arch) {
|
||||
$instArgs += '--arch', '64bit'
|
||||
} elseif ('32bit' -in $info -and '32bit' -ne $def_arch) {
|
||||
$instArgs += '--arch', '32bit'
|
||||
} elseif ('arm64' -in $info -and 'arm64' -ne $def_arch) {
|
||||
$instArgs += '--arch', 'arm64'
|
||||
}
|
||||
|
||||
$app = if ($_.Source -in $bucket_names) {
|
||||
"$($_.Source)/$($_.Name)"
|
||||
} elseif ($_.Source -eq '<auto-generated>') {
|
||||
"$($_.Name)@$($_.Version)"
|
||||
$app = if ($item.Source -in $bucket_names) {
|
||||
"$($item.Source)/$($item.Name)"
|
||||
} elseif ($item.Source -eq '<auto-generated>') {
|
||||
"$($item.Name)@$($item.Version)"
|
||||
} else {
|
||||
$_.Source
|
||||
$item.Source
|
||||
}
|
||||
|
||||
& "$PSScriptRoot\scoop-install.ps1" $app$global$arch
|
||||
& "$PSScriptRoot\scoop-install.ps1" $app @instArgs
|
||||
|
||||
if ('Held package' -in $info) {
|
||||
& "$PSScriptRoot\scoop-hold.ps1" $($_.Name)$global
|
||||
& "$PSScriptRoot\scoop-hold.ps1" $item.Name @holdArgs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Usage: scoop info <app> [--verbose]
|
||||
# Usage: scoop info <app> [options]
|
||||
# Summary: Display information about an app
|
||||
# Options:
|
||||
# -v, --verbose Show full paths and URLs
|
||||
# Help: Options:
|
||||
# -v, --verbose Show full paths and URLs
|
||||
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
|
||||
@@ -84,7 +84,7 @@ if ($manifest.depends) {
|
||||
|
||||
if (Test-Path $manifest_file) {
|
||||
if (Get-Command git -ErrorAction Ignore) {
|
||||
$gitinfo = (git -C (Split-Path $manifest_file) log -1 -s --format='%aD#%an' $manifest_file 2> $null) -Split '#'
|
||||
$gitinfo = (Invoke-Git -Path (Split-Path $manifest_file) -ArgumentList @('log', '-1', '-s', '--format=%aD#%an', $manifest_file) 2> $null) -Split '#'
|
||||
}
|
||||
if ($gitinfo) {
|
||||
$item.'Updated at' = $gitinfo[0] | Get-Date
|
||||
@@ -112,7 +112,7 @@ if ($status.installed) {
|
||||
|
||||
# Collect file list from each location
|
||||
$appFiles = Get-ChildItem $appsdir -Filter $app
|
||||
$currentFiles = Get-ChildItem $appFiles -Filter (Select-CurrentVersion $app $global)
|
||||
$currentFiles = Get-ChildItem $appFiles.FullName -Filter (Select-CurrentVersion $app $global)
|
||||
$persistFiles = Get-ChildItem $persist_dir -ErrorAction Ignore # Will fail if app does not persist data
|
||||
$cacheFiles = Get-ChildItem $cachedir -Filter "$app#*"
|
||||
|
||||
@@ -120,7 +120,7 @@ if ($status.installed) {
|
||||
$fileTotals = @()
|
||||
foreach ($fileType in ($appFiles, $currentFiles, $persistFiles, $cacheFiles)) {
|
||||
if ($null -ne $fileType) {
|
||||
$fileSum = (Get-ChildItem $fileType -Recurse | Measure-Object -Property Length -Sum).Sum
|
||||
$fileSum = (Get-ChildItem $fileType.FullName -Recurse -File | Measure-Object -Property Length -Sum).Sum
|
||||
$fileTotals += coalesce $fileSum 0
|
||||
} else {
|
||||
$fileTotals += 0
|
||||
@@ -158,7 +158,7 @@ if ($status.installed) {
|
||||
if ($verbose) {
|
||||
# Get download size if app not installed
|
||||
$totalPackage = 0
|
||||
foreach ($url in @(url $manifest (default_architecture))) {
|
||||
foreach ($url in @(url $manifest (Get-DefaultArchitecture))) {
|
||||
try {
|
||||
if (Test-Path (fullpath (cache_path $app $manifest.version $url))) {
|
||||
$cached = " (latest version is cached)"
|
||||
|
||||
@@ -14,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"
|
||||
@@ -39,16 +39,16 @@ $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: $_"
|
||||
}
|
||||
|
||||
if (!$apps) { error '<app> missing'; my_usage; exit 1 }
|
||||
|
||||
if ($global -and !(is_admin)) {
|
||||
if ($global -and !(Test-IsAdmin)) {
|
||||
abort 'ERROR: you need admin rights to install global apps'
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -56,14 +56,14 @@ $apps | ForEach-Object {
|
||||
return
|
||||
}
|
||||
|
||||
if($global -and !(is_admin)) {
|
||||
if($global -and !(Test-IsAdmin)) {
|
||||
warn "'$app' ($version) is a global app. You need admin rights to reset it. Skipping."
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ param($query)
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'manifest'
|
||||
. "$PSScriptRoot\..\lib\versions.ps1" # 'Get-LatestVersion'
|
||||
|
||||
$list = @()
|
||||
$list = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
|
||||
try {
|
||||
$query = New-Object Regex $query, 'IgnoreCase'
|
||||
@@ -32,24 +32,90 @@ function bin_match($manifest, $query) {
|
||||
if ((strip_ext $fname) -match $query) { $fname }
|
||||
elseif ($alias -match $query) { $alias }
|
||||
}
|
||||
|
||||
if ($bins) { return $bins }
|
||||
else { return $false }
|
||||
}
|
||||
|
||||
function bin_match_json($json, $query) {
|
||||
[System.Text.Json.JsonElement]$bin = [System.Text.Json.JsonElement]::new()
|
||||
if (!$json.RootElement.TryGetProperty("bin", [ref] $bin)) { return $false }
|
||||
$bins = @()
|
||||
if($bin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($bin) -match $query) {
|
||||
$bins += [System.IO.Path]::GetFileName($bin)
|
||||
} elseif ($bin.ValueKind -eq [System.Text.Json.JsonValueKind]::Array) {
|
||||
foreach($subbin in $bin.EnumerateArray()) {
|
||||
if($subbin.ValueKind -eq [System.Text.Json.JsonValueKind]::String -and [System.IO.Path]::GetFileNameWithoutExtension($subbin) -match $query) {
|
||||
$bins += [System.IO.Path]::GetFileName($subbin)
|
||||
} elseif ($subbin.ValueKind -eq [System.Text.Json.JsonValueKind]::Array) {
|
||||
if([System.IO.Path]::GetFileNameWithoutExtension($subbin[0]) -match $query) {
|
||||
$bins += [System.IO.Path]::GetFileName($subbin[0])
|
||||
} elseif ($subbin.GetArrayLength() -ge 2 -and $subbin[1] -match $query) {
|
||||
$bins += $subbin[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($bins) { return $bins }
|
||||
else { return $false }
|
||||
}
|
||||
|
||||
function search_bucket($bucket, $query) {
|
||||
$apps = apps_in_bucket (Find-BucketDirectory $bucket) | ForEach-Object { @{ name = $_ } }
|
||||
$apps = Get-ChildItem (Find-BucketDirectory $bucket) -Filter '*.json' -Recurse
|
||||
|
||||
if ($query) {
|
||||
$apps = $apps | Where-Object {
|
||||
if ($_.name -match $query) { return $true }
|
||||
$bin = bin_match (manifest $_.name $bucket) $query
|
||||
$apps | ForEach-Object {
|
||||
$json = [System.Text.Json.JsonDocument]::Parse([System.IO.File]::ReadAllText($_.FullName))
|
||||
$name = $_.BaseName
|
||||
|
||||
if ($name -match $query) {
|
||||
$list.Add([PSCustomObject]@{
|
||||
Name = $name
|
||||
Version = $json.RootElement.GetProperty("version")
|
||||
Source = $bucket
|
||||
Binaries = ""
|
||||
})
|
||||
} else {
|
||||
$bin = bin_match_json $json $query
|
||||
if ($bin) {
|
||||
$_.bin = $bin
|
||||
return $true
|
||||
$list.Add([PSCustomObject]@{
|
||||
Name = $name
|
||||
Version = $json.RootElement.GetProperty("version")
|
||||
Source = $bucket
|
||||
Binaries = $bin -join ' | '
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# fallback function for PowerShell 5
|
||||
function search_bucket_legacy($bucket, $query) {
|
||||
$apps = Get-ChildItem (Find-BucketDirectory $bucket) -Filter '*.json' -Recurse
|
||||
|
||||
$apps | ForEach-Object {
|
||||
$manifest = [System.IO.File]::ReadAllText($_.FullName) | ConvertFrom-Json -ErrorAction Continue
|
||||
$name = $_.BaseName
|
||||
|
||||
if ($name -match $query) {
|
||||
$list.Add([PSCustomObject]@{
|
||||
Name = $name
|
||||
Version = $manifest.Version
|
||||
Source = $bucket
|
||||
Binaries = ""
|
||||
})
|
||||
} else {
|
||||
$bin = bin_match $manifest $query
|
||||
if ($bin) {
|
||||
$list.Add([PSCustomObject]@{
|
||||
Name = $name
|
||||
Version = $manifest.Version
|
||||
Source = $bucket
|
||||
Binaries = $bin -join ' | '
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
$apps | ForEach-Object { $_.version = (Get-LatestVersion -AppName $_.name -Bucket $bucket); $_ }
|
||||
}
|
||||
|
||||
function download_json($url) {
|
||||
@@ -96,43 +162,35 @@ function search_remotes($query) {
|
||||
(add them using 'scoop bucket add <bucket name>')"
|
||||
}
|
||||
|
||||
$remote_list = @()
|
||||
$results | ForEach-Object {
|
||||
$name = $_.bucket
|
||||
$bucket = $_.bucket
|
||||
$_.results | ForEach-Object {
|
||||
$item = [ordered]@{}
|
||||
$item.Name = $_
|
||||
$item.Source = $name
|
||||
$list += [PSCustomObject]$item
|
||||
$item.Source = $bucket
|
||||
$remote_list += [PSCustomObject]$item
|
||||
}
|
||||
}
|
||||
|
||||
$list
|
||||
$remote_list
|
||||
}
|
||||
|
||||
$jsonTextAvailable = [System.AppDomain]::CurrentDomain.GetAssemblies() | Where-object { [System.IO.Path]::GetFileNameWithoutExtension($_.Location) -eq "System.Text.Json" }
|
||||
|
||||
Get-LocalBucket | ForEach-Object {
|
||||
$res = search_bucket $_ $query
|
||||
$local_results = $local_results -or $res
|
||||
if ($res) {
|
||||
$name = "$_"
|
||||
|
||||
$res | ForEach-Object {
|
||||
$item = [ordered]@{}
|
||||
$item.Name = $_.name
|
||||
$item.Version = $_.version
|
||||
$item.Source = $name
|
||||
$item.Binaries = ""
|
||||
if ($_.bin) { $item.Binaries = $_.bin -join ' | ' }
|
||||
$list += [PSCustomObject]$item
|
||||
}
|
||||
if ($jsonTextAvailable) {
|
||||
search_bucket $_ $query
|
||||
} else {
|
||||
search_bucket_legacy $_ $query
|
||||
}
|
||||
}
|
||||
|
||||
if ($list.Length -gt 0) {
|
||||
if ($list.Count -gt 0) {
|
||||
Write-Host "Results from local buckets..."
|
||||
$list
|
||||
}
|
||||
|
||||
if (!$local_results -and !(github_ratelimit_reached)) {
|
||||
if ($list.Count -eq 0 -and !(github_ratelimit_reached)) {
|
||||
$remote_results = search_remotes $query
|
||||
if (!$remote_results) {
|
||||
warn "No matches found."
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
@@ -9,6 +12,8 @@ $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"
|
||||
@@ -16,10 +21,10 @@ if (!(Get-FormatData ScoopStatus)) {
|
||||
|
||||
function Test-UpdateStatus($repopath) {
|
||||
if (Test-Path "$repopath\.git") {
|
||||
git_cmd -C "`"$repopath`"" fetch -q origin
|
||||
Invoke-Git -Path $repopath -ArgumentList @('fetch', '-q', 'origin')
|
||||
$script:network_failure = 128 -eq $LASTEXITCODE
|
||||
$branch = git -C $repopath branch --show-current
|
||||
$commits = git -C $repopath log "HEAD..origin/$branch" --oneline
|
||||
$branch = Invoke-Git -Path $repopath -ArgumentList @('branch', '--show-current')
|
||||
$commits = Invoke-Git -Path $repopath -ArgumentList @('log', "HEAD..origin/$branch", '--oneline')
|
||||
if ($commits) { return $true }
|
||||
else { return $false }
|
||||
} else {
|
||||
@@ -27,10 +32,13 @@ function Test-UpdateStatus($repopath) {
|
||||
}
|
||||
}
|
||||
|
||||
$needs_update = Test-UpdateStatus $currentdir
|
||||
foreach ($bucket in Get-LocalBucket) {
|
||||
if (Test-UpdateStatus (Find-BucketDirectory $bucket -Root)) {
|
||||
$bucket_needs_update = $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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +46,7 @@ if ($needs_update) {
|
||||
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) {
|
||||
} elseif (!$script:network_failure -and !$no_remotes) {
|
||||
success 'Scoop is up to date.'
|
||||
}
|
||||
|
||||
@@ -57,7 +65,7 @@ $true, $false | ForEach-Object { # local and global apps
|
||||
$item.'Installed Version' = $status.version
|
||||
$item.'Latest Version' = if ($status.outdated) { $status.latest_version } else { "" }
|
||||
$item.'Missing Dependencies' = $status.missing_deps -Split ' ' -Join ' | '
|
||||
$info = $()
|
||||
$info = @()
|
||||
if ($status.failed) { $info += 'Install failed' }
|
||||
if ($status.hold) { $info += 'Held package' }
|
||||
if ($status.removed) { $info += 'Manifest removed' }
|
||||
|
||||
@@ -24,7 +24,7 @@ if (!$apps) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($global -and !(is_admin)) {
|
||||
if ($global -and !(Test-IsAdmin)) {
|
||||
error 'You need admin rights to unhold a global app.'
|
||||
exit 1
|
||||
}
|
||||
@@ -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,15 +46,23 @@ $apps | ForEach-Object {
|
||||
return
|
||||
}
|
||||
|
||||
if (get_config NO_JUNCTIONS) {
|
||||
if (get_config NO_JUNCTION){
|
||||
$version = Select-CurrentVersion -App $app -Global:$global
|
||||
} else {
|
||||
$version = 'current'
|
||||
}
|
||||
$dir = versiondir $app $version $global
|
||||
$json = install_info $app $version $global
|
||||
if (!$json) {
|
||||
error "Failed to unhold '$app'"
|
||||
continue
|
||||
}
|
||||
$install = @{}
|
||||
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }
|
||||
if (!$install.hold) {
|
||||
info "'$app' is not held."
|
||||
continue
|
||||
}
|
||||
$install.hold = $null
|
||||
save_install_info $install $dir
|
||||
success "$app is no longer held and can be updated again."
|
||||
|
||||
@@ -30,7 +30,7 @@ if (!$apps) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($global -and !(is_admin)) {
|
||||
if ($global -and !(Test-IsAdmin)) {
|
||||
error 'You need admin rights to uninstall global apps.'
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -54,23 +54,29 @@ 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 Sync-Scoop {
|
||||
[CmdletBinding()]
|
||||
Param (
|
||||
[Switch]$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-GitAvailable)) { 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"
|
||||
$olddir = "$currentdir\..\old"
|
||||
|
||||
# get git scoop
|
||||
git_cmd clone -q $configRepo --branch $configBranch --single-branch "`"$newdir`""
|
||||
Invoke-Git -ArgumentList @('clone', '-q', $configRepo, '--branch', $configBranch, '--single-branch', $newdir)
|
||||
|
||||
# check if scoop was successful downloaded
|
||||
if (!(Test-Path "$newdir\bin\scoop.ps1")) {
|
||||
@@ -83,7 +89,7 @@ function update_scoop() {
|
||||
Rename-Item $newdir 'current' -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Warning $_
|
||||
abort "Scoop update failed. Folder in use. Paste $newdir into $currentdir."
|
||||
abort "Scoop update failed. Folder in use. Please rename folders $currentdir to ``old`` and $newdir to ``current``."
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -91,35 +97,46 @@ function update_scoop() {
|
||||
Remove-Item "$currentdir\..\old" -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
$previousCommit = git -C "$currentdir" rev-parse HEAD
|
||||
$currentRepo = git -C "$currentdir" config remote.origin.url
|
||||
$currentBranch = git -C "$currentdir" branch
|
||||
$previousCommit = Invoke-Git -Path $currentdir -ArgumentList @('rev-parse', 'HEAD')
|
||||
$currentRepo = Invoke-Git -Path $currentdir -ArgumentList @('config', 'remote.origin.url')
|
||||
$currentBranch = Invoke-Git -Path $currentdir -ArgumentList @('branch')
|
||||
|
||||
$isRepoChanged = !($currentRepo -match $configRepo)
|
||||
$isBranchChanged = !($currentBranch -match "\*\s+$configBranch")
|
||||
|
||||
# Stash uncommitted changes
|
||||
if (Invoke-Git -Path $currentdir -ArgumentList @('diff', 'HEAD', '--name-only')) {
|
||||
if (get_config AUTOSTASH_ON_CONFLICT) {
|
||||
warn "Uncommitted changes detected. Stashing..."
|
||||
Invoke-Git -Path $currentdir -ArgumentList @('stash', 'push', '-m', "WIP at $([System.DateTime]::Now.ToString('o'))", '-u', '-q')
|
||||
} else {
|
||||
warn "Uncommitted changes detected. Update aborted."
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
# Change remote url if the repo is changed
|
||||
if ($isRepoChanged) {
|
||||
git -C "$currentdir" config remote.origin.url "$configRepo"
|
||||
Invoke-Git -Path $currentdir -ArgumentList @('config', 'remote.origin.url', $configRepo)
|
||||
}
|
||||
|
||||
# Fetch and reset local repo if the repo or the branch is changed
|
||||
if ($isRepoChanged -or $isBranchChanged) {
|
||||
# Reset git fetch refs, so that it can fetch all branches (GH-3368)
|
||||
git -C "$currentdir" config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
|
||||
Invoke-Git -Path $currentdir -ArgumentList @('config', 'remote.origin.fetch', '+refs/heads/*:refs/remotes/origin/*')
|
||||
# fetch remote branch
|
||||
git_cmd -C "`"$currentdir`"" fetch --force origin "refs/heads/`"$configBranch`":refs/remotes/origin/$configBranch" -q
|
||||
Invoke-Git -Path $currentdir -ArgumentList @('fetch', '--force', 'origin', "refs/heads/$configBranch`:refs/remotes/origin/$configBranch", '-q')
|
||||
# checkout and track the branch
|
||||
git_cmd -C "`"$currentdir`"" checkout -B $configBranch -t origin/$configBranch -q
|
||||
Invoke-Git -Path $currentdir -ArgumentList @('checkout', '-B', $configBranch, '-t', "origin/$configBranch", '-q')
|
||||
# reset branch HEAD
|
||||
git -C "$currentdir" reset --hard origin/$configBranch -q
|
||||
Invoke-Git -Path $currentdir -ArgumentList @('reset', '--hard', "origin/$configBranch", '-q')
|
||||
} else {
|
||||
git_cmd -C "`"$currentdir`"" pull -q
|
||||
Invoke-Git -Path $currentdir -ArgumentList @('pull', '-q')
|
||||
}
|
||||
|
||||
$res = $lastexitcode
|
||||
if ($show_update_log) {
|
||||
git -C "$currentdir" --no-pager log --no-decorate --grep='^(chore)' --invert-grep --format='tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset' "$previousCommit..HEAD"
|
||||
if ($Log) {
|
||||
Invoke-GitLog -Path $currentdir -CommitHash $previousCommit
|
||||
}
|
||||
|
||||
if ($res -ne 0) {
|
||||
@@ -127,47 +144,65 @@ function update_scoop() {
|
||||
}
|
||||
}
|
||||
|
||||
# This should have been deprecated after 2019-05-12
|
||||
# if ((Get-LocalBucket) -notcontains 'main') {
|
||||
# info "The main bucket of Scoop has been separated to 'https://github.com/ScoopInstaller/Main'"
|
||||
# info "Adding main bucket..."
|
||||
# add_bucket 'main'
|
||||
# }
|
||||
|
||||
shim "$currentdir\bin\scoop.ps1" $false
|
||||
}
|
||||
|
||||
foreach ($bucket in Get-LocalBucket) {
|
||||
Write-Host "Updating '$bucket' bucket..."
|
||||
function Sync-Bucket {
|
||||
Param (
|
||||
[Switch]$Log
|
||||
)
|
||||
Write-Host "Updating Buckets..."
|
||||
|
||||
$bucketLoc = Find-BucketDirectory $bucket -Root
|
||||
|
||||
if (!(Test-Path (Join-Path $bucketLoc '.git'))) {
|
||||
if ($bucket -eq 'main') {
|
||||
# Make sure main bucket, which was downloaded as zip, will be properly "converted" into git
|
||||
Write-Host " Converting 'main' bucket to git repo..."
|
||||
$status = rm_bucket 'main'
|
||||
if ($status -ne 0) {
|
||||
abort "Failed to remove local 'main' bucket."
|
||||
}
|
||||
$status = add_bucket 'main' (known_bucket_repo 'main')
|
||||
if ($status -ne 0) {
|
||||
abort "Failed to add remote 'main' bucket."
|
||||
}
|
||||
} else {
|
||||
Write-Host "'$bucket' is not a git repository. Skipped."
|
||||
}
|
||||
continue
|
||||
if (!(Test-Path (Join-Path (Find-BucketDirectory 'main' -Root) '.git'))) {
|
||||
info "Converting 'main' bucket to git repo..."
|
||||
$status = rm_bucket 'main'
|
||||
if ($status -ne 0) {
|
||||
abort "Failed to remove local 'main' bucket."
|
||||
}
|
||||
|
||||
$previousCommit = git -C "$bucketLoc" rev-parse HEAD
|
||||
git_cmd -C "`"$bucketLoc`"" pull -q
|
||||
if ($show_update_log) {
|
||||
git -C "$bucketLoc" --no-pager log --no-decorate --grep='^(chore)' --invert-grep --format='tformat: * %C(yellow)%h%Creset %<|(72,trunc)%s %C(cyan)%cr%Creset' "$previousCommit..HEAD"
|
||||
$status = add_bucket 'main' (known_bucket_repo 'main')
|
||||
if ($status -ne 0) {
|
||||
abort "Failed to add remote 'main' bucket."
|
||||
}
|
||||
}
|
||||
|
||||
set_config lastupdate ([System.DateTime]::Now.ToString('o')) | Out-Null
|
||||
success 'Scoop was updated successfully!'
|
||||
|
||||
$buckets = Get-LocalBucket | ForEach-Object {
|
||||
$path = Find-BucketDirectory $_ -Root
|
||||
return @{
|
||||
name = $_
|
||||
valid = Test-Path (Join-Path $path '.git')
|
||||
path = $path
|
||||
}
|
||||
}
|
||||
|
||||
$buckets | Where-Object { !$_.valid } | ForEach-Object { Write-Host "'$($_.name)' is not a git repository. Skipped." }
|
||||
|
||||
if ($PSVersionTable.PSVersion.Major -ge 7) {
|
||||
# Parallel parameter is available since PowerShell 7
|
||||
$buckets | Where-Object { $_.valid } | ForEach-Object -ThrottleLimit 5 -Parallel {
|
||||
. "$using:PSScriptRoot\..\lib\core.ps1"
|
||||
|
||||
$bucketLoc = $_.path
|
||||
$name = $_.name
|
||||
|
||||
$previousCommit = Invoke-Git -Path $bucketLoc -ArgumentList @('rev-parse', 'HEAD')
|
||||
Invoke-Git -Path $bucketLoc -ArgumentList @('pull', '-q')
|
||||
if ($using:Log) {
|
||||
Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$buckets | Where-Object { $_.valid } | ForEach-Object {
|
||||
$bucketLoc = $_.path
|
||||
$name = $_.name
|
||||
|
||||
$previousCommit = Invoke-Git -Path $bucketLoc -ArgumentList @('rev-parse', 'HEAD')
|
||||
Invoke-Git -Path $bucketLoc -ArgumentList @('pull', '-q')
|
||||
if ($Log) {
|
||||
Invoke-GitLog -Path $bucketLoc -Name $name -CommitHash $previousCommit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function update($app, $global, $quiet = $false, $independent, $suggested, $use_cache = $true, $check_hash = $true) {
|
||||
@@ -176,7 +211,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 +222,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 +245,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 +278,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 +297,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 +311,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,9 +341,12 @@ if (-not ($apps -or $all)) {
|
||||
error 'scoop update: --no-cache is invalid when <app> is not specified.'
|
||||
exit 1
|
||||
}
|
||||
update_scoop
|
||||
Sync-Scoop -Log:$show_update_log
|
||||
Sync-Bucket -Log:$show_update_log
|
||||
set_config LAST_UPDATE ([System.DateTime]::Now.ToString('o')) | Out-Null
|
||||
success 'Scoop was updated successfully!'
|
||||
} else {
|
||||
if ($global -and !(is_admin)) {
|
||||
if ($global -and !(Test-IsAdmin)) {
|
||||
'ERROR: You need admin rights to update global apps.'; exit 1
|
||||
}
|
||||
|
||||
@@ -312,7 +356,10 @@ if (-not ($apps -or $all)) {
|
||||
$apps_param = $apps
|
||||
|
||||
if ($updateScoop) {
|
||||
update_scoop
|
||||
Sync-Scoop -Log:$show_update_log
|
||||
Sync-Bucket -Log:$show_update_log
|
||||
set_config LAST_UPDATE ([System.DateTime]::Now.ToString('o')) | Out-Null
|
||||
success 'Scoop was updated successfully!'
|
||||
}
|
||||
|
||||
if ($apps_param -eq '*' -or $all) {
|
||||
@@ -336,7 +383,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
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
|
||||
$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
|
||||
if (!$apps -and -$all) { my_usage; exit 1 }
|
||||
$architecture = Format-ArchitectureString
|
||||
|
||||
if (is_scoop_outdated) {
|
||||
if ($opt.u -or $opt.'no-update-scoop') {
|
||||
@@ -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" +
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
param($command)
|
||||
|
||||
if (!$command) {
|
||||
'ERROR: <command> missing'
|
||||
error '<command> missing'
|
||||
my_usage
|
||||
exit 1
|
||||
}
|
||||
@@ -12,7 +12,7 @@ if (!$command) {
|
||||
$path = Get-CommandPath $command
|
||||
|
||||
if ($null -eq $path) {
|
||||
Write-Host "'$command' not found / not a scoop shim."
|
||||
warn "'$command' not found, not a scoop shim, or a broken shim."
|
||||
exit 2
|
||||
} else {
|
||||
friendly_path $path
|
||||
|
||||
168
schema.json
168
schema.json
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
b624949df8b0e3a6153fdfb730a7c6f4990b6592ee0d922e1788433d276610f3 *Newtonsoft.Json.dll
|
||||
cff8fc4ce358d7daff84ab47129a776797a4ec819c1586a15bd5e63144f5b73f *Newtonsoft.Json.Schema.dll
|
||||
9abb57d73d82a2d77008321a85aff2b62e5ac68bebb54ece8668c96cc112e36b *Newtonsoft.Json.Schema.dll
|
||||
0318c8221ce4d44806f8def619bcc02886be0902aab80080e6251c50c6ca53a9 *Scoop.Validator.dll
|
||||
40a70bee96d108701f8f2e81392f9b79fd003f1cb4e1653ad2429753153fd7ee *validator.exe
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
2fdf035661f349206f58ea1feed8805b7f9517a21f9c113e7301c69de160f184c774350a12a710046e3ff6baa37345d319b6f47fd24fbba4e042d54014bee511 *Newtonsoft.Json.dll
|
||||
298d3d0b656acbb1fe5ed0c3abb49a640c47889184ab7bd4b594e51a7d7f829d5c8685edbd10a286fd56bfd8d601b9f187da463a5a9c8509365eddaea280642f *Newtonsoft.Json.Schema.dll
|
||||
855ab2e30c9d523c9f321ae861c5969244185f660fa47e05cec96df8e2970d19843dbd3d89a0fca845544641915d1adf4b4a2145ef568dd99da7791e5064d70e *Newtonsoft.Json.Schema.dll
|
||||
338793e6127330c0b05728291fcf18441127ffb56e1bd5c0f0588cd7436605f4b852f4bb622f655896a7eb7b1262add142b200fd5f37391b47d1401becb6b81c *Scoop.Validator.dll
|
||||
d497c27b48f44f4cff270d3c8801b0cecc74108f8786a4a7c40e57541308ae33a69f5456cfc43ae1ce4214038d20da9fbeac1bcf76cc58d972863b58dab18401 *validator.exe
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json.Schema" version="3.0.14" 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>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<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>
|
||||
<HintPath>packages\Newtonsoft.Json.Schema.3.0.15-beta2\lib\net45\Newtonsoft.Json.Schema.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
|
||||
@@ -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"
|
||||
@@ -1,127 +1,52 @@
|
||||
#Requires -Version 5.0
|
||||
#Requires -Modules @{ ModuleName = 'Pester'; RequiredVersion = '4.10.1' }
|
||||
#Requires -Version 5.1
|
||||
#Requires -Modules @{ ModuleName = 'BuildHelpers'; ModuleVersion = '2.0.1' }
|
||||
#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.2.0' }
|
||||
param(
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[String]
|
||||
$repo_dir = (Split-Path -Path $MyInvocation.PSCommandPath -Parent)
|
||||
[String] $BucketPath = $MyInvocation.PSScriptRoot
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\Scoop-TestLib.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\unix.ps1"
|
||||
. "$PSScriptRoot\Scoop-00File.Tests.ps1" -TestPath $BucketPath
|
||||
|
||||
$bucketdir = $repo_dir
|
||||
if (Test-Path("$repo_dir\..\bucket")) {
|
||||
$bucketdir = "$repo_dir\..\bucket"
|
||||
} elseif (Test-Path("$repo_dir\bucket")) {
|
||||
$bucketdir = "$repo_dir\bucket"
|
||||
}
|
||||
|
||||
# Tests for non manifest files
|
||||
$repo_files = @(Get-ChildItem -Path $repo_dir -File -Recurse)
|
||||
$project_file_exclusions = @(
|
||||
'[\\/]\.git[\\/]',
|
||||
'.sublime-workspace$',
|
||||
'.DS_Store$'
|
||||
)
|
||||
. "$PSScriptRoot\Import-File-Tests.ps1"
|
||||
|
||||
# Tests for manifest files
|
||||
Describe 'Manifest Validator' -Tag 'Validator' {
|
||||
BeforeAll {
|
||||
$schema = "$PSScriptRoot\..\schema.json"
|
||||
$working_dir = setup_working 'manifest'
|
||||
Add-Type -Path "$PSScriptRoot\..\supporting\validator\bin\Newtonsoft.Json.dll"
|
||||
Add-Type -Path "$PSScriptRoot\..\supporting\validator\bin\Newtonsoft.Json.Schema.dll"
|
||||
Add-Type -Path "$PSScriptRoot\..\supporting\validator\bin\Scoop.Validator.dll"
|
||||
}
|
||||
|
||||
It 'Scoop.Validator is available' {
|
||||
([System.Management.Automation.PSTypeName]'Scoop.Validator').Type | Should -Be 'Scoop.Validator'
|
||||
}
|
||||
|
||||
Context 'parse_json function' {
|
||||
It 'fails with invalid json' {
|
||||
{ parse_json "$working_dir\broken_wget.json" } | Should -Throw
|
||||
Describe 'Manifest validates against the schema' {
|
||||
BeforeDiscovery {
|
||||
$bucketDir = if (Test-Path "$BucketPath\bucket") {
|
||||
"$BucketPath\bucket"
|
||||
} else {
|
||||
$BucketPath
|
||||
}
|
||||
}
|
||||
|
||||
Context 'schema validation' {
|
||||
It 'fails with broken schema' {
|
||||
$validator = New-Object Scoop.Validator("$working_dir\broken_schema.json", $true)
|
||||
$validator.Validate("$working_dir\wget.json") | Should -BeFalse
|
||||
$validator.Errors.Count | Should -Be 1
|
||||
$validator.Errors | Select-Object -First 1 | Should -Match 'broken_schema.*(line 6).*(position 4)'
|
||||
}
|
||||
It 'fails with broken manifest' {
|
||||
$validator = New-Object Scoop.Validator($schema, $true)
|
||||
$validator.Validate("$working_dir\broken_wget.json") | Should -BeFalse
|
||||
$validator.Errors.Count | Should -Be 1
|
||||
$validator.Errors | Select-Object -First 1 | Should -Match 'broken_wget.*(line 5).*(position 4)'
|
||||
}
|
||||
It 'fails with invalid manifest' {
|
||||
$validator = New-Object Scoop.Validator($schema, $true)
|
||||
$validator.Validate("$working_dir\invalid_wget.json") | Should -BeFalse
|
||||
$validator.Errors.Count | Should -Be 16
|
||||
$validator.Errors | Select-Object -First 1 | Should -Match "Property 'randomproperty' has not been defined and the schema does not allow additional properties\."
|
||||
$validator.Errors | Select-Object -Last 1 | Should -Match 'Required properties are missing from object: version\.'
|
||||
}
|
||||
}
|
||||
}
|
||||
Describe 'manifest validates against the schema' -Tag 'Manifests' {
|
||||
BeforeAll {
|
||||
$schema = "$PSScriptRoot\..\schema.json"
|
||||
$changed_manifests = @()
|
||||
if ($env:CI -eq $true) {
|
||||
# AppVeyor
|
||||
$commit = if ($env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT) { $env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT } else { $env:APPVEYOR_REPO_COMMIT }
|
||||
|
||||
# GitHub Actions
|
||||
if ($env:GITHUB_SHA) {
|
||||
$commit = $env:GITHUB_SHA
|
||||
}
|
||||
$changed_manifests = (Get-GitChangedFile -Path $repo_dir -Include '*.json' -Commit $commit)
|
||||
Set-BuildEnvironment -Force
|
||||
$manifestFiles = @(Get-GitChangedFile -Path $bucketDir -Include '*.json' -Commit $env:BHCommitHash)
|
||||
} else {
|
||||
$manifestFiles = (Get-ChildItem $bucketDir -Filter '*.json' -Recurse).FullName
|
||||
}
|
||||
$manifest_files = Get-ChildItem $bucketdir *.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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
if ([String]::IsNullOrEmpty($MyInvocation.PSScriptRoot)) {
|
||||
Write-Error 'This script should not be called directly! It has to be imported from a buckets test file!'
|
||||
exit 1
|
||||
}
|
||||
|
||||
Describe 'Style constraints for non-binary project files' {
|
||||
|
||||
$files = @(
|
||||
# gather all files except '*.exe', '*.zip', or any .git repository files
|
||||
$repo_files |
|
||||
Where-Object { $_.fullname -inotmatch $($project_file_exclusions -join '|') } |
|
||||
Where-Object { $_.fullname -inotmatch '(.exe|.zip|.dll)$' } |
|
||||
Where-Object { $_.fullname -inotmatch '(unformatted)' }
|
||||
)
|
||||
|
||||
$files_exist = ($files.Count -gt 0)
|
||||
|
||||
It $('non-binary project files exist ({0} found)' -f $files.Count) -Skip:$(-not $files_exist) {
|
||||
if (-not ($files.Count -gt 0)) {
|
||||
throw 'No non-binary project were found'
|
||||
}
|
||||
}
|
||||
|
||||
It 'files do not contain leading UTF-8 BOM' -Skip:$(-not $files_exist) {
|
||||
# UTF-8 BOM == 0xEF 0xBB 0xBF
|
||||
# see http://www.powershellmagazine.com/2012/12/17/pscxtip-how-to-determine-the-byte-order-mark-of-a-text-file @@ https://archive.is/RgT42
|
||||
# ref: http://poshcode.org/2153 @@ https://archive.is/sGnnu
|
||||
$badFiles = @(
|
||||
foreach ($file in $files) {
|
||||
if ((Get-Command Get-Content).parameters.ContainsKey('AsByteStream')) {
|
||||
# PowerShell Core (6.0+) '-Encoding byte' is replaced by '-AsByteStream'
|
||||
$content = ([char[]](Get-Content $file.FullName -AsByteStream -TotalCount 3) -join '')
|
||||
} else {
|
||||
$content = ([char[]](Get-Content $file.FullName -Encoding byte -TotalCount 3) -join '')
|
||||
}
|
||||
if ([regex]::match($content, '(?ms)^\xEF\xBB\xBF').success) {
|
||||
$file.FullName
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if ($badFiles.Count -gt 0) {
|
||||
throw "The following files have utf-8 BOM: `r`n`r`n$($badFiles -join "`r`n")"
|
||||
}
|
||||
}
|
||||
|
||||
It 'files end with a newline' -Skip:$(-not $files_exist) {
|
||||
$badFiles = @(
|
||||
foreach ($file in $files) {
|
||||
# Ignore previous TestResults.xml
|
||||
if ($file -match 'TestResults.xml') {
|
||||
continue
|
||||
}
|
||||
$string = [System.IO.File]::ReadAllText($file.FullName)
|
||||
if ($string.Length -gt 0 -and $string[-1] -ne "`n") {
|
||||
$file.FullName
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if ($badFiles.Count -gt 0) {
|
||||
throw "The following files do not end with a newline: `r`n`r`n$($badFiles -join "`r`n")"
|
||||
}
|
||||
}
|
||||
|
||||
It 'file newlines are CRLF' -Skip:$(-not $files_exist) {
|
||||
$badFiles = @(
|
||||
foreach ($file in $files) {
|
||||
$content = 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
189
test/Scoop-00File.Tests.ps1
Normal 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")"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
33
test/Scoop-00Linting.Tests.ps1
Normal file
33
test/Scoop-00Linting.Tests.ps1
Normal 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)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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' {
|
||||
|
||||
@@ -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'"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
. "$PSScriptRoot\Scoop-TestLib.ps1"
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\decompress.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\unix.ps1"
|
||||
BeforeAll {
|
||||
. "$PSScriptRoot\Scoop-TestLib.ps1"
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\decompress.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
}
|
||||
|
||||
$isUnix = is_unix
|
||||
|
||||
Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
|
||||
Describe 'Decompression function' -Tag 'Scoop', 'Windows', 'Decompress' {
|
||||
|
||||
BeforeAll {
|
||||
$working_dir = setup_working 'decompress'
|
||||
@@ -19,13 +18,17 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
|
||||
return $to
|
||||
}
|
||||
|
||||
It 'Decompression test cases should exist' {
|
||||
}
|
||||
Context 'Decompression test cases should exist' {
|
||||
BeforeAll {
|
||||
$testcases = "$working_dir\TestCases.zip"
|
||||
}
|
||||
It 'Test cases should exist and hash should match' {
|
||||
$testcases | Should -Exist
|
||||
compute_hash $testcases 'sha256' | Should -Be '791bfce192917a2ff225dcdd87d23ae5f720b20178d85e68e4b1b56139cf8e6a'
|
||||
if (!$isUnix) {
|
||||
Microsoft.PowerShell.Archive\Expand-Archive $testcases $working_dir
|
||||
}
|
||||
(Get-FileHash -Path $testcases -Algorithm SHA256).Hash.ToLower() | Should -Be '791bfce192917a2ff225dcdd87d23ae5f720b20178d85e68e4b1b56139cf8e6a'
|
||||
}
|
||||
It 'Test cases should be extracted correctly' {
|
||||
{ Microsoft.PowerShell.Archive\Expand-Archive -Path $testcases -DestinationPath $working_dir } | Should -Not -Throw
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,14 +52,14 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
|
||||
$test6_3 = "$working_dir\7ZipTest6.part03.rar"
|
||||
}
|
||||
|
||||
It 'extract normal compressed file' -Skip:$isUnix {
|
||||
It 'extract normal compressed file' {
|
||||
$to = test_extract 'Expand-7zipArchive' $test1
|
||||
$to | Should -Exist
|
||||
"$to\empty" | Should -Exist
|
||||
(Get-ChildItem $to).Count | Should -Be 1
|
||||
}
|
||||
|
||||
It 'extract nested compressed file' -Skip:$isUnix {
|
||||
It 'extract nested compressed file' {
|
||||
# file ext: tgz
|
||||
$to = test_extract 'Expand-7zipArchive' $test2
|
||||
$to | Should -Exist
|
||||
@@ -70,28 +73,28 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
|
||||
(Get-ChildItem $to).Count | Should -Be 1
|
||||
}
|
||||
|
||||
It 'extract nested compressed file with different inner name' -Skip:$isUnix {
|
||||
It 'extract nested compressed file with different inner name' {
|
||||
$to = test_extract 'Expand-7zipArchive' $test4
|
||||
$to | Should -Exist
|
||||
"$to\empty" | Should -Exist
|
||||
(Get-ChildItem $to).Count | Should -Be 1
|
||||
}
|
||||
|
||||
It 'extract splited 7z archives (.001, .002, ...)' -Skip:$isUnix {
|
||||
It 'extract splited 7z archives (.001, .002, ...)' {
|
||||
$to = test_extract 'Expand-7zipArchive' $test5_1
|
||||
$to | Should -Exist
|
||||
"$to\empty" | Should -Exist
|
||||
(Get-ChildItem $to).Count | Should -Be 1
|
||||
}
|
||||
|
||||
It 'extract splited RAR archives (.part01.rar, .part02.rar, ...)' -Skip:$isUnix {
|
||||
It 'extract splited RAR archives (.part01.rar, .part02.rar, ...)' {
|
||||
$to = test_extract 'Expand-7zipArchive' $test6_1
|
||||
$to | Should -Exist
|
||||
"$to\dummy" | Should -Exist
|
||||
(Get-ChildItem $to).Count | Should -Be 1
|
||||
}
|
||||
|
||||
It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix {
|
||||
It 'works with "-Removal" switch ($removal param)' {
|
||||
$test1 | Should -Exist
|
||||
test_extract 'Expand-7zipArchive' $test1 $true
|
||||
$test1 | Should -Not -Exist
|
||||
@@ -126,21 +129,21 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
|
||||
$test2 = "$working_dir\ZstdTest.tar.zst"
|
||||
}
|
||||
|
||||
It 'extract normal compressed file' -Skip:$isUnix {
|
||||
It 'extract normal compressed file' {
|
||||
$to = test_extract 'Expand-ZstdArchive' $test1
|
||||
$to | Should -Exist
|
||||
"$to\ZstdTest" | Should -Exist
|
||||
(Get-ChildItem $to).Count | Should -Be 1
|
||||
}
|
||||
|
||||
It 'extract nested compressed file' -Skip:$isUnix {
|
||||
It 'extract nested compressed file' {
|
||||
$to = test_extract 'Expand-ZstdArchive' $test2
|
||||
$to | Should -Exist
|
||||
"$to\ZstdTest" | Should -Exist
|
||||
(Get-ChildItem $to).Count | Should -Be 1
|
||||
}
|
||||
|
||||
It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix {
|
||||
It 'works with "-Removal" switch ($removal param)' {
|
||||
$test1 | Should -Exist
|
||||
test_extract 'Expand-ZstdArchive' $test1 $true
|
||||
$test1 | Should -Not -Exist
|
||||
@@ -159,7 +162,7 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
|
||||
$test2 = "$working_dir\MSITestNull.msi"
|
||||
}
|
||||
|
||||
It 'extract normal MSI file' -Skip:$isUnix {
|
||||
It 'extract normal MSI file' {
|
||||
Mock get_config { $false }
|
||||
$to = test_extract 'Expand-MsiArchive' $test1
|
||||
$to | Should -Exist
|
||||
@@ -167,13 +170,13 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
|
||||
(Get-ChildItem "$to\MSITest").Count | Should -Be 1
|
||||
}
|
||||
|
||||
It 'extract empty MSI file using lessmsi' -Skip:$isUnix {
|
||||
It 'extract empty MSI file using lessmsi' {
|
||||
Mock get_config { $true }
|
||||
$to = test_extract 'Expand-MsiArchive' $test2
|
||||
$to | Should -Exist
|
||||
}
|
||||
|
||||
It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix {
|
||||
It 'works with "-Removal" switch ($removal param)' {
|
||||
Mock get_config { $false }
|
||||
$test1 | Should -Exist
|
||||
test_extract 'Expand-MsiArchive' $test1 $true
|
||||
@@ -192,14 +195,14 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
|
||||
$test = "$working_dir\InnoTest.exe"
|
||||
}
|
||||
|
||||
It 'extract Inno Setup file' -Skip:$isUnix {
|
||||
It 'extract Inno Setup file' {
|
||||
$to = test_extract 'Expand-InnoArchive' $test
|
||||
$to | Should -Exist
|
||||
"$to\empty" | Should -Exist
|
||||
(Get-ChildItem $to).Count | Should -Be 1
|
||||
}
|
||||
|
||||
It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix {
|
||||
It 'works with "-Removal" switch ($removal param)' {
|
||||
$test | Should -Exist
|
||||
test_extract 'Expand-InnoArchive' $test $true
|
||||
$test | Should -Not -Exist
|
||||
@@ -212,14 +215,14 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
|
||||
$test = "$working_dir\ZipTest.zip"
|
||||
}
|
||||
|
||||
It 'extract compressed file' -Skip:$isUnix {
|
||||
It 'extract compressed file' {
|
||||
$to = test_extract 'Expand-ZipArchive' $test
|
||||
$to | Should -Exist
|
||||
"$to\empty" | Should -Exist
|
||||
(Get-ChildItem $to).Count | Should -Be 1
|
||||
}
|
||||
|
||||
It 'works with "-Removal" switch ($removal param)' -Skip:$isUnix {
|
||||
It 'works with "-Removal" switch ($removal param)' {
|
||||
$test | Should -Exist
|
||||
test_extract 'Expand-ZipArchive' $test $true
|
||||
$test | Should -Not -Exist
|
||||
|
||||
@@ -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' {
|
||||
@@ -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' }
|
||||
|
||||
@@ -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\formatted" -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\unformatted\$_") | ConvertToPrettyJson
|
||||
$correct = (Get-Content "$format\formatted\$_") -join "`r`n"
|
||||
$correct.CompareTo($pretty_json) | Should -Be 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
. "$PSScriptRoot\Scoop-TestLib.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
BeforeAll {
|
||||
. "$PSScriptRoot\Scoop-TestLib.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
}
|
||||
|
||||
Describe 'getopt' -Tag 'Scoop' {
|
||||
It 'handle short option with required argument missing' {
|
||||
@@ -15,6 +17,12 @@ Describe 'getopt' -Tag 'Scoop' {
|
||||
$err | Should -Be 'Option --arb requires an argument.'
|
||||
}
|
||||
|
||||
It 'handle space in quote' {
|
||||
$opt, $rem, $err = getopt '-x', 'space arg' 'x:' ''
|
||||
$err | Should -BeNullOrEmpty
|
||||
$opt.x | Should -Be 'space arg'
|
||||
}
|
||||
|
||||
It 'handle unrecognized short option' {
|
||||
$null, $null, $err = getopt '-az' 'a' ''
|
||||
$err | Should -Be 'Option -z not recognized.'
|
||||
@@ -68,4 +76,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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
86
test/Scoop-Manifest.Tests.ps1
Normal file
86
test/Scoop-Manifest.Tests.ps1
Normal 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\.'
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user