mirror of
https://github.com/ScoopInstaller/Scoop.git
synced 2025-11-14 05:26:11 +00:00
Compare commits
152 Commits
v0.1.0
...
rasa/sandb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2745b0f42b | ||
|
|
212dc3d2ee | ||
|
|
39da19bcfc | ||
|
|
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 | ||
|
|
76c3bcd70c | ||
|
|
794f8a51aa | ||
|
|
53f0e9a18c | ||
|
|
1b5ee6f090 | ||
|
|
c4d1b9c22f | ||
|
|
9811a5f853 | ||
|
|
6629331799 | ||
|
|
4fec4d7089 | ||
|
|
83d0fef02f | ||
|
|
0fd6657572 | ||
|
|
9723725402 | ||
|
|
847756bd1e | ||
|
|
86e3efbaf0 | ||
|
|
93db5f47f1 | ||
|
|
5987e499b9 | ||
|
|
666d474ee1 | ||
|
|
e7c0faa29a | ||
|
|
9e6c758c1f | ||
|
|
0b38c91f1a | ||
|
|
6e25e440af | ||
|
|
d63b7d6f01 | ||
|
|
ecb8f02d4e | ||
|
|
7e6be8f3f5 | ||
|
|
574bea4975 | ||
|
|
9e70dcad79 | ||
|
|
64364b40b4 | ||
|
|
387835753d | ||
|
|
bfb5c8d04a | ||
|
|
3a1186ea1b | ||
|
|
ccd067b2b1 | ||
|
|
78c1bc45b4 | ||
|
|
dd0f51426b | ||
|
|
d6c6ddcbb3 | ||
|
|
2e52888b63 | ||
|
|
0f6d012d26 | ||
|
|
896ea6cdbd | ||
|
|
d056d542db | ||
|
|
ad04dc9e6f | ||
|
|
8140a2052c | ||
|
|
ac2fb38722 | ||
|
|
47d7f76f7c | ||
|
|
bb5392b486 | ||
|
|
f49f976618 | ||
|
|
5d58703484 | ||
|
|
b130e606cf | ||
|
|
c6fc2de306 | ||
|
|
a2600b1203 | ||
|
|
aaa726c09f | ||
|
|
b93d0b4157 | ||
|
|
0b6de90c03 | ||
|
|
b96abcfda9 | ||
|
|
cb7cd99e7a | ||
|
|
22365c2169 | ||
|
|
e6d03715fa | ||
|
|
6296822f1f | ||
|
|
7ee74a0638 | ||
|
|
f947b620d5 | ||
|
|
47c0f46d58 | ||
|
|
f6679c2170 | ||
|
|
22c7d58e33 | ||
|
|
55b26da657 | ||
|
|
f441968983 | ||
|
|
72fd0c5f83 | ||
|
|
d29e336417 | ||
|
|
32de4c5714 | ||
|
|
ced36b285d | ||
|
|
ad3fc4f8fb | ||
|
|
45db5fb325 | ||
|
|
5e4e6e9e5d | ||
|
|
53cdf68e26 | ||
|
|
4d36cbd90d | ||
|
|
635ae1715a | ||
|
|
5a795caca5 | ||
|
|
5025661fa2 | ||
|
|
476b507bb6 | ||
|
|
a66a086fb0 | ||
|
|
60d308f7d6 | ||
|
|
af26d86d02 |
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)"`]
|
||||
|
||||
|
||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -29,5 +29,7 @@ Relates to #XXXX
|
||||
<!-- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||
<!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||
- [ ] I have read the [Contributing Guide](https://github.com/ScoopInstaller/.github/blob/main/.github/CONTRIBUTING.md).
|
||||
- [ ] I have ensured that I am targeting the `develop` branch.
|
||||
- [ ] I have updated the documentation accordingly.
|
||||
- [ ] I have updated the tests accordingly.
|
||||
- [ ] I have added an entry in the CHANGELOG.
|
||||
|
||||
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,
|
||||
|
||||
239
CHANGELOG.md
239
CHANGELOG.md
@@ -1,3 +1,242 @@
|
||||
## [Unreleased](https://github.com/ScoopInstaller/Scoop/compare/master...develop)
|
||||
|
||||
### Features
|
||||
|
||||
- **scoop-update:** Add support for parallel syncing buckets in PowerShell 7 and improve output ([#5122](https://github.com/ScoopInstaller/Scoop/issues/5122))
|
||||
- **testing:** Add ability to test manifests inside a Windows sandbox ([#5349](https://github.com/(https://github.com/ScoopInstaller/Scoop/pulls/5349))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **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))
|
||||
- **shortcuts:** Output correctly formatted path ([#5333](https://github.com/ScoopInstaller/Scoop/issues/5333))
|
||||
|
||||
### 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))
|
||||
- **scoop-download:** Output more detailed manifest information ([#5277](https://github.com/ScoopInstaller/Scoop/issues/5277))
|
||||
|
||||
### Tests
|
||||
|
||||
- **bucket:** Skip manifest validation if no manifest changes ([#5270](https://github.com/ScoopInstaller/Scoop/issues/5270))
|
||||
|
||||
## [v0.3.1](https://github.com/ScoopInstaller/Scoop/compare/v0.3.0...v0.3.1) - 2022-11-15
|
||||
|
||||
### Features
|
||||
|
||||
- **config:** Allow Scoop to check if apps versioned as 'nightly' are outdated ([#5166](https://github.com/ScoopInstaller/Scoop/issues/5166))
|
||||
- **checkup:** Add Windows Developer Mode check ([#5233](https://github.com/ScoopInstaller/Scoop/issues/5233))
|
||||
- **bucket:** Add 'sysinternals' bucket to known ([#5237](https://github.com/ScoopInstaller/Scoop/issues/5237))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **decompress:** Use PS's default 'Expand-Archive()' ([#5185](https://github.com/ScoopInstaller/Scoop/issues/5185))
|
||||
- **hash:** Fix SourceForge's hash extraction ([#5189](https://github.com/ScoopInstaller/Scoop/issues/5189))
|
||||
- **decompress:** Trim ending '/' ([#5195](https://github.com/ScoopInstaller/Scoop/issues/5195))
|
||||
- **shim:** Exit if shim creating failed 'cause no git ([#5225](https://github.com/ScoopInstaller/Scoop/issues/5225))
|
||||
- **scoop-import:** Add correct architecture argument ([#5210](https://github.com/ScoopInstaller/Scoop/issues/5210))
|
||||
- **scoop-config:** Output `[DateTime]` as `[String]` ([#5232](https://github.com/ScoopInstaller/Scoop/issues/5232))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
- **hash:** Use `Get-FileHash()` directly ([#5177](https://github.com/ScoopInstaller/Scoop/issues/5177))
|
||||
- **installer:** Drop the old installer ([#5186](https://github.com/ScoopInstaller/Scoop/issues/5186))
|
||||
- **unix:** Remove `unix.ps1` ([#5235](https://github.com/ScoopInstaller/Scoop/issues/5235))
|
||||
|
||||
### Builds
|
||||
|
||||
- **auto-pr:** Add `CommitMessageFormat` option ([#5171](https://github.com/ScoopInstaller/Scoop/issues/5171))
|
||||
- **checkver:** Support XML default namespace ([#5191](https://github.com/ScoopInstaller/Scoop/issues/5191))
|
||||
- **pssa:** Remove unused 'ExcludeRules' ([#5201](https://github.com/ScoopInstaller/Scoop/issues/5201))
|
||||
- **schema:** Add 'installer' and 'shortcuts' to 'autoupdate' ([#5220](https://github.com/ScoopInstaller/Scoop/issues/5220))
|
||||
- **checkhashes:** Use correct version number if `UseCache` ([#5240](https://github.com/ScoopInstaller/Scoop/issues/5240))
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
- **module:** Update modules version ([#5209](https://github.com/ScoopInstaller/Scoop/issues/5209))
|
||||
|
||||
### Tests
|
||||
|
||||
- **unix:** Fix tests in Linux and macOS ([#5179](https://github.com/ScoopInstaller/Scoop/issues/5179))
|
||||
- **pester:** Update to Pester 5 ([#5222](https://github.com/ScoopInstaller/Scoop/issues/5222))
|
||||
- **bucket:** Use BuildHelpers' EnvVars ([#5226](https://github.com/ScoopInstaller/Scoop/issues/5226))
|
||||
|
||||
### Documentation
|
||||
|
||||
- **scoop-cat:** Fix help message([#5224](https://github.com/ScoopInstaller/Scoop/issues/5224))
|
||||
|
||||
## [v0.3.0](https://github.com/ScoopInstaller/Scoop/compare/v0.2.4...v0.3.0) - 2022-10-10
|
||||
|
||||
### Features
|
||||
|
||||
- **install:** Add support for ARM64 architecture ([#5154](https://github.com/ScoopInstaller/Scoop/issues/5154))
|
||||
- **install:** Show the running process ([#5102](https://github.com/ScoopInstaller/Scoop/issues/5102))
|
||||
- **getopt:** Support option terminator (`--`) ([#5121](https://github.com/ScoopInstaller/Scoop/issues/5121))
|
||||
- **subdir:** Allow subdir in 'bucket' ([#5119](https://github.com/ScoopInstaller/Scoop/issues/5119))
|
||||
- **scoop-config:** Allow 'hold_update_until' be set manually ([#5100](https://github.com/ScoopInstaller/Scoop/issues/5100))
|
||||
- **scoop-(un)hold:** Support `scoop (un)hold scoop` ([#5089](https://github.com/ScoopInstaller/Scoop/issues/5089))
|
||||
- **scoop-update:** Stash uncommitted changes before update ([#5091](https://github.com/ScoopInstaller/Scoop/issues/5091))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **config:** Change config option to snake_case in file and SCREAMING_CASE in code ([#5116](https://github.com/ScoopInstaller/Scoop/issues/5116))
|
||||
- **jsonpath:** Prevent converting date string to DateTime in JSONPath ([#5130](https://github.com/ScoopInstaller/Scoop/issues/5130))
|
||||
- **psmodule:** Remove folder recursively when unlinking previous module path ([#5127](https://github.com/ScoopInstaller/Scoop/issues/5127))
|
||||
- **scoop-update:** Add `uninstall_psmodule` to update process ([#5136](https://github.com/ScoopInstaller/Scoop/issues/5136))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
- **download:** Rename `dl()` to `Invoke-Download()` ([#5143](https://github.com/ScoopInstaller/Scoop/issues/5143))
|
||||
- **path:** Use 'Convert-Path()' instead of 'Resolve-Path()' ([#5109](https://github.com/ScoopInstaller/Scoop/issues/5109))
|
||||
- **scoop-shim:** Use `getopt` to parse arguments ([#5125](https://github.com/ScoopInstaller/Scoop/issues/5125))
|
||||
|
||||
### Builds
|
||||
|
||||
- **checkver:** Implement SourceForge checkver functionality ([#5113](https://github.com/ScoopInstaller/Scoop/issues/5113), [#5163](https://github.com/ScoopInstaller/Scoop/issues/5163))
|
||||
- **checkurls:** Allow checking URLs from private_hosts ([#5152](https://github.com/ScoopInstaller/Scoop/issues/5152))
|
||||
- **schema:** Set manifest schema to be stricter ([#5093](https://github.com/ScoopInstaller/Scoop/issues/5093))
|
||||
- **vscode:** Tweak VSCode setting ([#5149](https://github.com/ScoopInstaller/Scoop/issues/5149))
|
||||
|
||||
## [v0.2.4](https://github.com/ScoopInstaller/Scoop/compare/v0.2.3...v0.2.4) - 2022-08-08
|
||||
|
||||
### Features
|
||||
|
||||
- **core:** Create no window by default in `Invoke-ExternalCommand` ([#5066](https://github.com/ScoopInstaller/Scoop/issues/5066))
|
||||
- **core:** Improve argument concatenation in `Invoke-ExternalCommand` ([#5065](https://github.com/ScoopInstaller/Scoop/issues/5065))
|
||||
- **install:** Show bucket name while installing an app ([#5075](https://github.com/ScoopInstaller/Scoop/issues/5075))
|
||||
- **scoop-status:** Add flag to disable remote checking ([#5073](https://github.com/ScoopInstaller/Scoop/issues/5073))
|
||||
- **scoop-update:** Add support for `pre_uninstall` and `post_uninstall` ([#5085](https://github.com/ScoopInstaller/Scoop/issues/5085))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **core:** Avoid deadlock in `Invoke-ExternalCommand` ([#5064](https://github.com/ScoopInstaller/Scoop/issues/5064))
|
||||
- **core:** Use 'System.Nullable<bool>' for param 'global' ([#5088](https://github.com/ScoopInstaller/Scoop/issues/5088))
|
||||
- **install:** Move from cache when `--no-cache` is specified ([#5039](https://github.com/ScoopInstaller/Scoop/issues/5039))
|
||||
- **scoop-status:** Correct formatting of `Info` output ([#5047](https://github.com/ScoopInstaller/Scoop/issues/5047))
|
||||
|
||||
### Builds
|
||||
|
||||
- **checkver:** Load page content before running 'script' ([#5080](https://github.com/ScoopInstaller/Scoop/issues/5080))
|
||||
- **json:** Update Newtonsoft.Json.Schema to 3.0.15-beta2 ([#5053](https://github.com/ScoopInstaller/Scoop/issues/5053))
|
||||
|
||||
## [v0.2.3](https://github.com/ScoopInstaller/Scoop/compare/v0.2.2...v0.2.3) - 2022-07-07
|
||||
|
||||
### Features
|
||||
|
||||
- **chore:** Add missing -a/--all param to all commands ([#5004](https://github.com/ScoopInstaller/Scoop/issues/5004))
|
||||
- **scoop-status:** Check bucket status, improve output ([#5011](https://github.com/ScoopInstaller/Scoop/issues/5011))
|
||||
- **scoop-info:** Show app installed/download size ([#4886](https://github.com/ScoopInstaller/Scoop/issues/4886))
|
||||
- **scoop-import:** Import a Scoop installation from JSON ([#5014](https://github.com/ScoopInstaller/Scoop/issues/5014), [#5034](https://github.com/ScoopInstaller/Scoop/issues/5034))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **chore:** Update help documentation ([#5002](https://github.com/ScoopInstaller/Scoop/issues/5002), [#5029](https://github.com/ScoopInstaller/Scoop/issues/5029))
|
||||
- **decompress:** Handle split RAR archives ([#4994](https://github.com/ScoopInstaller/Scoop/issues/4994))
|
||||
- **shortcuts:** Fix network drive shortcut creation ([#4410](https://github.com/ScoopInstaller/Scoop/issues/4410), [#5006](https://github.com/ScoopInstaller/Scoop/issues/5006))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
- **scoop-search:** Output PSObject, use API token ([#4997](https://github.com/ScoopInstaller/Scoop/issues/4997))
|
||||
|
||||
### Builds
|
||||
|
||||
- **checkver,auto-pr:** Allow passing file path ([#5019](https://github.com/ScoopInstaller/Scoop/issues/5019))
|
||||
- **checkver:** Exit routine earlier if error ([#5025](https://github.com/ScoopInstaller/Scoop/issues/5025))
|
||||
- **json:** Update Newton.Json to 13.0.1 ([#5026](https://github.com/ScoopInstaller/Scoop/issues/5026))
|
||||
|
||||
### Tests
|
||||
|
||||
- **typo:** Fix typo ('formated' -> 'formatted') ([#4217](https://github.com/ScoopInstaller/Scoop/issues/4217))
|
||||
|
||||
## [v0.2.2](https://github.com/ScoopInstaller/Scoop/compare/v0.2.1...v0.2.2) - 2022-06-21
|
||||
|
||||
### Features
|
||||
|
||||
- **core:** Add `Get-Encoding` function to fix missing webclient encoding ([#4956](https://github.com/ScoopInstaller/Scoop/issues/4956))
|
||||
- **scoop-(un)hold:** Add `-g`/`--global` flag ([#4991](https://github.com/ScoopInstaller/Scoop/issues/4991))
|
||||
- **scoop-update:** Support `scoop update scoop` ([#4992](https://github.com/ScoopInstaller/Scoop/issues/4992))
|
||||
- **scoop-virustotal:** Migrate to VirusTotal API v3 ([#4983](https://github.com/ScoopInstaller/Scoop/issues/4983))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **manifest:** Fix bugs in 'Get-Manifest()' ([#4986](https://github.com/ScoopInstaller/Scoop/issues/4986))
|
||||
|
||||
## [v0.2.1](https://github.com/ScoopInstaller/Scoop/compare/v0.2.0...v0.2.1) - 2022-06-10
|
||||
|
||||
### Features
|
||||
|
||||
- **core:** Add pre_uninstall and post_uninstall hooks ([#4957](https://github.com/ScoopInstaller/Scoop/issues/4957), [#4962](https://github.com/ScoopInstaller/Scoop/issues/4962))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **bucket:** Make sure `list_buckets` return array ([#4979](https://github.com/ScoopInstaller/Scoop/issues/4979))
|
||||
- **chore:** Deprecate tls1 and tls1.1 ([#4950](https://github.com/ScoopInstaller/Scoop/issues/4950))
|
||||
- **chore:** Update Nonportable bucket URL ([#4955](https://github.com/ScoopInstaller/Scoop/issues/4955))
|
||||
- **core:** Using `Invoke-Command` instead of `Invoke-Expression` ([#4941](https://github.com/ScoopInstaller/Scoop/issues/4941))
|
||||
- **core:** Load config file before initialization ([#4932](https://github.com/ScoopInstaller/Scoop/issues/4932))
|
||||
- **core:** Allow to use '_' and '.' in bucket name ([#4952](https://github.com/ScoopInstaller/Scoop/issues/4952))
|
||||
- **depends:** Avoid digits in archive file extension (except for .7z and .001) ([#4915](https://github.com/ScoopInstaller/Scoop/issues/4915))
|
||||
- **bucket:** Don't check remote URL of non-git buckets ([#4923](https://github.com/ScoopInstaller/Scoop/issues/4923))
|
||||
- **bucket:** Don't write message OK before bucket is cloned ([#4925](https://github.com/ScoopInstaller/Scoop/issues/4925))
|
||||
- **shim:** Add 'Get-CommandPath()' to find git ([#4913](https://github.com/ScoopInstaller/Scoop/issues/4913))
|
||||
- **shim:** Remove character replacement in .cmd -> .ps1 shims ([#4914](https://github.com/ScoopInstaller/Scoop/issues/4914))
|
||||
- **scoop:** Pass CLI arguments as string objects ([#4931](https://github.com/ScoopInstaller/Scoop/issues/4931))
|
||||
- **scoop-info:** Fix error message when manifest is not found ([#4935](https://github.com/ScoopInstaller/Scoop/issues/4935))
|
||||
- **scoop-search:** Require files in 'bucket' dir for remote known buckets ([#4944](https://github.com/ScoopInstaller/Scoop/issues/4944))
|
||||
- **update:** Prevent uninstall when update ([#4949](https://github.com/ScoopInstaller/Scoop/issues/4949))
|
||||
- **scoop-download:** Use correct Args when calling `Get-Manifest` ([#4970](https://github.com/ScoopInstaller/Scoop/issues/4970))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
- **manifest:** Rename 'Find-Manifest()' to 'Get-Manifest() ([#4966](https://github.com/ScoopInstaller/Scoop/issues/4966), [#4981](https://github.com/ScoopInstaller/Scoop/issues/4981))
|
||||
|
||||
### Documentation
|
||||
|
||||
- **readme:** Update license badge ([#4929](https://github.com/ScoopInstaller/Scoop/issues/4929))
|
||||
|
||||
## [v0.2.0](https://github.com/ScoopInstaller/Scoop/compare/v0.1.0...v0.2.0) - 2022-05-10
|
||||
|
||||
### Features
|
||||
|
||||
- **relicense:** Relicense to dual-license (Unlicense or MIT) ([#4903](https://github.com/ScoopInstaller/Scoop/issues/4903), [#4870](https://github.com/ScoopInstaller/Scoop/issues/4870))
|
||||
- **install:** Allow downloading from private repositories ([#4254](https://github.com/ScoopInstaller/Scoop/issues/4254))
|
||||
- **scoop-cleanup:** Add `-a/--all` switch to cleanup all apps ([#4906](https://github.com/ScoopInstaller/Scoop/issues/4906))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **bucket:** Return empty list correctly in `Get-LocalBucket` ([#4885](https://github.com/ScoopInstaller/Scoop/issues/4885))
|
||||
- **install:** Fix issue with installation inside containers ([#4837](https://github.com/ScoopInstaller/Scoop/issues/4837))
|
||||
- **installed:** If no `$global`, check both local and global installed ([#4798](https://github.com/ScoopInstaller/Scoop/issues/4798))
|
||||
- **shim:** Manipulating shims with UTF8 encoding ([#4791](https://github.com/ScoopInstaller/Scoop/issues/4791), [#4813](https://github.com/ScoopInstaller/Scoop/issues/4813))
|
||||
- **shim:** Correctly quote $@ in sh->ps1 shims ([#4809](https://github.com/ScoopInstaller/Scoop/issues/4809))
|
||||
- **update:** Skip logs starting with `(chore)` ([#4800](https://github.com/ScoopInstaller/Scoop/issues/4800))
|
||||
- **scoop-download:** Add failure check ([#4822](https://github.com/ScoopInstaller/Scoop/issues/4822))
|
||||
- **scoop-list:** Fix date in 'Updated' column showing the months in the place of minutes ([#4880](https://github.com/ScoopInstaller/Scoop/issues/4880))
|
||||
- **scoop-prefix:** Fix typo that breaks global installed apps ([#4795](https://github.com/ScoopInstaller/Scoop/issues/4795))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
- **scoop:** Load libs only once ([#4839](https://github.com/ScoopInstaller/Scoop/issues/4839), [#4884](https://github.com/ScoopInstaller/Scoop/issues/4884))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
- **bucket:** Move 'Find-Manifest' and 'list_buckets' to 'buckets' ([#4814](https://github.com/ScoopInstaller/Scoop/issues/4814))
|
||||
- **relpath:** Use `$PSScriptRoot` instead of `relpath` ([#4793](https://github.com/ScoopInstaller/Scoop/issues/4793))
|
||||
- **reset_aliases:** Move core function of `reset_aliases` to `scoop` ([#4794](https://github.com/ScoopInstaller/Scoop/issues/4794))
|
||||
- **config:** Rename checkver_token to gh_token and SCOOP_CHECKVER_TOKEN to SCOOP_GH_TOKEN ([#4832](https://github.com/ScoopInstaller/Scoop/issues/4832), [#4842](https://github.com/ScoopInstaller/Scoop/issues/4842))
|
||||
|
||||
### Builds
|
||||
|
||||
- **checkver:** Add option to throw error as exception ([#4867](https://github.com/ScoopInstaller/Scoop/issues/4867))
|
||||
- **schema:** Remove 'description' from required fields ([#4853](https://github.com/ScoopInstaller/Scoop/issues/4853), [#4874](https://github.com/ScoopInstaller/Scoop/issues/4874))
|
||||
|
||||
### Documentation
|
||||
|
||||
- **changelog:** Rearrange CHANGELOG ([#4897](https://github.com/ScoopInstaller/Scoop/issues/4897))
|
||||
- **readme:** Update installation instruction ([#4825](https://github.com/ScoopInstaller/Scoop/issues/4825))
|
||||
- **readme:** Fix badges for Gitter and CI Tests ([#4830](https://github.com/ScoopInstaller/Scoop/issues/4830))
|
||||
- **scoop-shim:** Fix typo ([#4836](https://github.com/ScoopInstaller/Scoop/issues/4836))
|
||||
|
||||
## [v0.1.0](https://github.com/ScoopInstaller/Scoop/compare/2021-12-26...v0.1.0) - 2022-03-01
|
||||
|
||||
### Features
|
||||
|
||||
49
LICENSE
49
LICENSE
@@ -1,3 +1,27 @@
|
||||
SPDX-License-Identifier: UNLICENSE or MIT
|
||||
|
||||
INFORMATION ABOUT THIS PROJECT'S LICENSE (SHORT)
|
||||
============================================================================================
|
||||
This project is licensed under the Unlicense or the MIT license,
|
||||
at your option.
|
||||
|
||||
INFORMATION ABOUT THIS PROJECT'S LICENSE (LONG)
|
||||
============================================================================================
|
||||
This project ("Scoop") is free software, licensed under the Unlicense or the
|
||||
MIT license, at your option. Scoop was previously licensed under only the Unlicense,
|
||||
but was dual-licensed from version 0.2.0.
|
||||
|
||||
Scoop comes with ABSOLUTELY NO WARRANTY. Use it at your own risk. Scoop is provided
|
||||
on an AS-IS BASIS and its contributors disclaim all warranties.
|
||||
|
||||
You may use, modify, distribute, sell, copy, compile, or merge Scoop by any means.
|
||||
|
||||
Copies of both licenses can be found below.
|
||||
|
||||
THE LICENSE OF SCOOP
|
||||
============================================================================================
|
||||
Unlicense
|
||||
---------
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
@@ -22,3 +46,28 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
||||
MIT license
|
||||
-----------
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2017 Luke Sampson (https://github.com/lukesampson)
|
||||
Copyright (c) 2013-present Scoop contributors (https://github.com/ScoopInstaller/Scoop/graphs/contributors)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -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'
|
||||
)
|
||||
}
|
||||
|
||||
71
README.md
71
README.md
@@ -18,17 +18,17 @@
|
||||
<a href="https://github.com/ScoopInstaller/Scoop">
|
||||
<img src="https://img.shields.io/github/repo-size/ScoopInstaller/Scoop.svg" alt="Repository size" />
|
||||
</a>
|
||||
<a href="https://ci.appveyor.com/project/ScoopInstaller/Scoop">
|
||||
<img src="https://ci.appveyor.com/api/projects/status/05foxatmrqo0l788?svg=true" alt="Build Status" />
|
||||
<a href="https://github.com/ScoopInstaller/Scoop/actions/workflows/ci.yml">
|
||||
<img src="https://github.com/ScoopInstaller/Scoop/actions/workflows/ci.yml/badge.svg" alt="Scoop Core CI Tests" />
|
||||
</a>
|
||||
<a href="https://discord.gg/s9yRQHt">
|
||||
<img src="https://img.shields.io/badge/chat-on%20discord-7289DA.svg" alt="Discord Chat" />
|
||||
</a>
|
||||
<a href="https://gitter.im/ScoopInstaller/Scoop">
|
||||
<img src="https://badges.gitter.im/ScoopInstaller/Scoop.png" alt="Gitter Chat" />
|
||||
<a href="https://gitter.im/lukesampson/scoop">
|
||||
<img src="https://badges.gitter.im/lukesampson/scoop.png" alt="Gitter Chat" />
|
||||
</a>
|
||||
<a href="https://github.com/ScoopInstaller/Scoop/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/ScoopInstaller/Scoop.svg" alt="License" />
|
||||
<a href="./LICENSE">
|
||||
<img src="https://img.shields.io/badge/license-UNLICENSE%20or%20MIT-blue" alt="License" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -56,60 +56,15 @@ 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.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Windows 7 SP1+ / Windows Server 2008+
|
||||
- [PowerShell 5](https://aka.ms/wmf5download) (or later, include [PowerShell Core](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows?view=powershell-6)) and [.NET Framework 4.5](https://www.microsoft.com/net/download) (or later)
|
||||
- PowerShell must be enabled for your user account e.g. `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser`
|
||||
|
||||
## Installation
|
||||
|
||||
Run the following command from your PowerShell to install scoop to its default location (`C:\Users\<user>\scoop`)
|
||||
Run the following command from a **non-admin** PowerShell to install scoop to its default location `C:\Users\<YOUR USERNAME>\scoop`.
|
||||
|
||||
```powershell
|
||||
Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
|
||||
|
||||
# or shorter
|
||||
iwr -useb get.scoop.sh | iex
|
||||
```
|
||||
|
||||
Once installed, run `scoop help` for instructions.
|
||||
|
||||
The default setup is configured so all user installed programs and Scoop itself live in `C:\Users\<user>\scoop`.
|
||||
Globally installed programs (`--global`) live in `C:\ProgramData\scoop`.
|
||||
These settings can be changed through environment variables.
|
||||
|
||||
### Install Scoop to a Custom Directory by changing `SCOOP`
|
||||
|
||||
```powershell
|
||||
$env:SCOOP='D:\Applications\Scoop'
|
||||
[Environment]::SetEnvironmentVariable('SCOOP', $env:SCOOP, 'User')
|
||||
# run the installer
|
||||
```
|
||||
|
||||
### Configure Scoop to install global programs to a Custom Directory by changing `SCOOP_GLOBAL`
|
||||
|
||||
```powershell
|
||||
$env:SCOOP_GLOBAL='F:\GlobalScoopApps'
|
||||
[Environment]::SetEnvironmentVariable('SCOOP_GLOBAL', $env:SCOOP_GLOBAL, 'Machine')
|
||||
# run the installer
|
||||
```
|
||||
|
||||
### Configure Scoop to store downloads to a Custom Directory by changing `SCOOP_CACHE`
|
||||
|
||||
```powershell
|
||||
$env:SCOOP_CACHE='F:\ScoopCache'
|
||||
[Environment]::SetEnvironmentVariable('SCOOP_CACHE', $env:SCOOP_CACHE, 'Machine')
|
||||
# run the installer
|
||||
```
|
||||
|
||||
### Configure Scoop to use a GitHub API token during searching and checkver by setting `SCOOP_CHECKVER_TOKEN`
|
||||
|
||||
```powershell
|
||||
$env:SCOOP_CHECKVER_TOKEN='<paste-token-here>'
|
||||
[Environment]::SetEnvironmentVariable('SCOOP_CHECKVER_TOKEN', $env:SCOOP_CHECKVER_TOKEN, 'Machine')
|
||||
# search for an app
|
||||
```
|
||||
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.
|
||||
|
||||
## [Documentation](https://github.com/ScoopInstaller/Scoop/wiki)
|
||||
|
||||
@@ -165,17 +120,21 @@ The following buckets are known to scoop:
|
||||
- [games](https://github.com/Calinou/scoop-games) - Open source/freeware games and game-related tools
|
||||
- [nerd-fonts](https://github.com/matthewjberger/scoop-nerd-fonts) - Nerd Fonts
|
||||
- [nirsoft](https://github.com/kodybrown/scoop-nirsoft) - Almost all of the [250+](https://rasa.github.io/scoop-directory/by-apps#kodybrown_scoop-nirsoft) apps from [Nirsoft](https://nirsoft.net)
|
||||
- [sysinternals](https://github.com/niheaven/scoop-sysinternals) - Sysinternals Suite and all individual application from [Microsoft](https://learn.microsoft.com/sysinternals/)
|
||||
- [java](https://github.com/ScoopInstaller/Java) - A collection of Java development kits (JDKs), Java runtime engines (JREs), Java's virtual machine debugging tools and Java based runtime engines.
|
||||
- [nonportable](https://github.com/TheRandomLabs/scoop-nonportable) - Non-portable apps (may require UAC)
|
||||
- [nonportable](https://github.com/ScoopInstaller/Nonportable) - Non-portable apps (may require UAC)
|
||||
- [php](https://github.com/ScoopInstaller/PHP) - Installers for most versions of PHP
|
||||
- [versions](https://github.com/ScoopInstaller/Versions) - Alternative versions of apps found in other buckets
|
||||
|
||||
The main bucket is installed by default. To add any of the other buckets, type:
|
||||
```
|
||||
|
||||
```console
|
||||
scoop bucket add bucketname
|
||||
```
|
||||
|
||||
For example, to add the extras bucket, type:
|
||||
```
|
||||
|
||||
```console
|
||||
scoop bucket add extras
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -23,6 +27,8 @@
|
||||
An array of manifests, which should be updated all the time. (-ForceUpdate parameter to checkver)
|
||||
.PARAMETER SkipUpdated
|
||||
Updated manifests will not be shown.
|
||||
.PARAMETER ThrowError
|
||||
Throw error as exception instead of just printing it.
|
||||
.EXAMPLE
|
||||
PS BUCKETROOT > .\bin\auto-pr.ps1 'someUsername/repository:branch' -Request
|
||||
.EXAMPLE
|
||||
@@ -41,7 +47,7 @@ param(
|
||||
[String] $Upstream,
|
||||
[String] $OriginBranch = 'master',
|
||||
[String] $App = '*',
|
||||
[Parameter(Mandatory = $true)]
|
||||
[String] $CommitMessageFormat = '<app>: Update to version <version>',
|
||||
[ValidateScript( {
|
||||
if (!(Test-Path $_ -Type Container)) {
|
||||
throw "$_ is not a directory!"
|
||||
@@ -54,14 +60,20 @@ param(
|
||||
[Switch] $Request,
|
||||
[Switch] $Help,
|
||||
[string[]] $SpecialSnowflakes,
|
||||
[Switch] $SkipUpdated
|
||||
[Switch] $SkipUpdated,
|
||||
[Switch] $ThrowError
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\json.ps1"
|
||||
. "$PSScriptRoot\..\lib\unix.ps1"
|
||||
|
||||
$Dir = Resolve-Path $Dir
|
||||
if ($App -ne '*' -and (Test-Path $App -PathType Leaf)) {
|
||||
$Dir = Split-Path $App
|
||||
} elseif ($Dir) {
|
||||
$Dir = Convert-Path $Dir
|
||||
} else {
|
||||
throw "'-Dir' parameter required if '-App' is not a filepath!"
|
||||
}
|
||||
|
||||
if ((!$Push -and !$Request) -or $Help) {
|
||||
Write-Host @'
|
||||
@@ -79,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
|
||||
@@ -93,7 +105,7 @@ if (is_unix) {
|
||||
|
||||
function execute($cmd) {
|
||||
Write-Host $cmd -ForegroundColor Green
|
||||
$output = Invoke-Expression $cmd
|
||||
$output = Invoke-Command ([scriptblock]::Create($cmd))
|
||||
|
||||
if ($LASTEXITCODE -gt 0) {
|
||||
abort "^^^ Error! See above ^^^ (last command: $cmd)"
|
||||
@@ -102,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"
|
||||
@@ -119,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"
|
||||
|
||||
@@ -134,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.
|
||||
@@ -147,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')"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,11 +172,11 @@ if ($Push) {
|
||||
execute "hub push origin $OriginBranch"
|
||||
}
|
||||
|
||||
. "$PSScriptRoot\checkver.ps1" -App $App -Dir $Dir -Update -SkipUpdated:$SkipUpdated
|
||||
. "$PSScriptRoot\checkver.ps1" -App $App -Dir $Dir -Update -SkipUpdated:$SkipUpdated -ThrowError:$ThrowError
|
||||
if ($SpecialSnowflakes) {
|
||||
Write-Host "Forcing update on our special snowflakes: $($SpecialSnowflakes -join ',')" -ForegroundColor DarkCyan
|
||||
$SpecialSnowflakes -split ',' | ForEach-Object {
|
||||
. "$PSScriptRoot\checkver.ps1" $_ -Dir $Dir -ForceUpdate
|
||||
. "$PSScriptRoot\checkver.ps1" $_ -Dir $Dir -ForceUpdate -ThrowError:$ThrowError
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,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"
|
||||
@@ -190,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
|
||||
|
||||
143
bin/checkver.ps1
143
bin/checkver.ps1
@@ -17,6 +17,8 @@
|
||||
Updated manifests will not be shown.
|
||||
.PARAMETER Version
|
||||
Update manifest to specific version.
|
||||
.PARAMETER ThrowError
|
||||
Throw error as exception instead of just printing it.
|
||||
.EXAMPLE
|
||||
PS BUCKETROOT > .\bin\checkver.ps1
|
||||
Check all manifest inside default directory.
|
||||
@@ -50,7 +52,6 @@
|
||||
#>
|
||||
param(
|
||||
[String] $App = '*',
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateScript( {
|
||||
if (!(Test-Path $_ -Type Container)) {
|
||||
throw "$_ is not a directory!"
|
||||
@@ -62,21 +63,29 @@ param(
|
||||
[Switch] $Update,
|
||||
[Switch] $ForceUpdate,
|
||||
[Switch] $SkipUpdated,
|
||||
[String] $Version = ''
|
||||
[String] $Version = '',
|
||||
[Switch] $ThrowError
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\autoupdate.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\autoupdate.ps1"
|
||||
. "$PSScriptRoot\..\lib\json.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1" # needed for hash generation
|
||||
. "$PSScriptRoot\..\lib\unix.ps1"
|
||||
|
||||
$Dir = Resolve-Path $Dir
|
||||
$Search = $App
|
||||
$GitHubToken = $env:SCOOP_CHECKVER_TOKEN, (get_config 'checkver-token') | Where-Object -Property Length -Value 0 -GT | Select-Object -First 1
|
||||
if ($App -ne '*' -and (Test-Path $App -PathType Leaf)) {
|
||||
$Dir = Split-Path $App
|
||||
$files = Get-ChildItem $Dir -Filter (Split-Path $App -Leaf)
|
||||
} elseif ($Dir) {
|
||||
$Dir = Convert-Path $Dir
|
||||
$files = Get-ChildItem $Dir -Filter "$App.json" -Recurse
|
||||
} else {
|
||||
throw "'-Dir' parameter required if '-App' is not a filepath!"
|
||||
}
|
||||
|
||||
$GitHubToken = Get-GitHubToken
|
||||
|
||||
# don't use $Version with $App = '*'
|
||||
if ($App -eq '*' -and $Version -ne '') {
|
||||
@@ -86,23 +95,23 @@ if ($App -eq '*' -and $Version -ne '') {
|
||||
# get apps to check
|
||||
$Queue = @()
|
||||
$json = ''
|
||||
Get-ChildItem $Dir "$App.json" | ForEach-Object {
|
||||
$json = parse_json "$Dir\$($_.Name)"
|
||||
$files | ForEach-Object {
|
||||
$file = $_.FullName
|
||||
$json = parse_json $file
|
||||
if ($json.checkver) {
|
||||
$Queue += , @($_.Name, $json)
|
||||
$Queue += , @($_.BaseName, $json, $file)
|
||||
}
|
||||
}
|
||||
|
||||
# clear any existing events
|
||||
Get-Event | ForEach-Object {
|
||||
Remove-Event $_.SourceIdentifier
|
||||
}
|
||||
Get-Event | Remove-Event
|
||||
Get-EventSubscriber | Unregister-Event
|
||||
|
||||
# start all downloads
|
||||
$Queue | ForEach-Object {
|
||||
$name, $json = $_
|
||||
$name, $json, $file = $_
|
||||
|
||||
$substitutions = Get-VersionSubstitution $json.version
|
||||
$substitutions = Get-VersionSubstitution $json.version # 'autoupdate.ps1'
|
||||
|
||||
$wc = New-Object Net.Webclient
|
||||
if ($json.checkver.useragent) {
|
||||
@@ -110,20 +119,34 @@ $Queue | ForEach-Object {
|
||||
} else {
|
||||
$wc.Headers.Add('User-Agent', (Get-UserAgent))
|
||||
}
|
||||
Register-ObjectEvent $wc downloadstringcompleted -ErrorAction Stop | Out-Null
|
||||
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"
|
||||
@@ -139,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) {
|
||||
@@ -156,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
|
||||
}
|
||||
|
||||
@@ -176,7 +226,8 @@ $Queue | ForEach-Object {
|
||||
$url = substitute $url $substitutions
|
||||
|
||||
$state = New-Object psobject @{
|
||||
app = (strip_ext $name);
|
||||
app = $name;
|
||||
file = $file;
|
||||
url = $url;
|
||||
regex = $regex;
|
||||
json = $json;
|
||||
@@ -187,7 +238,7 @@ $Queue | ForEach-Object {
|
||||
}
|
||||
|
||||
$wc.Headers.Add('Referer', (strip_filename $url))
|
||||
$wc.DownloadStringAsync($url, $state)
|
||||
$wc.DownloadDataAsync($url, $state)
|
||||
}
|
||||
|
||||
function next($er) {
|
||||
@@ -204,31 +255,34 @@ while ($in_progress -gt 0) {
|
||||
|
||||
$state = $ev.SourceEventArgs.UserState
|
||||
$app = $state.app
|
||||
$file = $state.file
|
||||
$json = $state.json
|
||||
$url = $state.url
|
||||
$regexp = $state.regex
|
||||
$jsonpath = $state.jsonpath
|
||||
$xpath = $state.xpath
|
||||
$script = $json.checkver.script
|
||||
$reverse = $state.reverse
|
||||
$replace = $state.replace
|
||||
$expected_ver = $json.version
|
||||
$ver = $Version
|
||||
|
||||
if (!$ver) {
|
||||
$page = $ev.SourceEventArgs.Result
|
||||
$err = $ev.SourceEventArgs.Error
|
||||
if ($json.checkver.script) {
|
||||
$page = $json.checkver.script -join "`r`n" | Invoke-Expression
|
||||
if (!$regex -and $replace) {
|
||||
next "'replace' requires 're' or 'regex'"
|
||||
continue
|
||||
}
|
||||
|
||||
$err = $ev.SourceEventArgs.Error
|
||||
if ($err) {
|
||||
next "$($err.message)`r`nURL $url is not valid"
|
||||
continue
|
||||
}
|
||||
|
||||
if (!$regex -and $replace) {
|
||||
next "'replace' requires 're' or 'regex'"
|
||||
continue
|
||||
if ($url) {
|
||||
$page = (Get-Encoding($wc)).GetString($ev.SourceEventArgs.Result)
|
||||
}
|
||||
if ($script) {
|
||||
$page = Invoke-Command ([scriptblock]::Create($script -join "`r`n"))
|
||||
}
|
||||
|
||||
if ($jsonpath) {
|
||||
@@ -255,12 +309,17 @@ while ($in_progress -gt 0) {
|
||||
# Then add them into the NamespaceManager
|
||||
$nsmgr = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
|
||||
$nsList | ForEach-Object {
|
||||
$nsmgr.AddNamespace($_.LocalName, $_.Value)
|
||||
if ($_.LocalName -eq 'xmlns') {
|
||||
$nsmgr.AddNamespace('ns', $_.Value)
|
||||
$xpath = $xpath -replace '/([^:/]+)((?=/)|(?=$))', '/ns:$1'
|
||||
} else {
|
||||
$nsmgr.AddNamespace($_.LocalName, $_.Value)
|
||||
}
|
||||
}
|
||||
# Getting version from XML, using XPath
|
||||
$ver = $xml.SelectSingleNode($xpath, $nsmgr).'#text'
|
||||
if (!$ver) {
|
||||
next "couldn't find '$xpath' in $url"
|
||||
next "couldn't find '$($xpath -replace 'ns:', '')' in $url"
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -308,7 +367,7 @@ while ($in_progress -gt 0) {
|
||||
# Skip actual only if versions are same and there is no -f
|
||||
if (($ver -eq $expected_ver) -and !$ForceUpdate -and $SkipUpdated) { continue }
|
||||
|
||||
Write-Host "$App`: " -NoNewline
|
||||
Write-Host "$app`: " -NoNewline
|
||||
|
||||
# version hasn't changed (step over if forced update)
|
||||
if ($ver -eq $expected_ver -and !$ForceUpdate) {
|
||||
@@ -334,9 +393,13 @@ while ($in_progress -gt 0) {
|
||||
Write-Host 'Forcing autoupdate!' -ForegroundColor DarkMagenta
|
||||
}
|
||||
try {
|
||||
Invoke-AutoUpdate $App $Dir $json $ver $matchesHashtable
|
||||
Invoke-AutoUpdate $app $file $json $ver $matchesHashtable # 'autoupdate.ps1'
|
||||
} catch {
|
||||
error $_.Exception.Message
|
||||
if ($ThrowError) {
|
||||
throw $_
|
||||
} else {
|
||||
error $_.Exception.Message
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -44,7 +44,8 @@ $Queue | ForEach-Object {
|
||||
try {
|
||||
$wc = New-Object Net.Webclient
|
||||
$wc.Headers.Add('User-Agent', (Get-UserAgent))
|
||||
$home_html = $wc.DownloadString($manifest.homepage)
|
||||
$homepage = $wc.DownloadData($manifest.homepage)
|
||||
$home_html = (Get-Encoding($wc)).GetString($homepage)
|
||||
} catch {
|
||||
Write-Host "`n$($_.Exception.Message)" -ForegroundColor Red
|
||||
return
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# for development, update the installed scripts to match local source
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
|
||||
$src = relpath ".."
|
||||
$src = "$PSScriptRoot\.."
|
||||
$dest = ensure (versiondir 'scoop' 'current')
|
||||
|
||||
# make sure not running from the installed directory
|
||||
|
||||
237
bin/sandbox.ps1
Normal file
237
bin/sandbox.ps1
Normal file
@@ -0,0 +1,237 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
# Parse arguments
|
||||
|
||||
Param(
|
||||
[Parameter(Position = 0, HelpMessage = 'The Manifest to install in the Sandbox.')]
|
||||
[String] $Manifest,
|
||||
[Parameter(Position = 1, HelpMessage = 'Options to pass to scoop.')]
|
||||
[String] $Options,
|
||||
[Parameter(Position = 2, HelpMessage = 'The script to run in the Sandbox.')]
|
||||
[ScriptBlock] $Script,
|
||||
[Parameter(HelpMessage = 'The folder to map in the Sandbox.')]
|
||||
[String] $MapFolder = $pwd
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$mapFolder = (Resolve-Path -Path $MapFolder).Path
|
||||
|
||||
if (-Not (Test-Path -Path $mapFolder -PathType Container)) {
|
||||
Write-Error -Category InvalidArgument -Message 'The provided MapFolder is not a folder.'
|
||||
}
|
||||
|
||||
# Check if Windows Sandbox is enabled
|
||||
|
||||
if (-Not (Get-Command 'WindowsSandbox' -ErrorAction SilentlyContinue)) {
|
||||
Write-Error -Category NotInstalled -Message @'
|
||||
Windows Sandbox does not seem to be available. Check the following URL for prerequisites and further details:
|
||||
https://docs.microsoft.com/windows/security/threat-protection/windows-sandbox/windows-sandbox-overview
|
||||
|
||||
You can run the following command in an elevated PowerShell for enabling Windows Sandbox:
|
||||
$ Enable-WindowsOptionalFeature -Online -FeatureName 'Containers-DisposableClientVM'
|
||||
'@
|
||||
}
|
||||
|
||||
# Close Windows Sandbox
|
||||
|
||||
$sandbox = Get-Process 'WindowsSandboxClient' -ErrorAction SilentlyContinue
|
||||
if ($sandbox) {
|
||||
Write-Host '--> Closing Windows Sandbox'
|
||||
|
||||
$sandbox | Stop-Process
|
||||
Start-Sleep -Seconds 5
|
||||
|
||||
Write-Host
|
||||
}
|
||||
Remove-Variable sandbox
|
||||
|
||||
# Initialize Temp Folder
|
||||
|
||||
$tempFolderName = 'SandboxTest'
|
||||
$tempFolder = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath $tempFolderName
|
||||
|
||||
Remove-Item $tempFolder -Force -Recurse
|
||||
|
||||
New-Item $tempFolder -ItemType Directory | Out-Null
|
||||
|
||||
if (-Not [String]::IsNullOrWhiteSpace($Manifest)) {
|
||||
Copy-Item -Path $Manifest -Recurse -Destination $tempFolder
|
||||
}
|
||||
|
||||
if ($null -eq $env:SCOOP_HOME) { $env:SCOOP_HOME = "$env:USERPROFILE\scoop" }
|
||||
$scoopCache = $env:SCOOP_HOME + '\cache'
|
||||
|
||||
Write-Host "Copying $scoopCache to $tempFolder\cache"
|
||||
|
||||
Copy-Item -Path $scoopCache -Recurse -Destination $tempFolder | Out-Null
|
||||
|
||||
$userprofileInSandbox = 'C:\Users\WDAGUtilityAccount'
|
||||
$desktopInSandbox = $userprofileInSandbox + '\Desktop'
|
||||
$sandboxTestInSandbox = $desktopInSandbox + '\' + $tempFolderName
|
||||
$copiedCacheInSandbox = $sandboxTestInSandbox + "\cache"
|
||||
$scoopCacheInSandbox = $userprofileInSandbox + "\scoop\cache"
|
||||
|
||||
# Create Bootstrap script
|
||||
|
||||
# See: https://stackoverflow.com/a/22670892/12156188
|
||||
$bootstrapPs1Content = @'
|
||||
function Update-EnvironmentVariables {
|
||||
foreach($level in "Machine","User") {
|
||||
[Environment]::GetEnvironmentVariables($level).GetEnumerator() | % {
|
||||
# For Path variables, append the new values, if they're not already in there
|
||||
if($_.Name -match 'Path$') {
|
||||
$_.Value = ($((Get-Content "Env:$($_.Name)") + ";$($_.Value)") -split ';' | Select -unique) -join ';'
|
||||
}
|
||||
$_
|
||||
} | Set-Content -Path { "Env:$($_.Name)" }
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ARPTable {
|
||||
$registry_paths = @('HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*','HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKCU:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*')
|
||||
return Get-ItemProperty $registry_paths -ErrorAction SilentlyContinue |
|
||||
Select-Object DisplayName, DisplayVersion, Publisher, @{N='ProductCode'; E={$_.PSChildName}} |
|
||||
Where-Object {$null -ne $_.DisplayName }
|
||||
}
|
||||
'@
|
||||
|
||||
$bootstrapPs1Content += @"
|
||||
Write-Host @'
|
||||
--> Installing Scoop, 7zip, git, innounp, dark and lessmsi
|
||||
'@
|
||||
`$ProgressPreference = 'SilentlyContinue'
|
||||
|
||||
irm get.scoop.sh -outfile 'install.ps1'
|
||||
.\install.ps1 -RunAsAdmin
|
||||
Update-EnvironmentVariables
|
||||
|
||||
xcopy /I /Q /Y $copiedCacheInSandbox\*.* $scoopCacheInSandbox\
|
||||
|
||||
scoop install --no-update-scoop main/7zip
|
||||
scoop install --no-update-scoop main/git
|
||||
scoop install --no-update-scoop main/innounp
|
||||
scoop install --no-update-scoop main/dark
|
||||
scoop install --no-update-scoop main/lessmsi
|
||||
|
||||
Write-Host @'
|
||||
Tip: you can type 'Update-EnvironmentVariables' to update your environment variables, such as after installing a new software.
|
||||
'@
|
||||
|
||||
|
||||
"@
|
||||
|
||||
if (-Not [String]::IsNullOrWhiteSpace($Manifest)) {
|
||||
$manifestFileName = Split-Path $Manifest -Leaf
|
||||
$manifestPathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $manifestFileName)
|
||||
|
||||
$bootstrapPs1Content += @"
|
||||
Write-Host @'
|
||||
|
||||
--> Saving current ARP entries
|
||||
'@
|
||||
`$originalARP = Get-ARPTable
|
||||
Write-Host @'
|
||||
|
||||
--> Running: scoop install $Options $Manifest
|
||||
|
||||
'@
|
||||
|
||||
scoop install $Options --no-update-scoop $manifestPathInSandbox
|
||||
|
||||
Write-Host @'
|
||||
|
||||
--> Refreshing environment variables
|
||||
'@
|
||||
Update-EnvironmentVariables
|
||||
|
||||
Write-Host @'
|
||||
|
||||
--> Comparing ARP entries
|
||||
'@
|
||||
(Compare-Object (Get-ARPTable) `$originalARP -Property DisplayName,DisplayVersion,Publisher,ProductCode)| Select-Object -Property * -ExcludeProperty SideIndicator | Format-Table
|
||||
|
||||
"@
|
||||
}
|
||||
|
||||
if (-Not [String]::IsNullOrWhiteSpace($Script)) {
|
||||
$bootstrapPs1Content += @"
|
||||
Write-Host @'
|
||||
|
||||
--> Running the following script:
|
||||
|
||||
{
|
||||
$Script
|
||||
}
|
||||
|
||||
'@
|
||||
|
||||
$Script
|
||||
|
||||
|
||||
"@
|
||||
}
|
||||
|
||||
$bootstrapPs1Content += @'
|
||||
Write-Host
|
||||
'@
|
||||
|
||||
$bootstrapPs1FileName = 'Bootstrap.ps1'
|
||||
$bootstrapPs1Content | Out-File (Join-Path -Path $tempFolder -ChildPath $bootstrapPs1FileName)
|
||||
|
||||
# Create Wsb file
|
||||
|
||||
$bootstrapPs1InSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $bootstrapPs1FileName)
|
||||
$mapFolderInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Split-Path -Path $mapFolder -Leaf)
|
||||
|
||||
$sandboxTestWsbContent = @"
|
||||
<Configuration>
|
||||
<MappedFolders>
|
||||
<MappedFolder>
|
||||
<HostFolder>$tempFolder</HostFolder>
|
||||
<ReadOnly>true</ReadOnly>
|
||||
</MappedFolder>
|
||||
<MappedFolder>
|
||||
<HostFolder>$mapFolder</HostFolder>
|
||||
</MappedFolder>
|
||||
</MappedFolders>
|
||||
<LogonCommand>
|
||||
<Command>PowerShell Start-Process PowerShell -WindowStyle Maximized -WorkingDirectory '$mapFolderInSandbox' -ArgumentList '-ExecutionPolicy Bypass -NoExit -NoLogo -File $bootstrapPs1InSandbox'</Command>
|
||||
</LogonCommand>
|
||||
</Configuration>
|
||||
"@
|
||||
|
||||
$sandboxTestWsbFileName = 'SandboxTest.wsb'
|
||||
$sandboxTestWsbFile = Join-Path -Path $tempFolder -ChildPath $sandboxTestWsbFileName
|
||||
$sandboxTestWsbContent | Out-File $sandboxTestWsbFile
|
||||
|
||||
Write-Host @"
|
||||
--> Starting Windows Sandbox, and:
|
||||
- Mounting the following directories:
|
||||
- $tempFolder as read-only
|
||||
- $mapFolder as read-and-write
|
||||
- Installing Scoop
|
||||
"@
|
||||
|
||||
if (-Not [String]::IsNullOrWhiteSpace($Manifest)) {
|
||||
Write-Host @"
|
||||
- Installing the Manifest $manifestFileName
|
||||
- Refreshing environment variables
|
||||
- Comparing ARP Entries
|
||||
"@
|
||||
}
|
||||
|
||||
if (-Not [String]::IsNullOrWhiteSpace($Script)) {
|
||||
Write-Host @"
|
||||
- Running the following script:
|
||||
|
||||
{
|
||||
$Script
|
||||
}
|
||||
"@
|
||||
}
|
||||
|
||||
Write-Host
|
||||
|
||||
WindowsSandbox $SandboxTestWsbFile
|
||||
@@ -1,29 +1,53 @@
|
||||
#Requires -Version 5
|
||||
param($cmd)
|
||||
|
||||
Set-StrictMode -off
|
||||
Set-StrictMode -Off
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\commands.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
|
||||
reset_aliases
|
||||
$subCommand = $Args[0]
|
||||
|
||||
$commands = commands
|
||||
if ('--version' -contains $cmd -or (!$cmd -and '-v' -contains $args)) {
|
||||
Write-Host "Current Scoop version:"
|
||||
Invoke-Expression "git -C '$(versiondir 'scoop' 'current')' --no-pager log --oneline HEAD -n 1"
|
||||
Write-Host ""
|
||||
# for aliases where there's a local function, re-alias so the function takes precedence
|
||||
$aliases = Get-Alias | Where-Object { $_.Options -notmatch 'ReadOnly|AllScope' } | ForEach-Object { $_.Name }
|
||||
Get-ChildItem Function: | Where-Object -Property Name -In -Value $aliases | ForEach-Object {
|
||||
Set-Alias -Name $_.Name -Value Local:$($_.Name) -Scope Script
|
||||
}
|
||||
|
||||
Get-LocalBucket | ForEach-Object {
|
||||
$bucketLoc = Find-BucketDirectory $_ -Root
|
||||
if(Test-Path (Join-Path $bucketLoc '.git')) {
|
||||
Write-Host "'$_' bucket:"
|
||||
Invoke-Expression "git -C '$bucketLoc' --no-pager log --oneline HEAD -n 1"
|
||||
Write-Host ""
|
||||
switch ($subCommand) {
|
||||
({ $subCommand -in @($null, '-h', '--help', '/?') }) {
|
||||
exec 'help'
|
||||
}
|
||||
({ $subCommand -in @('-v', '--version') }) {
|
||||
Write-Host 'Current Scoop version:'
|
||||
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
|
||||
Write-Host " - Released at $($version.Matches.Groups[2].Value)"
|
||||
}
|
||||
Write-Host ''
|
||||
|
||||
Get-LocalBucket | ForEach-Object {
|
||||
$bucketLoc = Find-BucketDirectory $_ -Root
|
||||
if (Test-GitAvailable -and (Test-Path "$bucketLoc\.git")) {
|
||||
Write-Host "'$_' bucket:"
|
||||
Invoke-Git -Path $bucketLoc -ArgumentList @('log', 'HEAD', '-1', '--oneline')
|
||||
Write-Host ''
|
||||
}
|
||||
}
|
||||
}
|
||||
({ $subCommand -in (commands) }) {
|
||||
[string[]]$arguments = $Args | Select-Object -Skip 1
|
||||
if ($null -ne $arguments -and $arguments[0] -in @('-h', '--help', '/?')) {
|
||||
exec 'help' @($subCommand)
|
||||
} else {
|
||||
exec $subCommand $arguments
|
||||
}
|
||||
}
|
||||
default {
|
||||
warn "scoop: '$subCommand' isn't a scoop command. See 'scoop help'."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
elseif (@($null, '--help', '/?') -contains $cmd -or $args[0] -contains '-h') { exec 'help' $args }
|
||||
elseif ($commands -contains $cmd) { exec $cmd $args }
|
||||
else { "scoop: '$cmd' isn't a scoop command. See 'scoop help'."; exit 1 }
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
"extras": "https://github.com/ScoopInstaller/Extras",
|
||||
"versions": "https://github.com/ScoopInstaller/Versions",
|
||||
"nirsoft": "https://github.com/kodybrown/scoop-nirsoft",
|
||||
"sysinternals": "https://github.com/niheaven/scoop-sysinternals",
|
||||
"php": "https://github.com/ScoopInstaller/PHP",
|
||||
"nerd-fonts": "https://github.com/matthewjberger/scoop-nerd-fonts",
|
||||
"nonportable": "https://github.com/TheRandomLabs/scoop-nonportable",
|
||||
"nonportable": "https://github.com/ScoopInstaller/Nonportable",
|
||||
"java": "https://github.com/ScoopInstaller/Java",
|
||||
"games": "https://github.com/Calinou/scoop-games"
|
||||
}
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
<#
|
||||
TODO
|
||||
- clean up
|
||||
#>
|
||||
. "$PSScriptRoot\core.ps1"
|
||||
. "$PSScriptRoot\json.ps1"
|
||||
|
||||
# Must included with 'json.ps1'
|
||||
function find_hash_in_rdf([String] $url, [String] $basename) {
|
||||
$data = $null
|
||||
$xml = $null
|
||||
try {
|
||||
# Download and parse RDF XML file
|
||||
$wc = New-Object Net.Webclient
|
||||
$wc.Headers.Add('Referer', (strip_filename $url))
|
||||
$wc.Headers.Add('User-Agent', (Get-UserAgent))
|
||||
[xml]$data = $wc.downloadstring($url)
|
||||
} catch [system.net.webexception] {
|
||||
write-host -f darkred $_
|
||||
write-host -f darkred "URL $url is not valid"
|
||||
$data = $wc.DownloadData($url)
|
||||
[xml]$xml = (Get-Encoding($wc)).GetString($data)
|
||||
} catch [System.Net.WebException] {
|
||||
Write-Host $_ -ForegroundColor DarkRed
|
||||
Write-Host "URL $url is not valid" -ForegroundColor DarkRed
|
||||
return $null
|
||||
}
|
||||
|
||||
# Find file content
|
||||
$digest = $data.RDF.Content | Where-Object { [String]$_.about -eq $basename }
|
||||
$digest = $xml.RDF.Content | Where-Object { [String]$_.about -eq $basename }
|
||||
|
||||
return format_hash $digest.sha256
|
||||
}
|
||||
@@ -29,22 +24,23 @@ 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 {
|
||||
$wc = New-Object Net.Webclient
|
||||
$wc.Headers.Add('Referer', (strip_filename $url))
|
||||
$wc.Headers.Add('User-Agent', (Get-UserAgent))
|
||||
$hashfile = $wc.downloadstring($url)
|
||||
$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
|
||||
}
|
||||
|
||||
@@ -54,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 {
|
||||
@@ -73,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]
|
||||
}
|
||||
}
|
||||
@@ -94,14 +92,16 @@ function find_hash_in_json([String] $url, [Hashtable] $substitutions, [String] $
|
||||
$wc = New-Object Net.Webclient
|
||||
$wc.Headers.Add('Referer', (strip_filename $url))
|
||||
$wc.Headers.Add('User-Agent', (Get-UserAgent))
|
||||
$json = $wc.downloadstring($url)
|
||||
} catch [system.net.webexception] {
|
||||
write-host -f darkred $_
|
||||
write-host -f darkred "URL $url is not valid"
|
||||
$data = $wc.DownloadData($url)
|
||||
$json = (Get-Encoding($wc)).GetString($data)
|
||||
} 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,10 +114,11 @@ function find_hash_in_xml([String] $url, [Hashtable] $substitutions, [String] $x
|
||||
$wc = New-Object Net.Webclient
|
||||
$wc.Headers.Add('Referer', (strip_filename $url))
|
||||
$wc.Headers.Add('User-Agent', (Get-UserAgent))
|
||||
$xml = [xml]$wc.downloadstring($url)
|
||||
$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
|
||||
}
|
||||
|
||||
@@ -127,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
|
||||
@@ -150,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
|
||||
}
|
||||
|
||||
@@ -184,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) {
|
||||
@@ -217,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'
|
||||
}
|
||||
|
||||
@@ -245,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
|
||||
}
|
||||
|
||||
@@ -348,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
|
||||
@@ -361,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
|
||||
@@ -390,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -451,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) {
|
||||
@@ -459,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
|
||||
|
||||
130
lib/buckets.ps1
130
lib/buckets.ps1
@@ -1,5 +1,3 @@
|
||||
. "$PSScriptRoot\core.ps1"
|
||||
|
||||
$bucketsdir = "$scoopdir\buckets"
|
||||
|
||||
function Find-BucketDirectory {
|
||||
@@ -18,7 +16,9 @@ function Find-BucketDirectory {
|
||||
)
|
||||
|
||||
# Handle info passing empty string as bucket ($install.bucket)
|
||||
if(($null -eq $Name) -or ($Name -eq '')) { $Name = 'main' }
|
||||
if (($null -eq $Name) -or ($Name -eq '')) {
|
||||
$Name = 'main'
|
||||
}
|
||||
$bucket = "$bucketsdir\$Name"
|
||||
|
||||
if ((Test-Path "$bucket\bucket") -and !$Root) {
|
||||
@@ -37,7 +37,7 @@ function bucketdir($name) {
|
||||
function known_bucket_repos {
|
||||
$json = "$PSScriptRoot\..\buckets.json"
|
||||
|
||||
return Get-Content $json -raw | convertfrom-json -ea stop
|
||||
return Get-Content $json -Raw | ConvertFrom-Json -ErrorAction stop
|
||||
}
|
||||
|
||||
function known_bucket_repo($name) {
|
||||
@@ -46,11 +46,11 @@ function known_bucket_repo($name) {
|
||||
}
|
||||
|
||||
function known_buckets {
|
||||
known_bucket_repos | ForEach-Object { $_.psobject.properties | Select-Object -expand 'name' }
|
||||
known_bucket_repos | ForEach-Object { $_.PSObject.Properties | Select-Object -Expand 'name' }
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -58,8 +58,12 @@ function Get-LocalBucket {
|
||||
.SYNOPSIS
|
||||
List all local buckets.
|
||||
#>
|
||||
|
||||
return (Get-ChildItem -Directory $bucketsdir).Name
|
||||
$bucketNames = (Get-ChildItem -Path $bucketsdir -Directory).Name
|
||||
if ($null -eq $bucketNames) {
|
||||
return @() # Return a zero-length list instead of $null.
|
||||
} else {
|
||||
return $bucketNames
|
||||
}
|
||||
}
|
||||
|
||||
function buckets {
|
||||
@@ -68,66 +72,104 @@ function buckets {
|
||||
return Get-LocalBucket
|
||||
}
|
||||
|
||||
function find_manifest($app, $bucket) {
|
||||
if ($bucket) {
|
||||
$manifest = manifest $app $bucket
|
||||
if ($manifest) { return $manifest, $bucket }
|
||||
return $null
|
||||
}
|
||||
function Convert-RepositoryUri {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory, Position = 0, ValueFromPipeline = $true)]
|
||||
[AllowEmptyString()]
|
||||
[String] $Uri
|
||||
)
|
||||
|
||||
foreach($bucket in Get-LocalBucket) {
|
||||
$manifest = manifest $app $bucket
|
||||
if($manifest) { return $manifest, $bucket }
|
||||
process {
|
||||
# https://git-scm.com/docs/git-clone#_git_urls
|
||||
# https://regex101.com/r/xGmwRr/1
|
||||
if ($Uri -match '(?:@|/{1,3})(?:www\.|.*@)?(?<provider>[^/]+?)(?::\d+)?[:/](?<user>.+)/(?<repo>.+?)(?:\.git)?/?$') {
|
||||
$Matches.provider, $Matches.user, $Matches.repo -join '/'
|
||||
} else {
|
||||
error "$Uri is not a valid Git URL!"
|
||||
error "Please see https://git-scm.com/docs/git-clone#_git_urls for valid ones."
|
||||
return $null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function list_buckets {
|
||||
$buckets = @()
|
||||
Get-LocalBucket | ForEach-Object {
|
||||
$bucket = [Ordered]@{ Name = $_ }
|
||||
$path = Find-BucketDirectory $_ -Root
|
||||
if ((Test-Path (Join-Path $path '.git')) -and (Get-Command git -ErrorAction SilentlyContinue)) {
|
||||
$bucket.Source = Invoke-Git -Path $path -ArgumentList @('config', 'remote.origin.url')
|
||||
$bucket.Updated = Invoke-Git -Path $path -ArgumentList @('log', "--format='%aD'", '-n', '1')
|
||||
} else {
|
||||
$bucket.Source = friendly_path $path
|
||||
$bucket.Updated = (Get-Item "$path\bucket").LastWriteTime
|
||||
}
|
||||
$bucket.Manifests = Get-ChildItem "$path\bucket" -Force -Recurse -ErrorAction SilentlyContinue |
|
||||
Measure-Object | Select-Object -ExpandProperty Count
|
||||
$buckets += [PSCustomObject]$bucket
|
||||
}
|
||||
,$buckets
|
||||
}
|
||||
|
||||
function add_bucket($name, $repo) {
|
||||
if (!$name) { "<name> missing"; $usage_add; exit 1 }
|
||||
if (!$repo) {
|
||||
$repo = known_bucket_repo $name
|
||||
if (!$repo) { "Unknown bucket '$name'. Try specifying <repo>."; $usage_add; exit 1 }
|
||||
}
|
||||
|
||||
if (!(Test-CommandAvailable git)) {
|
||||
abort "Git is required for buckets. Run 'scoop install git' and try again."
|
||||
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."
|
||||
exit 0
|
||||
if (Test-Path $dir) {
|
||||
warn "The '$name' bucket already exists. To add this bucket again, first remove it by running 'scoop bucket rm $name'."
|
||||
return 2
|
||||
}
|
||||
|
||||
write-host 'Checking repo... ' -nonewline
|
||||
$out = git_cmd ls-remote $repo 2>&1
|
||||
if ($lastexitcode -ne 0) {
|
||||
abort "'$repo' doesn't look like a valid git repository`n`nError given:`n$out"
|
||||
$uni_repo = Convert-RepositoryUri -Uri $repo
|
||||
if ($null -eq $uni_repo) {
|
||||
return 1
|
||||
}
|
||||
foreach ($bucket in Get-LocalBucket) {
|
||||
if (Test-Path -Path "$bucketsdir\$bucket\.git") {
|
||||
$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
|
||||
}
|
||||
}
|
||||
}
|
||||
write-host 'ok'
|
||||
|
||||
ensure $bucketsdir > $null
|
||||
Write-Host 'Checking repo... ' -NoNewline
|
||||
$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
|
||||
}
|
||||
|
||||
function rm_bucket($name) {
|
||||
if (!$name) { "<name> missing"; $usage_rm; exit 1 }
|
||||
$dir = Find-BucketDirectory $name -Root
|
||||
if (!(test-path $dir)) {
|
||||
abort "'$name' bucket not found."
|
||||
if (!(Test-Path $dir)) {
|
||||
error "'$name' bucket not found."
|
||||
return 1
|
||||
}
|
||||
|
||||
Remove-Item $dir -r -force -ea stop
|
||||
Remove-Item $dir -Recurse -Force -ErrorAction Stop
|
||||
return 0
|
||||
}
|
||||
|
||||
function new_issue_msg($app, $bucket, $title, $body) {
|
||||
$app, $manifest, $bucket, $url = Find-Manifest $app $bucket
|
||||
$app, $manifest, $bucket, $url = Get-Manifest "$bucket/$app"
|
||||
$url = known_bucket_repo $bucket
|
||||
$bucket_path = "$bucketsdir\$bucket"
|
||||
|
||||
if (Test-path $bucket_path) {
|
||||
$remote = Invoke-Expression "git -C '$bucket_path' config --get remote.origin.url"
|
||||
if (Test-Path $bucket_path) {
|
||||
$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
|
||||
@@ -135,7 +177,7 @@ function new_issue_msg($app, $bucket, $title, $body) {
|
||||
$url = "https://$($Matches.Provider)/$($Matches.User)/$($Matches.Repo)"
|
||||
}
|
||||
|
||||
if(!$url) { return 'Please contact the bucket maintainer!' }
|
||||
if (!$url) { return 'Please contact the bucket maintainer!' }
|
||||
|
||||
# Print only github repositories
|
||||
if ($url -like '*github*') {
|
||||
@@ -143,7 +185,7 @@ function new_issue_msg($app, $bucket, $title, $body) {
|
||||
$body = [System.Web.HttpUtility]::UrlEncode($body)
|
||||
$url = $url -replace '\.git$', ''
|
||||
$url = "$url/issues/new?title=$title"
|
||||
if($body) {
|
||||
if ($body) {
|
||||
$url += "&body=$body"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
function command_files {
|
||||
(Get-ChildItem (relpath '..\libexec')) `
|
||||
+ (Get-ChildItem "$scoopdir\shims") `
|
||||
| Where-Object { $_.name -match 'scoop-.*?\.ps1$' }
|
||||
(Get-ChildItem "$PSScriptRoot\..\libexec") + (Get-ChildItem "$scoopdir\shims") |
|
||||
Where-Object 'scoop-.*?\.ps1$' -Property Name -Match
|
||||
}
|
||||
|
||||
function commands {
|
||||
@@ -13,7 +12,7 @@ function command_name($filename) {
|
||||
}
|
||||
|
||||
function command_path($cmd) {
|
||||
$cmd_path = relpath "..\libexec\scoop-$cmd.ps1"
|
||||
$cmd_path = "$PSScriptRoot\..\libexec\scoop-$cmd.ps1"
|
||||
|
||||
# built in commands
|
||||
if (!(Test-Path $cmd_path)) {
|
||||
@@ -21,7 +20,7 @@ function command_path($cmd) {
|
||||
$shim_path = "$scoopdir\shims\scoop-$cmd.ps1"
|
||||
$line = ((Get-Content $shim_path) | Where-Object { $_.startswith('$path') })
|
||||
if($line) {
|
||||
Invoke-Expression -command "$line"
|
||||
Invoke-Command ([scriptblock]::Create($line)) -NoNewScope
|
||||
$cmd_path = $path
|
||||
}
|
||||
else { $cmd_path = $shim_path }
|
||||
|
||||
534
lib/core.ps1
534
lib/core.ps1
@@ -9,14 +9,22 @@ function Optimize-SecurityProtocol {
|
||||
|
||||
# If not, change it to support TLS 1.2
|
||||
if (!($isNewerNetFramework -and $isSystemDefault)) {
|
||||
# Set to TLS 1.2 (3072), then TLS 1.1 (768), and TLS 1.0 (192). Ssl3 has been superseded,
|
||||
# https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype?view=netframework-4.5
|
||||
[System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192
|
||||
# Set to TLS 1.2 (3072). Ssl3, TLS 1.0, and 1.1 have been deprecated,
|
||||
# https://datatracker.ietf.org/doc/html/rfc8996
|
||||
[System.Net.ServicePointManager]::SecurityProtocol = 3072
|
||||
}
|
||||
}
|
||||
|
||||
function Get-Encoding($wc) {
|
||||
if ($null -ne $wc.ResponseHeaders -and $wc.ResponseHeaders['Content-Type'] -match 'charset=([^;]*)') {
|
||||
return [System.Text.Encoding]::GetEncoding($Matches[1])
|
||||
} else {
|
||||
return [System.Text.Encoding]::GetEncoding('utf-8')
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -51,6 +59,7 @@ function load_cfg($file) {
|
||||
}
|
||||
|
||||
function get_config($name, $default) {
|
||||
$name = $name.ToLowerInvariant()
|
||||
if($null -eq $scoopConfig.$name -and $null -ne $default) {
|
||||
return $default
|
||||
}
|
||||
@@ -64,6 +73,8 @@ function set_config {
|
||||
$value
|
||||
)
|
||||
|
||||
$name = $name.ToLowerInvariant()
|
||||
|
||||
if ($null -eq $scoopConfig -or $scoopConfig.Count -eq 0) {
|
||||
ensure (Split-Path -Path $configFile) | Out-Null
|
||||
$scoopConfig = New-Object -TypeName PSObject
|
||||
@@ -90,7 +101,7 @@ function set_config {
|
||||
|
||||
function setup_proxy() {
|
||||
# note: '@' and ':' in password must be escaped, e.g. 'p@ssword' -> p\@ssword'
|
||||
$proxy = get_config 'proxy'
|
||||
$proxy = get_config PROXY
|
||||
if(!$proxy) {
|
||||
return
|
||||
}
|
||||
@@ -117,13 +128,78 @@ 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
|
||||
$arguments = $ArgumentList -join ' '
|
||||
$cmd = "`"$git`" $arguments"
|
||||
|
||||
if ($WorkingDirectory) {
|
||||
$cmd = "`"$git`" -C `"$WorkingDirectory`" $arguments"
|
||||
}
|
||||
$sb = [scriptblock]::Create("& $cmd")
|
||||
|
||||
if([String]::IsNullOrEmpty($proxy) -or $proxy -eq 'none') {
|
||||
return Invoke-Command $sb
|
||||
}
|
||||
|
||||
if($arguments -Match '\b(clone|checkout|pull|fetch|ls-remote)\b') {
|
||||
$old_https = $env:HTTPS_PROXY
|
||||
$old_http = $env:HTTP_PROXY
|
||||
try {
|
||||
# convert proxy setting for git
|
||||
if ($proxy.StartsWith('currentuser@')) {
|
||||
$proxy = $proxy.Replace('currentuser@', ':@')
|
||||
}
|
||||
$env:HTTPS_PROXY = $proxy
|
||||
$env:HTTP_PROXY = $proxy
|
||||
return Invoke-Command $sb
|
||||
}
|
||||
catch {
|
||||
error $_
|
||||
return
|
||||
}
|
||||
finally {
|
||||
$env:HTTPS_PROXY = $old_https
|
||||
$env:HTTP_PROXY = $old_http
|
||||
}
|
||||
}
|
||||
|
||||
return Invoke-Command $sb
|
||||
}
|
||||
|
||||
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
|
||||
@@ -145,7 +221,7 @@ function error($msg) { write-host "ERROR $msg" -f darkred }
|
||||
function warn($msg) { write-host "WARN $msg" -f darkyellow }
|
||||
function info($msg) { write-host "INFO $msg" -f darkgray }
|
||||
function debug($obj) {
|
||||
if((get_config 'debug' $false) -ine 'true' -and $env:SCOOP_DEBUG -ine 'true') {
|
||||
if ((get_config DEBUG $false) -ine 'true' -and $env:SCOOP_DEBUG -ine 'true') {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -187,6 +263,9 @@ function filesize($length) {
|
||||
} elseif($length -gt $kb) {
|
||||
"{0:n1} KB" -f ($length / $kb)
|
||||
} else {
|
||||
if ($null -eq $length) {
|
||||
$length = 0
|
||||
}
|
||||
"$($length) B"
|
||||
}
|
||||
}
|
||||
@@ -199,7 +278,7 @@ function appdir($app, $global) { "$(appsdir $global)\$app" }
|
||||
function versiondir($app, $version, $global) { "$(appdir $app $global)\$version" }
|
||||
|
||||
function currentdir($app, $global) {
|
||||
if (get_config NO_JUNCTIONS) {
|
||||
if (get_config NO_JUNCTION) {
|
||||
$version = Select-CurrentVersion -App $app -Global:$global
|
||||
} else {
|
||||
$version = 'current'
|
||||
@@ -214,7 +293,10 @@ function cache_path($app, $version, $url) { "$cachedir\$app#$version#$($url -rep
|
||||
|
||||
# apps
|
||||
function sanitary_path($path) { return [regex]::replace($path, "[/\\?:*<>|]", "") }
|
||||
function installed($app, $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
|
||||
# "dependency". So we need to extract the bucket from the name and only give the app
|
||||
# name to is_directory
|
||||
@@ -232,7 +314,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))
|
||||
}
|
||||
|
||||
@@ -276,12 +358,16 @@ Function Test-CommandAvailable {
|
||||
Return [Boolean](Get-Command $Name -ErrorAction Ignore)
|
||||
}
|
||||
|
||||
Function Test-GitAvailable {
|
||||
Return [Boolean](Test-Path (Get-HelperPath -Helper Git) -ErrorAction Ignore)
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
@@ -290,6 +376,14 @@ function Get-HelperPath {
|
||||
}
|
||||
process {
|
||||
switch ($Helper) {
|
||||
'Git' {
|
||||
$internalgit = "$(versiondir 'git' 'current')\mingw64\bin\git.exe"
|
||||
if (Test-Path $internalgit) {
|
||||
$HelperPath = $internalgit
|
||||
} else {
|
||||
$HelperPath = (Get-Command git -ErrorAction Ignore).Source
|
||||
}
|
||||
}
|
||||
'7zip' {
|
||||
$HelperPath = Get-AppFilePath '7zip' '7z.exe'
|
||||
if ([String]::IsNullOrEmpty($HelperPath)) {
|
||||
@@ -312,6 +406,39 @@ function Get-HelperPath {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-CommandPath {
|
||||
[CmdletBinding()]
|
||||
[OutputType([String])]
|
||||
param(
|
||||
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
|
||||
[String]
|
||||
$Command
|
||||
)
|
||||
|
||||
begin {
|
||||
$userShims = Convert-Path (shimdir $false)
|
||||
$globalShims = fullpath (shimdir $true) # don't resolve: may not exist
|
||||
}
|
||||
|
||||
process {
|
||||
try {
|
||||
$comm = Get-Command $Command -ErrorAction Stop
|
||||
} catch {
|
||||
return $null
|
||||
}
|
||||
$commandPath = if ($comm.Path -like "$userShims*" -or $comm.Path -like "$globalShims*") {
|
||||
Get-ShimTarget ($comm.Path -replace '\.exe$', '.shim')
|
||||
} elseif ($comm.CommandType -eq 'Application') {
|
||||
$comm.Source
|
||||
} elseif ($comm.CommandType -eq 'Alias') {
|
||||
Get-CommandPath $comm.ResolvedCommandName
|
||||
} else {
|
||||
$null
|
||||
}
|
||||
return $commandPath
|
||||
}
|
||||
}
|
||||
|
||||
function Test-HelperInstalled {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
@@ -347,7 +474,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)
|
||||
@@ -401,11 +528,16 @@ function url_remote_filename($url) {
|
||||
return $basename
|
||||
}
|
||||
|
||||
function ensure($dir) { if(!(test-path $dir)) { mkdir $dir > $null }; resolve-path $dir }
|
||||
function fullpath($path) { # should be ~ rooted
|
||||
$executionContext.sessionState.path.getUnresolvedProviderPathFromPSPath($path)
|
||||
function ensure($dir) {
|
||||
if (!(Test-Path -Path $dir)) {
|
||||
New-Item -Path $dir -ItemType Directory | Out-Null
|
||||
}
|
||||
Convert-Path -Path $dir
|
||||
}
|
||||
function fullpath($path) {
|
||||
# should be ~ rooted
|
||||
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
|
||||
}
|
||||
function relpath($path) { "$($myinvocation.psscriptroot)\$path" } # relative to calling script
|
||||
function friendly_path($path) {
|
||||
$h = (Get-PsProvider 'FileSystem').home; if(!$h.endswith('\')) { $h += '\' }
|
||||
if($h -eq '\') { return $path }
|
||||
@@ -455,12 +587,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
|
||||
}
|
||||
@@ -468,9 +600,32 @@ function Invoke-ExternalCommand {
|
||||
if ($RunAs) {
|
||||
$Process.StartInfo.UseShellExecute = $true
|
||||
$Process.StartInfo.Verb = 'RunAs'
|
||||
} else {
|
||||
$Process.StartInfo.CreateNoWindow = $true
|
||||
}
|
||||
if ($FilePath -match '^((cmd|cscript|wscript|msiexec)(\.exe)?|.*\.(bat|cmd|js|vbs|wsf))$') {
|
||||
$Process.StartInfo.Arguments = $ArgumentList -join ' '
|
||||
} elseif ($Process.StartInfo.ArgumentList.Add) {
|
||||
# ArgumentList is supported in PowerShell 6.1 and later (built on .NET Core 2.1+)
|
||||
# ref-1: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.argumentlist?view=net-6.0
|
||||
# ref-2: https://docs.microsoft.com/en-us/powershell/scripting/whats-new/differences-from-windows-powershell?view=powershell-7.2#net-framework-vs-net-core
|
||||
$ArgumentList | ForEach-Object { $Process.StartInfo.ArgumentList.Add($_) }
|
||||
} else {
|
||||
# escape arguments manually in lower versions, refer to https://docs.microsoft.com/en-us/previous-versions/17w5ykft(v=vs.85)
|
||||
$escapedArgs = $ArgumentList | ForEach-Object {
|
||||
# escape N consecutive backslash(es), which are followed by a double quote, to 2N consecutive ones
|
||||
$s = $_ -replace '(\\+)"', '$1$1"'
|
||||
# escape N consecutive backslash(es), which are at the end of the string, to 2N consecutive ones
|
||||
$s = $s -replace '(\\+)$', '$1$1'
|
||||
# escape double quotes
|
||||
$s = $s -replace '"', '\"'
|
||||
# quote the argument
|
||||
"`"$s`""
|
||||
}
|
||||
$Process.StartInfo.Arguments = $escapedArgs -join ' '
|
||||
}
|
||||
try {
|
||||
$Process.Start() | Out-Null
|
||||
[void]$Process.Start()
|
||||
} catch {
|
||||
if ($Activity) {
|
||||
Write-Host "error." -ForegroundColor DarkRed
|
||||
@@ -478,11 +633,17 @@ function Invoke-ExternalCommand {
|
||||
error $_.Exception.Message
|
||||
return $false
|
||||
}
|
||||
if ($LogPath -and ($FilePath -notmatch '(^|\W)msiexec($|\W)')) {
|
||||
Out-File -FilePath $LogPath -Encoding Default -Append -InputObject $Process.StandardOutput.ReadToEnd()
|
||||
Out-File -FilePath $LogPath -Encoding Default -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) {
|
||||
@@ -504,13 +665,6 @@ function Invoke-ExternalCommand {
|
||||
return $true
|
||||
}
|
||||
|
||||
function dl($url,$to) {
|
||||
$wc = New-Object Net.Webclient
|
||||
$wc.headers.add('Referer', (strip_filename $url))
|
||||
$wc.Headers.Add('User-Agent', (Get-UserAgent))
|
||||
$wc.downloadFile($url,$to)
|
||||
}
|
||||
|
||||
function env($name,$global,$val='__get') {
|
||||
$target = 'User'; if($global) {$target = 'Machine'}
|
||||
if($val -eq '__get') { [environment]::getEnvironmentVariable($name,$target) }
|
||||
@@ -552,12 +706,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))"
|
||||
}
|
||||
|
||||
@@ -588,6 +742,20 @@ function get_app_name_from_shim($shim) {
|
||||
return get_app_name $content
|
||||
}
|
||||
|
||||
function Get-ShimTarget($ShimPath) {
|
||||
if ($ShimPath) {
|
||||
$shimTarget = if ($ShimPath.EndsWith('.shim')) {
|
||||
(Get-Content -Path $ShimPath | Select-Object -First 1).Replace('path = ', '').Replace('"', '')
|
||||
} else {
|
||||
((Select-String -Path $ShimPath -Pattern '^(?:@rem|#)\s*(.*)$').Matches.Groups | Select-Object -Index 1).Value
|
||||
}
|
||||
if (!$shimTarget) {
|
||||
$shimTarget = ((Select-String -Path $ShimPath -Pattern '[''"]([^@&]*?)[''"]' -AllMatches).Matches.Groups | Select-Object -Last 1).Value
|
||||
}
|
||||
$shimTarget | Convert-Path
|
||||
}
|
||||
}
|
||||
|
||||
function warn_on_overwrite($shim, $path) {
|
||||
if (!(Test-Path $shim)) {
|
||||
return
|
||||
@@ -619,15 +787,15 @@ function shim($path, $global, $name, $arg) {
|
||||
Push-Location $abs_shimdir
|
||||
$relative_path = Resolve-Path -Relative $path
|
||||
Pop-Location
|
||||
$resolved_path = Resolve-Path $path
|
||||
$resolved_path = Convert-Path $path
|
||||
|
||||
if ($path -match '\.(exe|com)$') {
|
||||
# for programs with no awareness of any shell
|
||||
warn_on_overwrite "$shim.shim" $path
|
||||
Copy-Item (get_shim_path) "$shim.exe" -Force
|
||||
Write-Output "path = `"$resolved_path`"" | Out-File "$shim.shim" -Encoding ASCII
|
||||
Write-Output "path = `"$resolved_path`"" | Out-UTF8File "$shim.shim"
|
||||
if ($arg) {
|
||||
Write-Output "args = $arg" | Out-File "$shim.shim" -Encoding ASCII -Append
|
||||
Write-Output "args = $arg" | Out-UTF8File "$shim.shim" -Append
|
||||
}
|
||||
} elseif ($path -match '\.(bat|cmd)$') {
|
||||
# shim .bat, .cmd so they can be used by programs with no awareness of PSH
|
||||
@@ -635,14 +803,14 @@ function shim($path, $global, $name, $arg) {
|
||||
@(
|
||||
"@rem $resolved_path",
|
||||
"@`"$resolved_path`" $arg %*"
|
||||
) -join "`r`n" | Out-File "$shim.cmd" -Encoding ASCII
|
||||
) -join "`r`n" | Out-UTF8File "$shim.cmd"
|
||||
|
||||
warn_on_overwrite $shim $path
|
||||
@(
|
||||
"#!/bin/sh",
|
||||
"# $resolved_path",
|
||||
"MSYS2_ARG_CONV_EXCL=/C cmd.exe /C `"$resolved_path`" $arg `"$@`""
|
||||
) -join "`n" | Out-File $shim -Encoding ASCII -NoNewline
|
||||
) -join "`n" | Out-UTF8File $shim -NoNewLine
|
||||
} elseif ($path -match '\.ps1$') {
|
||||
# if $path points to another drive resolve-path prepends .\ which could break shims
|
||||
warn_on_overwrite "$shim.ps1" $path
|
||||
@@ -661,89 +829,86 @@ function shim($path, $global, $name, $arg) {
|
||||
"exit `$LASTEXITCODE"
|
||||
)
|
||||
}
|
||||
$ps1text -join "`r`n" | Out-File "$shim.ps1" -Encoding ASCII
|
||||
$ps1text -join "`r`n" | Out-UTF8File "$shim.ps1"
|
||||
|
||||
# make ps1 accessible from cmd.exe
|
||||
warn_on_overwrite "$shim.cmd" $path
|
||||
@(
|
||||
"@rem $resolved_path",
|
||||
"@echo off",
|
||||
"setlocal enabledelayedexpansion",
|
||||
"set args=%*",
|
||||
":: replace problem characters in arguments",
|
||||
"set args=%args:`"='%",
|
||||
"set args=%args:(=``(%",
|
||||
"set args=%args:)=``)%",
|
||||
"set invalid=`"='",
|
||||
"if !args! == !invalid! ( set args= )",
|
||||
"where /q pwsh.exe",
|
||||
"if %errorlevel% equ 0 (",
|
||||
" pwsh -noprofile -ex unrestricted -file `"$resolved_path`" $arg %args%",
|
||||
" pwsh -noprofile -ex unrestricted -file `"$resolved_path`" $arg %*",
|
||||
") else (",
|
||||
" powershell -noprofile -ex unrestricted -file `"$resolved_path`" $arg %args%",
|
||||
" powershell -noprofile -ex unrestricted -file `"$resolved_path`" $arg %*",
|
||||
")"
|
||||
) -join "`r`n" | Out-File "$shim.cmd" -Encoding ASCII
|
||||
) -join "`r`n" | Out-UTF8File "$shim.cmd"
|
||||
|
||||
warn_on_overwrite $shim $path
|
||||
@(
|
||||
"#!/bin/sh",
|
||||
"# $resolved_path",
|
||||
"if command -v pwsh.exe > /dev/null 2>&1; then",
|
||||
" pwsh.exe -noprofile -ex unrestricted -file `"$resolved_path`" $arg $@",
|
||||
" pwsh.exe -noprofile -ex unrestricted -file `"$resolved_path`" $arg `"$@`"",
|
||||
"else",
|
||||
" powershell.exe -noprofile -ex unrestricted -file `"$resolved_path`" $arg $@",
|
||||
" powershell.exe -noprofile -ex unrestricted -file `"$resolved_path`" $arg `"$@`"",
|
||||
"fi"
|
||||
) -join "`n" | Out-File $shim -Encoding ASCII -NoNewline
|
||||
) -join "`n" | Out-UTF8File $shim -NoNewLine
|
||||
} elseif ($path -match '\.jar$') {
|
||||
warn_on_overwrite "$shim.cmd" $path
|
||||
@(
|
||||
"@rem $resolved_path",
|
||||
"@java -jar `"$resolved_path`" $arg %*"
|
||||
) -join "`r`n" | Out-File "$shim.cmd" -Encoding ASCII
|
||||
) -join "`r`n" | Out-UTF8File "$shim.cmd"
|
||||
|
||||
warn_on_overwrite $shim $path
|
||||
@(
|
||||
"#!/bin/sh",
|
||||
"# $resolved_path",
|
||||
"java.exe -jar `"$resolved_path`" $arg `"$@`""
|
||||
) -join "`n" | Out-File $shim -Encoding ASCII -NoNewline
|
||||
) -join "`n" | Out-UTF8File $shim -NoNewLine
|
||||
} elseif ($path -match '\.py$') {
|
||||
warn_on_overwrite "$shim.cmd" $path
|
||||
@(
|
||||
"@rem $resolved_path",
|
||||
"@python `"$resolved_path`" $arg %*"
|
||||
) -join "`r`n" | Out-File "$shim.cmd" -Encoding ASCII
|
||||
) -join "`r`n" | Out-UTF8File "$shim.cmd"
|
||||
|
||||
warn_on_overwrite $shim $path
|
||||
@(
|
||||
"#!/bin/sh",
|
||||
"# $resolved_path",
|
||||
"python.exe `"$resolved_path`" $arg `"$@`""
|
||||
) -join "`n" | Out-File $shim -Encoding ASCII -NoNewline
|
||||
) -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-Command git -CommandType:Application -ErrorAction:Stop).Source -ErrorAction:Stop).Directory.Parent
|
||||
if (!(Get-CommandPath git)) {
|
||||
error "Can't shim '$shim': 'git' is needed but not installed."
|
||||
error "Please install git ('scoop install git') and try again."
|
||||
exit 1
|
||||
}
|
||||
$gitdir = (Get-Item (Get-CommandPath git) -ErrorAction:Stop).Directory.Parent
|
||||
if ($gitdir.FullName -imatch 'mingw') {
|
||||
$gitdir = $gitdir.Parent
|
||||
}
|
||||
@(
|
||||
"@rem $resolved_path",
|
||||
"@`"$(Join-Path (Join-Path $gitdir.FullName 'bin') 'bash.exe')`" `"$resolved_path`" $arg %*"
|
||||
) -join "`r`n" | Out-File "$shim.cmd" -Encoding ASCII
|
||||
) -join "`r`n" | Out-UTF8File "$shim.cmd"
|
||||
|
||||
warn_on_overwrite $shim $path
|
||||
@(
|
||||
"#!/bin/sh",
|
||||
"# $resolved_path",
|
||||
"`"$resolved_path`" $arg `"$@`""
|
||||
) -join "`n" | Out-File $shim -Encoding ASCII -NoNewline
|
||||
) -join "`n" | Out-UTF8File $shim -NoNewLine
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
@@ -774,15 +939,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'" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -797,7 +985,7 @@ function Confirm-InstallationStatus {
|
||||
$Global
|
||||
)
|
||||
$Installed = @()
|
||||
$Apps | Select-Object -Unique | Where-Object { $_.Name -ne 'scoop' } | ForEach-Object {
|
||||
$Apps | Select-Object -Unique | Where-Object { $_ -ne 'scoop' } | ForEach-Object {
|
||||
$App, $null, $null = parse_app $_
|
||||
if ($Global) {
|
||||
if (Test-Path (appdir $App $true)) {
|
||||
@@ -885,66 +1073,18 @@ function pluralize($count, $singular, $plural) {
|
||||
if($count -eq 1) { $singular } else { $plural }
|
||||
}
|
||||
|
||||
function reset_alias($name, $value) {
|
||||
if($existing = get-alias $name -ea ignore | Where-Object { $_.options -match 'readonly' }) {
|
||||
if($existing.definition -ne $value) {
|
||||
write-host "Alias $name is read-only; can't reset it." -f darkyellow
|
||||
}
|
||||
return # already set
|
||||
}
|
||||
if($value -is [scriptblock]) {
|
||||
if(!(test-path -path "function:script:$name")) {
|
||||
new-item -path function: -name "script:$name" -value $value | out-null
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
set-alias $name $value -scope script -option allscope
|
||||
}
|
||||
|
||||
function reset_aliases() {
|
||||
# for aliases where there's a local function, re-alias so the function takes precedence
|
||||
$aliases = get-alias | Where-Object { $_.options -notmatch 'readonly|allscope' } | ForEach-Object { $_.name }
|
||||
get-childitem function: | ForEach-Object {
|
||||
$fn = $_.name
|
||||
if($aliases -contains $fn) {
|
||||
set-alias $fn local:$fn -scope script
|
||||
}
|
||||
}
|
||||
|
||||
# for dealing with user aliases
|
||||
$default_aliases = @{
|
||||
'cp' = 'copy-item'
|
||||
'echo' = 'write-output'
|
||||
'gc' = 'get-content'
|
||||
'gci' = 'get-childitem'
|
||||
'gcm' = 'get-command'
|
||||
'gm' = 'get-member'
|
||||
'iex' = 'invoke-expression'
|
||||
'ls' = 'get-childitem'
|
||||
'mkdir' = { new-item -type directory @args }
|
||||
'mv' = 'move-item'
|
||||
'rm' = 'remove-item'
|
||||
'sc' = 'set-content'
|
||||
'select' = 'select-object'
|
||||
'sls' = 'select-string'
|
||||
}
|
||||
|
||||
# set default aliases
|
||||
$default_aliases.keys | ForEach-Object { reset_alias $_ $default_aliases[$_] }
|
||||
}
|
||||
|
||||
# convert list of apps to list of ($app, $global) tuples
|
||||
function applist($apps, $global) {
|
||||
if(!$apps) { return @() }
|
||||
return ,@($apps | ForEach-Object { ,@($_, $global) })
|
||||
}
|
||||
|
||||
function parse_app([string] $app) {
|
||||
if($app -match '(?:(?<bucket>[a-zA-Z0-9-]+)\/)?(?<app>.*\.json$|[a-zA-Z0-9-_.]+)(?:@(?<version>.*))?') {
|
||||
return $matches['app'], $matches['bucket'], $matches['version']
|
||||
function parse_app([string]$app) {
|
||||
if ($app -match '^(?:(?<bucket>[a-zA-Z0-9-_.]+)/)?(?<app>.*\.json$|[a-zA-Z0-9-_.]+)(?:@(?<version>.*))?$') {
|
||||
return $Matches['app'], $Matches['bucket'], $Matches['version']
|
||||
} else {
|
||||
return $app, $null, $null
|
||||
}
|
||||
return $app, $null, $null
|
||||
}
|
||||
|
||||
function show_app($app, $bucket, $version) {
|
||||
@@ -957,29 +1097,39 @@ function show_app($app, $bucket, $version) {
|
||||
return $app
|
||||
}
|
||||
|
||||
function last_scoop_update() {
|
||||
# PowerShell 6 returns an DateTime Object
|
||||
$last_update = (scoop 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) {
|
||||
scoop 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) {
|
||||
@@ -1046,6 +1196,10 @@ function get_hash([String] $multihash) {
|
||||
return $type, $hash.ToLower()
|
||||
}
|
||||
|
||||
function Get-GitHubToken {
|
||||
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)
|
||||
{
|
||||
# FossHub.com
|
||||
@@ -1071,6 +1225,18 @@ function handle_special_urls($url)
|
||||
# Reshapes the URL to avoid redirections
|
||||
$url = "https://downloads.sourceforge.net/project/$($matches['project'])/$($matches['file'])"
|
||||
}
|
||||
|
||||
# Github.com
|
||||
if ($url -match 'github.com/(?<owner>[^/]+)/(?<repo>[^/]+)/releases/download/(?<tag>[^/]+)/(?<file>[^/#]+)(?<filename>.*)' -and ($token = Get-GitHubToken)) {
|
||||
$headers = @{ "Authorization" = "token $token" }
|
||||
$privateUrl = "https://api.github.com/repos/$($Matches.owner)/$($Matches.repo)"
|
||||
$assetUrl = "https://api.github.com/repos/$($Matches.owner)/$($Matches.repo)/releases/tags/$($Matches.tag)"
|
||||
|
||||
if ((Invoke-RestMethod -Uri $privateUrl -Headers $headers).Private) {
|
||||
$url = ((Invoke-RestMethod -Uri $assetUrl -Headers $headers).Assets | Where-Object -Property Name -EQ -Value $Matches.file).Url, $Matches.filename -join ''
|
||||
}
|
||||
}
|
||||
|
||||
return $url
|
||||
}
|
||||
|
||||
@@ -1101,14 +1267,25 @@ function Out-UTF8File {
|
||||
[Parameter(Mandatory = $True, Position = 0)]
|
||||
[Alias("Path")]
|
||||
[String] $FilePath,
|
||||
[Switch] $Append,
|
||||
[Switch] $NoNewLine,
|
||||
[Parameter(ValueFromPipeline = $True)]
|
||||
[PSObject] $InputObject
|
||||
)
|
||||
process {
|
||||
# Ref: https://stackoverflow.com/questions/5596982
|
||||
# Performance Note: `WriteAllLines` throttles memory usage while
|
||||
# `WriteAllText` needs to keep the complete string in memory.
|
||||
[System.IO.File]::WriteAllLines($FilePath, $InputObject)
|
||||
if ($Append) {
|
||||
[System.IO.File]::AppendAllText($FilePath, $InputObject)
|
||||
} else {
|
||||
if (!$NoNewLine) {
|
||||
# Ref: https://stackoverflow.com/questions/5596982
|
||||
# Performance Note: `WriteAllLines` throttles memory usage while
|
||||
# `WriteAllText` needs to keep the complete string in memory.
|
||||
[System.IO.File]::WriteAllLines($FilePath, $InputObject)
|
||||
} else {
|
||||
# However `WriteAllText` does not add ending newline.
|
||||
[System.IO.File]::WriteAllText($FilePath, $InputObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1120,31 +1297,56 @@ function Out-UTF8File {
|
||||
# for all communication with api.github.com
|
||||
Optimize-SecurityProtocol
|
||||
|
||||
# Load Scoop config
|
||||
$configHome = $env:XDG_CONFIG_HOME, "$env:USERPROFILE\.config" | Select-Object -First 1
|
||||
$configFile = "$configHome\scoop\config.json"
|
||||
$scoopConfig = load_cfg $configFile
|
||||
|
||||
# NOTE Scoop config file migration. Remove this after 2023/6/30
|
||||
if ($scoopConfig) {
|
||||
$newConfigNames = @{
|
||||
'lastUpdate' = 'last_update'
|
||||
'SCOOP_REPO' = 'scoop_repo'
|
||||
'SCOOP_BRANCH' = 'scoop_branch'
|
||||
'7ZIPEXTRACT_USE_EXTERNAL' = 'use_external_7zip'
|
||||
'MSIEXTRACT_USE_LESSMSI' = 'use_lessmsi'
|
||||
'NO_JUNCTIONS' = 'no_junction'
|
||||
'manifest_review' = 'show_manifest'
|
||||
'rootPath' = 'root_path'
|
||||
'globalPath' = 'global_path'
|
||||
'cachePath' = 'cache_path'
|
||||
}
|
||||
$newConfigNames.GetEnumerator() | ForEach-Object {
|
||||
if ($null -ne $scoopConfig.$($_.Key)) {
|
||||
$value = $scoopConfig.$($_.Key)
|
||||
$scoopConfig.PSObject.Properties.Remove($_.Key)
|
||||
$scoopConfig | Add-Member -MemberType NoteProperty -Name $_.Value -Value $value
|
||||
if ($_.Key -eq 'lastUpdate') {
|
||||
$scoopConfigChg = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($scoopConfigChg) { # Only save config file if there was a change
|
||||
ConvertTo-Json $scoopConfig | Out-UTF8File -FilePath $configFile
|
||||
}
|
||||
}
|
||||
# END NOTE
|
||||
|
||||
# Scoop root directory
|
||||
$scoopdir = $env:SCOOP, (get_config 'rootPath'), "$env:USERPROFILE\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
|
||||
$scoopdir = $env:SCOOP, (get_config ROOT_PATH), "$([System.Environment]::GetFolderPath('UserProfile'))\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
|
||||
|
||||
# Scoop global apps directory
|
||||
$globaldir = $env:SCOOP_GLOBAL, (get_config 'globalPath'), "$env:ProgramData\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -first 1
|
||||
$globaldir = $env:SCOOP_GLOBAL, (get_config GLOBAL_PATH), "$([System.Environment]::GetFolderPath('CommonApplicationData'))\scoop" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
|
||||
|
||||
# Scoop cache directory
|
||||
# Note: Setting the SCOOP_CACHE environment variable to use a shared directory
|
||||
# is experimental and untested. There may be concurrency issues when
|
||||
# multiple users write and access cached files at the same time.
|
||||
# Use at your own risk.
|
||||
$cachedir = $env:SCOOP_CACHE, (get_config 'cachePath'), "$scoopdir\cache" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -first 1
|
||||
$cachedir = $env:SCOOP_CACHE, (get_config CACHE_PATH), "$scoopdir\cache" | Where-Object { -not [String]::IsNullOrEmpty($_) } | Select-Object -First 1
|
||||
|
||||
# Scoop config file migration
|
||||
$configHome = $env:XDG_CONFIG_HOME, "$env:USERPROFILE\.config" | Select-Object -First 1
|
||||
$configFile = "$configHome\scoop\config.json"
|
||||
if ((Test-Path "$env:USERPROFILE\.scoop") -and !(Test-Path $configFile)) {
|
||||
New-Item -ItemType Directory (Split-Path -Path $configFile) -ErrorAction Ignore | Out-Null
|
||||
Move-Item "$env:USERPROFILE\.scoop" $configFile
|
||||
write-host "WARN Scoop configuration has been migrated from '~/.scoop'" -f darkyellow
|
||||
write-host "WARN to '$configFile'" -f darkyellow
|
||||
}
|
||||
|
||||
# Load Scoop config
|
||||
$scoopConfig = load_cfg $configFile
|
||||
# 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,9 +54,10 @@ 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) {
|
||||
$TarFile = (Get-Content -Path $LogPath)[-5] -replace '.{53}(.*)', '$1' # get inner tar file name
|
||||
# get inner tar file name
|
||||
$TarFile = (Select-String -Path $LogPath -Pattern '[^ ]*tar$').Matches.Value
|
||||
Expand-7zipArchive -Path "$DestinationPath\$TarFile" -DestinationPath $DestinationPath -ExtractDir $ExtractDir -Removal
|
||||
} else {
|
||||
abort "Failed to list files in $Path.`nNot a 7-Zip supported archive file."
|
||||
@@ -63,7 +65,15 @@ function Expand-7zipArchive {
|
||||
}
|
||||
if ($Removal) {
|
||||
# Remove original archive file
|
||||
Remove-Item $Path -Force
|
||||
if (($Path -replace '.*\.([^\.]*)$', '$1') -eq '001') {
|
||||
# Remove splited 7-zip archive parts
|
||||
Get-ChildItem "$($Path -replace '\.[^\.]*$', '').???" | Remove-Item -Force
|
||||
} elseif (($Path -replace '.*\.part(\d+)\.rar$', '$1')[-1] -eq '1') {
|
||||
# Remove splitted RAR archive parts
|
||||
Get-ChildItem "$($Path -replace '\.part(\d+)\.rar$', '').part*.rar" | Remove-Item -Force
|
||||
} else {
|
||||
Remove-Item $Path -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,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)
|
||||
@@ -137,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) {
|
||||
@@ -191,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" }
|
||||
@@ -231,7 +241,8 @@ function Expand-ZipArchive {
|
||||
$OriDestinationPath = $DestinationPath
|
||||
$DestinationPath = "$DestinationPath\_tmp"
|
||||
}
|
||||
Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force
|
||||
# Compatible with Pscx v3 (https://github.com/Pscx/Pscx) ('Microsoft.PowerShell.Archive' is not needed for Pscx v4)
|
||||
Microsoft.PowerShell.Archive\Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force
|
||||
if ($ExtractDir) {
|
||||
movedir "$DestinationPath\$ExtractDir" $OriDestinationPath | Out-Null
|
||||
Remove-Item $DestinationPath -Recurse -Force
|
||||
@@ -258,7 +269,7 @@ function Expand-DarkArchive {
|
||||
$Removal
|
||||
)
|
||||
$LogPath = "$(Split-Path $Path)\dark.log"
|
||||
$ArgList = @('-nologo', "-x `"$DestinationPath`"", "`"$Path`"")
|
||||
$ArgList = @('-nologo', '-x', $DestinationPath, $Path)
|
||||
if ($Switches) {
|
||||
$ArgList += (-split $Switches)
|
||||
}
|
||||
|
||||
@@ -32,9 +32,8 @@ function Get-Dependency {
|
||||
$Unresolved = @()
|
||||
)
|
||||
process {
|
||||
$AppName, $bucket, $null = parse_app $AppName
|
||||
$AppName, $manifest, $bucket, $url = Get-Manifest $AppName
|
||||
$Unresolved += $AppName
|
||||
$null, $manifest, $null, $null = Find-Manifest $AppName $bucket
|
||||
|
||||
if (!$manifest) {
|
||||
if (((Get-LocalBucket) -notcontains $bucket) -and $bucket) {
|
||||
@@ -58,7 +57,11 @@ function Get-Dependency {
|
||||
if ($bucket) {
|
||||
$Resolved += "$bucket/$AppName"
|
||||
} else {
|
||||
$Resolved += $AppName
|
||||
if ($url) {
|
||||
$Resolved += $url
|
||||
} else {
|
||||
$Resolved += $AppName
|
||||
}
|
||||
}
|
||||
if ($Unresolved.Length -eq 0) {
|
||||
return $Resolved
|
||||
@@ -103,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 *')) {
|
||||
@@ -141,7 +144,7 @@ function Test-7zipRequirement {
|
||||
$Uri
|
||||
)
|
||||
return ($Uri | Where-Object {
|
||||
$_ -match '\.((gz)|(tar)|(t[abgpx]z2?)|(lzma)|(bz2?)|(7z)|(rar)|(iso)|(xz)|(lzh)|(nupkg))(\.[^.]+)?$'
|
||||
$_ -match '\.((gz)|(tar)|(t[abgpx]z2?)|(lzma)|(bz2?)|(7z)|(001)|(rar)|(iso)|(xz)|(lzh)|(nupkg))(\.[^\d.]+)?$'
|
||||
}).Count -gt 0
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ function find_description($url, $html, $redir = $false) {
|
||||
if($refresh -and !$redir) {
|
||||
$wc = New-Object Net.Webclient
|
||||
$wc.Headers.Add('User-Agent', (Get-UserAgent))
|
||||
$html = $wc.downloadstring($refresh)
|
||||
$data = $wc.DownloadData($refresh)
|
||||
$html = (Get-Encoding($wc)).GetString($data)
|
||||
return find_description $refresh $html $true
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ 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.
|
||||
#>
|
||||
. "$PSScriptRoot\buckets.ps1"
|
||||
|
||||
function check_windows_defender($global) {
|
||||
$defender = Get-Service -Name WinDefend -ErrorAction SilentlyContinue
|
||||
if (Test-CommandAvailable Get-MpPreference) {
|
||||
@@ -56,3 +54,16 @@ function check_long_paths {
|
||||
return $true
|
||||
}
|
||||
|
||||
function Get-WindowsDeveloperModeStatus {
|
||||
$DevModRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
|
||||
if (!(Test-Path -Path $DevModRegistryPath) -or (Get-ItemProperty -Path `
|
||||
$DevModRegistryPath -Name AllowDevelopmentWithoutDevLicense -ErrorAction `
|
||||
SilentlyContinue).AllowDevelopmentWithoutDevLicense -ne 1) {
|
||||
warn "Windows Developer Mode is not enabled. Operations relevant to symlinks may fail without proper rights."
|
||||
Write-Host " You may read more about the symlinks support here:"
|
||||
Write-Host " https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/"
|
||||
return $false
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
# array of strings that are long-form options. options that take
|
||||
# a parameter should end with '='
|
||||
# returns @(opts hash, remaining_args array, error string)
|
||||
# NOTES:
|
||||
# The first "--" in $argv, if any, will terminate all options; any
|
||||
# following arguments are treated as non-option arguments, even if
|
||||
# they begin with a hyphen. The "--" itself will not be included in
|
||||
# the returned $opts. (POSIX-compatible)
|
||||
function getopt($argv, $shortopts, $longopts) {
|
||||
$opts = @{}; $rem = @()
|
||||
|
||||
@@ -16,29 +21,35 @@ function getopt($argv, $shortopts, $longopts) {
|
||||
}
|
||||
|
||||
function regex_escape($str) {
|
||||
return [regex]::escape($str)
|
||||
return [Regex]::Escape($str)
|
||||
}
|
||||
|
||||
# ensure these are arrays
|
||||
$argv = @($argv)
|
||||
$argv = @($argv -split ' ')
|
||||
$longopts = @($longopts)
|
||||
|
||||
for($i = 0; $i -lt $argv.length; $i++) {
|
||||
for ($i = 0; $i -lt $argv.Length; $i++) {
|
||||
$arg = $argv[$i]
|
||||
if($null -eq $arg) { continue }
|
||||
if ($null -eq $arg) { continue }
|
||||
# don't try to parse array arguments
|
||||
if($arg -is [array]) { $rem += ,$arg; continue }
|
||||
if($arg -is [int]) { $rem += $arg; continue }
|
||||
if($arg -is [decimal]) { $rem += $arg; continue }
|
||||
if ($arg -is [Array]) { $rem += , $arg; continue }
|
||||
if ($arg -is [Int]) { $rem += $arg; continue }
|
||||
if ($arg -is [Decimal]) { $rem += $arg; continue }
|
||||
|
||||
if($arg.startswith('--')) {
|
||||
$name = $arg.substring(2)
|
||||
if ($arg -eq '--') {
|
||||
if ($i -lt $argv.Length - 1) {
|
||||
$rem += $argv[($i + 1)..($argv.Length - 1)]
|
||||
}
|
||||
break
|
||||
} elseif ($arg.StartsWith('--')) {
|
||||
$name = $arg.Substring(2)
|
||||
|
||||
$longopt = $longopts | Where-Object { $_ -match "^$name=?$" }
|
||||
|
||||
if($longopt) {
|
||||
if($longopt.endswith('=')) { # requires arg
|
||||
if($i -eq $argv.length - 1) {
|
||||
if ($longopt) {
|
||||
if ($longopt.EndsWith('=')) {
|
||||
# requires arg
|
||||
if ($i -eq $argv.Length - 1) {
|
||||
return err "Option --$name requires an argument."
|
||||
}
|
||||
$opts.$name = $argv[++$i]
|
||||
@@ -48,14 +59,14 @@ function getopt($argv, $shortopts, $longopts) {
|
||||
} else {
|
||||
return err "Option --$name not recognized."
|
||||
}
|
||||
} elseif($arg.startswith('-') -and $arg -ne '-') {
|
||||
for($j = 1; $j -lt $arg.length; $j++) {
|
||||
$letter = $arg[$j].tostring()
|
||||
} elseif ($arg.StartsWith('-') -and $arg -ne '-') {
|
||||
for ($j = 1; $j -lt $arg.Length; $j++) {
|
||||
$letter = $arg[$j].ToString()
|
||||
|
||||
if($shortopts -match "$(regex_escape $letter)`:?") {
|
||||
$shortopt = $matches[0]
|
||||
if($shortopt[1] -eq ':') {
|
||||
if($j -ne $arg.length -1 -or $i -eq $argv.length - 1) {
|
||||
if ($shortopts -match "$(regex_escape $letter)`:?") {
|
||||
$shortopt = $Matches[0]
|
||||
if ($shortopt[1] -eq ':') {
|
||||
if ($j -ne $arg.Length - 1 -or $i -eq $argv.Length - 1) {
|
||||
return err "Option -$letter requires an argument."
|
||||
}
|
||||
$opts.$letter = $argv[++$i]
|
||||
|
||||
227
lib/install.ps1
227
lib/install.ps1
@@ -1,17 +1,12 @@
|
||||
. "$PSScriptRoot\autoupdate.ps1"
|
||||
. "$PSScriptRoot\buckets.ps1"
|
||||
|
||||
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) {
|
||||
$app, $bucket, $null = parse_app $app
|
||||
$app, $manifest, $bucket, $url = Find-Manifest $app $bucket
|
||||
$app, $manifest, $bucket, $url = Get-Manifest $app
|
||||
|
||||
if(!$manifest) {
|
||||
abort "Couldn't find manifest for '$app'$(if($url) { " at the URL $url" })."
|
||||
@@ -25,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 {
|
||||
@@ -47,14 +43,15 @@ 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
|
||||
pre_install $manifest $architecture
|
||||
$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
|
||||
ensure_install_dir_not_in_path $dir $global
|
||||
$dir = link_current $dir
|
||||
@@ -68,7 +65,7 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
|
||||
persist_data $manifest $original_dir $persist_dir
|
||||
persist_permission $manifest $global
|
||||
|
||||
post_install $manifest $architecture
|
||||
Invoke-HookScript -HookType 'post_install' -Manifest $manifest -Arch $architecture
|
||||
|
||||
# save info for uninstall
|
||||
save_installed_manifest $app $bucket $dir $url
|
||||
@@ -83,59 +80,31 @@ function install_app($app, $architecture, $global, $suggested, $use_cache = $tru
|
||||
show_notes $manifest $dir $original_dir $persist_dir
|
||||
}
|
||||
|
||||
function locate($app, $bucket) {
|
||||
Show-DeprecatedWarning $MyInvocation 'Find-Manifest'
|
||||
return Find-Manifest $app $bucket
|
||||
}
|
||||
|
||||
function Find-Manifest($app, $bucket) {
|
||||
$manifest, $url = $null, $null
|
||||
|
||||
# check if app is a URL or UNC path
|
||||
if($app -match '^(ht|f)tps?://|\\\\') {
|
||||
$url = $app
|
||||
$app = appname_from_url $url
|
||||
$manifest = url_manifest $url
|
||||
} else {
|
||||
# check buckets
|
||||
$manifest, $bucket = find_manifest $app $bucket
|
||||
|
||||
if(!$manifest) {
|
||||
# couldn't find app in buckets: check if it's a local path
|
||||
$path = $app
|
||||
if(!$path.endswith('.json')) { $path += '.json' }
|
||||
if(test-path $path) {
|
||||
$url = "$(resolve-path $path)"
|
||||
$app = appname_from_url $url
|
||||
$manifest, $bucket = url_manifest $url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $app, $manifest, $bucket, $url
|
||||
}
|
||||
|
||||
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 }
|
||||
@@ -210,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)
|
||||
|
||||
@@ -245,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)'"
|
||||
@@ -304,7 +273,7 @@ function dl_with_cache_aria2($app, $version, $manifest, $architecture, $dir, $co
|
||||
$oriConsoleEncoding = [Console]::OutputEncoding
|
||||
[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding
|
||||
|
||||
Invoke-Expression $aria2 | ForEach-Object {
|
||||
Invoke-Command ([scriptblock]::Create($aria2)) | ForEach-Object {
|
||||
# Skip blank lines
|
||||
if ([String]::IsNullOrWhiteSpace($_)) { return }
|
||||
|
||||
@@ -386,16 +355,26 @@ function dl_with_cache_aria2($app, $version, $manifest, $architecture, $dir, $co
|
||||
}
|
||||
|
||||
# download with filesize and progress indicator
|
||||
function dl($url, $to, $cookies, $progress) {
|
||||
$reqUrl = ($url -split "#")[0]
|
||||
$wreq = [net.webrequest]::create($reqUrl)
|
||||
if($wreq -is [net.httpwebrequest]) {
|
||||
$wreq.useragent = Get-UserAgent
|
||||
if (-not ($url -imatch "sourceforge\.net" -or $url -imatch "portableapps\.com")) {
|
||||
$wreq.referer = strip_filename $url
|
||||
function Invoke-Download ($url, $to, $cookies, $progress) {
|
||||
$reqUrl = ($url -split '#')[0]
|
||||
$wreq = [Net.WebRequest]::Create($reqUrl)
|
||||
if ($wreq -is [Net.HttpWebRequest]) {
|
||||
$wreq.UserAgent = Get-UserAgent
|
||||
if (-not ($url -match 'sourceforge\.net' -or $url -match 'portableapps\.com')) {
|
||||
$wreq.Referer = strip_filename $url
|
||||
}
|
||||
if($cookies) {
|
||||
$wreq.headers.add('Cookie', (cookie_header $cookies))
|
||||
if ($url -match 'api\.github\.com/repos') {
|
||||
$wreq.Accept = 'application/octet-stream'
|
||||
$wreq.Headers['Authorization'] = "token $(Get-GitHubToken)"
|
||||
}
|
||||
if ($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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,7 +409,7 @@ function dl($url, $to, $cookies, $progress) {
|
||||
$newUrl = "$newUrl#/$postfix"
|
||||
}
|
||||
|
||||
dl $newUrl $to $cookies $progress
|
||||
Invoke-Download $newUrl $to $cookies $progress
|
||||
return
|
||||
}
|
||||
|
||||
@@ -441,12 +420,12 @@ function dl($url, $to, $cookies, $progress) {
|
||||
|
||||
if ($progress -and ($total -gt 0)) {
|
||||
[console]::CursorVisible = $false
|
||||
function dl_onProgress($read) {
|
||||
dl_progress $read $total $url
|
||||
function Trace-DownloadProgress ($read) {
|
||||
Write-DownloadProgress $read $total $url
|
||||
}
|
||||
} else {
|
||||
write-host "Downloading $url ($(filesize $total))..."
|
||||
function dl_onProgress {
|
||||
function Trace-DownloadProgress {
|
||||
#no op
|
||||
}
|
||||
}
|
||||
@@ -458,17 +437,17 @@ function dl($url, $to, $cookies, $progress) {
|
||||
$totalRead = 0
|
||||
$sw = [diagnostics.stopwatch]::StartNew()
|
||||
|
||||
dl_onProgress $totalRead
|
||||
Trace-DownloadProgress $totalRead
|
||||
while(($read = $s.read($buffer, 0, $buffer.length)) -gt 0) {
|
||||
$fs.write($buffer, 0, $read)
|
||||
$totalRead += $read
|
||||
if ($sw.elapsedmilliseconds -gt 100) {
|
||||
$sw.restart()
|
||||
dl_onProgress $totalRead
|
||||
Trace-DownloadProgress $totalRead
|
||||
}
|
||||
}
|
||||
$sw.stop()
|
||||
dl_onProgress $totalRead
|
||||
Trace-DownloadProgress $totalRead
|
||||
} finally {
|
||||
if ($progress) {
|
||||
[console]::CursorVisible = $true
|
||||
@@ -484,7 +463,7 @@ function dl($url, $to, $cookies, $progress) {
|
||||
}
|
||||
}
|
||||
|
||||
function dl_progress_output($url, $read, $total, $console) {
|
||||
function Format-DownloadProgress ($url, $read, $total, $console) {
|
||||
$filename = url_remote_filename $url
|
||||
|
||||
# calculate current percentage done
|
||||
@@ -523,14 +502,14 @@ function dl_progress_output($url, $read, $total, $console) {
|
||||
"$left [$dashes$spaces] $right"
|
||||
}
|
||||
|
||||
function dl_progress($read, $total, $url) {
|
||||
function Write-DownloadProgress ($read, $total, $url) {
|
||||
$console = $host.UI.RawUI;
|
||||
$left = $console.CursorPosition.X;
|
||||
$top = $console.CursorPosition.Y;
|
||||
$width = $console.BufferSize.Width;
|
||||
|
||||
if($read -eq 0) {
|
||||
$maxOutputLength = $(dl_progress_output $url 100 $total $console).length
|
||||
$maxOutputLength = $(Format-DownloadProgress $url 100 $total $console).length
|
||||
if (($left + $maxOutputLength) -gt $width) {
|
||||
# not enough room to print progress on this line
|
||||
# print on new line
|
||||
@@ -541,11 +520,11 @@ function dl_progress($read, $total, $url) {
|
||||
}
|
||||
}
|
||||
|
||||
write-host $(dl_progress_output $url $read $total $console) -nonewline
|
||||
write-host $(Format-DownloadProgress $url $read $total $console) -nonewline
|
||||
[console]::SetCursorPosition($left, $top)
|
||||
}
|
||||
|
||||
function dl_urls($app, $version, $manifest, $bucket, $architecture, $dir, $use_cache = $true, $check_hash = $true) {
|
||||
function Invoke-ScoopDownload ($app, $version, $manifest, $bucket, $architecture, $dir, $use_cache = $true, $check_hash = $true) {
|
||||
# we only want to show this warning once
|
||||
if(!$use_cache) { warn "Cache is being ignored." }
|
||||
|
||||
@@ -566,13 +545,13 @@ function dl_urls($app, $version, $manifest, $bucket, $architecture, $dir, $use_c
|
||||
|
||||
# download first
|
||||
if(Test-Aria2Enabled) {
|
||||
dl_with_cache_aria2 $app $version $manifest $architecture $dir $cookies $use_cache $check_hash
|
||||
Invoke-CachedAria2Download $app $version $manifest $architecture $dir $cookies $use_cache $check_hash
|
||||
} else {
|
||||
foreach($url in $urls) {
|
||||
$fname = url_filename $url
|
||||
|
||||
try {
|
||||
dl_with_cache $app $version $url "$dir\$fname" $cookies $use_cache
|
||||
Invoke-CachedDownload $app $version $url "$dir\$fname" $cookies $use_cache
|
||||
} catch {
|
||||
write-host -f darkred $_
|
||||
abort "URL $url is not valid"
|
||||
@@ -609,7 +588,7 @@ function dl_urls($app, $version, $manifest, $bucket, $architecture, $dir, $use_c
|
||||
$extract_fn = 'Expand-InnoArchive'
|
||||
} elseif($fname -match '\.zip$') {
|
||||
# Use 7zip when available (more fast)
|
||||
if (((get_config 7ZIPEXTRACT_USE_EXTERNAL) -and (Test-CommandAvailable 7z)) -or (Test-HelperInstalled -Helper 7zip)) {
|
||||
if (((get_config USE_EXTERNAL_7ZIP) -and (Test-CommandAvailable 7z)) -or (Test-HelperInstalled -Helper 7zip)) {
|
||||
$extract_fn = 'Expand-7zipArchive'
|
||||
} else {
|
||||
$extract_fn = 'Expand-ZipArchive'
|
||||
@@ -680,7 +659,7 @@ function hash_for_url($manifest, $url, $arch) {
|
||||
function check_hash($file, $hash, $app_name) {
|
||||
$file = fullpath $file
|
||||
if(!$hash) {
|
||||
warn "Warning: No hash in manifest. SHA256 for '$(fname $file)' is:`n $(compute_hash $file 'sha256')"
|
||||
warn "Warning: No hash in manifest. SHA256 for '$(fname $file)' is:`n $((Get-FileHash -Path $file -Algorithm SHA256).Hash.ToLower())"
|
||||
return $true, $null
|
||||
}
|
||||
|
||||
@@ -692,7 +671,7 @@ function check_hash($file, $hash, $app_name) {
|
||||
return $false, "Hash type '$algorithm' isn't supported."
|
||||
}
|
||||
|
||||
$actual = compute_hash $file $algorithm
|
||||
$actual = (Get-FileHash -Path $file -Algorithm $algorithm).Hash.ToLower()
|
||||
$expected = $expected.ToLower()
|
||||
|
||||
if($actual -ne $expected) {
|
||||
@@ -712,25 +691,6 @@ function check_hash($file, $hash, $app_name) {
|
||||
return $true, $null
|
||||
}
|
||||
|
||||
function compute_hash($file, $algname) {
|
||||
try {
|
||||
if(Test-CommandAvailable Get-FileHash) {
|
||||
return (Get-FileHash -Path $file -Algorithm $algname).Hash.ToLower()
|
||||
} else {
|
||||
$fs = [system.io.file]::openread($file)
|
||||
$alg = [system.security.cryptography.hashalgorithm]::create($algname)
|
||||
$hexbytes = $alg.computehash($fs) | ForEach-Object { $_.tostring('x2') }
|
||||
return [string]::join('', $hexbytes)
|
||||
}
|
||||
} catch {
|
||||
error $_.exception.message
|
||||
} finally {
|
||||
if($fs) { $fs.dispose() }
|
||||
if($alg) { $alg.dispose() }
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
# for dealing with installers
|
||||
function args($config, $dir, $global) {
|
||||
if($config) { return $config | ForEach-Object { (format $_ @{'dir'=$dir;'global'=$global}) } }
|
||||
@@ -743,7 +703,7 @@ function run_installer($fname, $manifest, $architecture, $dir, $global) {
|
||||
$installer = installer $manifest $architecture
|
||||
if($installer.script) {
|
||||
write-output "Running installer script..."
|
||||
Invoke-Expression (@($installer.script) -join "`r`n")
|
||||
Invoke-Command ([scriptblock]::Create($installer.script -join "`r`n"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -823,7 +783,7 @@ function run_uninstaller($manifest, $architecture, $dir) {
|
||||
$version = $manifest.version
|
||||
if($uninstaller.script) {
|
||||
write-output "Running uninstaller script..."
|
||||
Invoke-Expression (@($uninstaller.script) -join "`r`n")
|
||||
Invoke-Command ([scriptblock]::Create($uninstaller.script -join "`r`n"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -930,7 +890,7 @@ function rm_shims($app, $manifest, $global, $arch) {
|
||||
# Returns the 'current' junction directory if in use, otherwise
|
||||
# the version directory.
|
||||
function link_current($versiondir) {
|
||||
if (get_config NO_JUNCTIONS) { return $versiondir.ToString() }
|
||||
if (get_config NO_JUNCTION) { return $versiondir.ToString() }
|
||||
|
||||
$currentdir = "$(Split-Path $versiondir)\current"
|
||||
|
||||
@@ -946,7 +906,7 @@ function link_current($versiondir) {
|
||||
Remove-Item $currentdir -Recurse -Force -ErrorAction Stop
|
||||
}
|
||||
|
||||
New-Item -Path $currentdir -ItemType Junction -Value $versiondir | Out-Null
|
||||
New-DirectoryJunction $currentdir $versiondir | Out-Null
|
||||
attrib $currentdir +R /L
|
||||
return $currentdir
|
||||
}
|
||||
@@ -957,7 +917,7 @@ function link_current($versiondir) {
|
||||
# Returns the 'current' junction directory (if it exists),
|
||||
# otherwise the normal version directory.
|
||||
function unlink_current($versiondir) {
|
||||
if (get_config NO_JUNCTIONS) { return $versiondir.ToString() }
|
||||
if (get_config NO_JUNCTION) { return $versiondir.ToString() }
|
||||
$currentdir = "$(Split-Path $versiondir)\current"
|
||||
|
||||
if (Test-Path $currentdir) {
|
||||
@@ -1056,19 +1016,25 @@ function env_rm($manifest, $global, $arch) {
|
||||
}
|
||||
}
|
||||
|
||||
function pre_install($manifest, $arch) {
|
||||
$pre_install = arch_specific 'pre_install' $manifest $arch
|
||||
if($pre_install) {
|
||||
write-output "Running pre-install script..."
|
||||
Invoke-Expression (@($pre_install) -join "`r`n")
|
||||
}
|
||||
}
|
||||
function Invoke-HookScript {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('pre_install', 'post_install',
|
||||
'pre_uninstall', 'post_uninstall')]
|
||||
[String] $HookType,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[PSCustomObject] $Manifest,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('32bit', '64bit', 'arm64')]
|
||||
[String] $Arch
|
||||
)
|
||||
|
||||
function post_install($manifest, $arch) {
|
||||
$post_install = arch_specific 'post_install' $manifest $arch
|
||||
if($post_install) {
|
||||
write-output "Running post-install script..."
|
||||
Invoke-Expression (@($post_install) -join "`r`n")
|
||||
$script = arch_specific $HookType $Manifest $Arch
|
||||
if ($script) {
|
||||
Write-Output "Running $HookType script..."
|
||||
Invoke-Command ([scriptblock]::Create($script -join "`r`n"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1197,7 +1163,7 @@ function persist_data($manifest, $original_dir, $persist_dir) {
|
||||
# create link
|
||||
if (is_directory $target) {
|
||||
# target is a directory, create junction
|
||||
New-Item -Path $source -ItemType Junction -Value $target | Out-Null
|
||||
New-DirectoryJunction $source $target | Out-Null
|
||||
attrib $source +R /L
|
||||
} else {
|
||||
# target is a file, create hard link
|
||||
@@ -1246,17 +1212,30 @@ 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 {
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# wrapper function to create junction links
|
||||
# Required to handle docker/for-win#12240
|
||||
function New-DirectoryJunction($source, $target) {
|
||||
# test if this script is being executed inside a docker container
|
||||
if (Get-Service -Name cexecsvc -ErrorAction SilentlyContinue) {
|
||||
cmd.exe /d /c "mklink /j `"$source`" `"$target`""
|
||||
} else {
|
||||
New-Item -Path $source -ItemType Junction -Value $target
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
126
lib/manifest.ps1
126
lib/manifest.ps1
@@ -1,13 +1,14 @@
|
||||
. "$PSScriptRoot\core.ps1"
|
||||
. "$PSScriptRoot\autoupdate.ps1"
|
||||
|
||||
function manifest_path($app, $bucket) {
|
||||
fullpath "$(Find-BucketDirectory $bucket)\$(sanitary_path $app).json"
|
||||
(Get-ChildItem (Find-BucketDirectory $bucket) -Filter "$(sanitary_path $app).json" -Recurse).FullName
|
||||
}
|
||||
|
||||
function parse_json($path) {
|
||||
if(!(test-path $path)) { return $null }
|
||||
Get-Content $path -raw -Encoding UTF8 | convertfrom-json -ea stop
|
||||
if ($null -eq $path -or !(Test-Path $path)) { return $null }
|
||||
try {
|
||||
Get-Content $path -Raw -Encoding UTF8 | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
warn "Error parsing JSON at $path."
|
||||
}
|
||||
}
|
||||
|
||||
function url_manifest($url) {
|
||||
@@ -15,26 +16,69 @@ function url_manifest($url) {
|
||||
try {
|
||||
$wc = New-Object Net.Webclient
|
||||
$wc.Headers.Add('User-Agent', (Get-UserAgent))
|
||||
$str = $wc.downloadstring($url)
|
||||
$data = $wc.DownloadData($url)
|
||||
$str = (Get-Encoding($wc)).GetString($data)
|
||||
} catch [system.management.automation.methodinvocationexception] {
|
||||
warn "error: $($_.exception.innerexception.message)"
|
||||
} catch {
|
||||
throw
|
||||
}
|
||||
if(!$str) { return $null }
|
||||
$str | convertfrom-json
|
||||
try {
|
||||
$str | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
warn "Error parsing JSON at $url."
|
||||
}
|
||||
}
|
||||
|
||||
function Get-Manifest($app) {
|
||||
$bucket, $manifest, $url = $null
|
||||
$app = $app.TrimStart('/')
|
||||
# check if app is a URL or UNC path
|
||||
if ($app -match '^(ht|f)tps?://|\\\\') {
|
||||
$url = $app
|
||||
$app = appname_from_url $url
|
||||
$manifest = url_manifest $url
|
||||
} else {
|
||||
$app, $bucket, $version = parse_app $app
|
||||
if ($bucket) {
|
||||
$manifest = manifest $app $bucket
|
||||
} else {
|
||||
foreach ($bucket in Get-LocalBucket) {
|
||||
$manifest = manifest $app $bucket
|
||||
if ($manifest) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$manifest) {
|
||||
# couldn't find app in buckets: check if it's a local path
|
||||
$appPath = $app
|
||||
$bucket = $null
|
||||
if (!$appPath.EndsWith('.json')) {
|
||||
$appPath += '.json'
|
||||
}
|
||||
if (Test-Path $appPath) {
|
||||
$url = Convert-Path $appPath
|
||||
$app = appname_from_url $url
|
||||
$manifest = url_manifest $url
|
||||
}
|
||||
}
|
||||
}
|
||||
return $app, $manifest, $bucket, $url
|
||||
}
|
||||
|
||||
function manifest($app, $bucket, $url) {
|
||||
if($url) { return url_manifest $url }
|
||||
if ($url) { return url_manifest $url }
|
||||
parse_json (manifest_path $app $bucket)
|
||||
}
|
||||
|
||||
function save_installed_manifest($app, $bucket, $dir, $url) {
|
||||
if($url) {
|
||||
if ($url) {
|
||||
$wc = New-Object Net.Webclient
|
||||
$wc.Headers.Add('User-Agent', (Get-UserAgent))
|
||||
$wc.downloadstring($url) > "$dir\manifest.json"
|
||||
$data = $wc.DownloadData($url)
|
||||
(Get-Encoding($wc)).GetString($data) | Out-UTF8File "$dir\manifest.json"
|
||||
} else {
|
||||
Copy-Item (manifest_path $app $bucket) "$dir\manifest.json"
|
||||
}
|
||||
@@ -48,48 +92,46 @@ function save_install_info($info, $dir) {
|
||||
$nulls = $info.keys | Where-Object { $null -eq $info[$_] }
|
||||
$nulls | ForEach-Object { $info.remove($_) } # strip null-valued
|
||||
|
||||
$file_content = $info | ConvertToPrettyJson
|
||||
$file_content = $info | ConvertToPrettyJson # in 'json.ps1'
|
||||
[System.IO.File]::WriteAllLines("$dir\install.json", $file_content)
|
||||
}
|
||||
|
||||
function install_info($app, $version, $global) {
|
||||
$path = "$(versiondir $app $version $global)\install.json"
|
||||
if(!(test-path $path)) { return $null }
|
||||
if (!(Test-Path $path)) { return $null }
|
||||
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
|
||||
function arch_specific($prop, $manifest, $architecture) {
|
||||
if ($manifest.architecture) {
|
||||
$val = $manifest.architecture.$architecture.$prop
|
||||
if ($val) { return $val } # else fallback to generic prop
|
||||
}
|
||||
|
||||
if ($manifest.$prop) { return $manifest.$prop }
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
}
|
||||
|
||||
return $arch
|
||||
}
|
||||
|
||||
function arch_specific($prop, $manifest, $architecture) {
|
||||
if($manifest.architecture) {
|
||||
$val = $manifest.architecture.$architecture.$prop
|
||||
if($val) { return $val } # else fallback to generic prop
|
||||
if (![String]::IsNullOrEmpty((arch_specific 'url' $manifest $architecture))) {
|
||||
return $architecture
|
||||
}
|
||||
|
||||
if($manifest.$prop) { return $manifest.$prop }
|
||||
}
|
||||
|
||||
function supports_architecture($manifest, $architecture) {
|
||||
return -not [String]::IsNullOrEmpty((arch_specific 'url' $manifest $architecture))
|
||||
}
|
||||
|
||||
function generate_user_manifest($app, $bucket, $version) {
|
||||
$null, $manifest, $bucket, $null = Find-Manifest $app $bucket
|
||||
# 'autoupdate.ps1' 'buckets.ps1' 'manifest.ps1'
|
||||
$app, $manifest, $bucket, $null = Get-Manifest "$bucket/$app"
|
||||
if ("$($manifest.version)" -eq "$version") {
|
||||
return manifest_path $app $bucket
|
||||
}
|
||||
@@ -100,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"
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@ function install_psmodule($manifest, $dir, $global) {
|
||||
|
||||
if (Test-Path $linkfrom) {
|
||||
warn "$(friendly_path $linkfrom) already exists. It will be replaced."
|
||||
Remove-Item -Path $linkfrom -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $linkfrom -Force -Recurse -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
New-Item -Path $linkfrom -ItemType Junction -Value $dir | Out-Null
|
||||
New-DirectoryJunction $linkfrom $dir | Out-Null
|
||||
}
|
||||
|
||||
function uninstall_psmodule($manifest, $dir, $global) {
|
||||
@@ -39,8 +39,8 @@ function uninstall_psmodule($manifest, $dir, $global) {
|
||||
$linkfrom = "$modulesdir\$module_name"
|
||||
if (Test-Path $linkfrom) {
|
||||
Write-Host "Removing $(friendly_path $linkfrom)"
|
||||
$linkfrom = Resolve-Path $linkfrom
|
||||
Remove-Item -Path $linkfrom -Force -ErrorAction SilentlyContinue
|
||||
$linkfrom = Convert-Path $linkfrom
|
||||
Remove-Item -Path $linkfrom -Force -Recurse -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,34 +5,35 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
function shortcut_folder($global) {
|
||||
$directory = [System.IO.Path]::Combine([Environment]::GetFolderPath('startmenu'), 'Programs', 'Scoop Apps')
|
||||
if($global) {
|
||||
$directory = [System.IO.Path]::Combine([Environment]::GetFolderPath('commonstartmenu'), 'Programs', 'Scoop Apps')
|
||||
if ($global) {
|
||||
$startmenu = 'CommonStartMenu'
|
||||
} else {
|
||||
$startmenu = 'StartMenu'
|
||||
}
|
||||
return $(ensure $directory)
|
||||
return Convert-Path (ensure ([System.IO.Path]::Combine([Environment]::GetFolderPath($startmenu), 'Programs', 'Scoop Apps')))
|
||||
}
|
||||
|
||||
function startmenu_shortcut([System.IO.FileInfo] $target, $shortcutName, $arguments, [System.IO.FileInfo]$icon, $global) {
|
||||
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
|
||||
}
|
||||
@@ -50,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
|
||||
@@ -62,23 +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
|
||||
}
|
||||
# Before issue 1514 Startmenu shortcut removal
|
||||
#
|
||||
# Shortcuts that should have been installed globally would
|
||||
# have been installed locally up until 27 June 2017.
|
||||
#
|
||||
# TODO: Remove this 'if' block and comment after
|
||||
# 27 June 2018.
|
||||
if($global) {
|
||||
$shortcut = "$(shortcut_folder $false)\$name.lnk"
|
||||
if(Test-Path -Path $shortcut) {
|
||||
Remove-Item $shortcut
|
||||
}
|
||||
$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
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
# versions
|
||||
function Get-LatestVersion {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
@@ -29,7 +28,7 @@ function Get-LatestVersion {
|
||||
}
|
||||
}
|
||||
|
||||
function Select-CurrentVersion {
|
||||
function Select-CurrentVersion { # 'manifest.ps1'
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Select current version of installed app, from 'current\manifest.json' or modified time of version directory
|
||||
@@ -51,7 +50,7 @@ function Select-CurrentVersion {
|
||||
)
|
||||
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
|
||||
@@ -148,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++) {
|
||||
|
||||
@@ -23,9 +23,7 @@ param(
|
||||
[Switch]$verbose = $false
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1" # shim related
|
||||
|
||||
$script:config_alias = 'alias'
|
||||
|
||||
@@ -54,11 +52,11 @@ function add_alias($name, $command) {
|
||||
# generate script
|
||||
$shimdir = shimdir $false
|
||||
$script =
|
||||
@"
|
||||
# Summary: $description
|
||||
$command
|
||||
"@
|
||||
$script | Out-File "$shimdir\$alias_file.ps1" -Encoding ASCII
|
||||
@(
|
||||
"# Summary: $description",
|
||||
"$command"
|
||||
) -join "`r`n"
|
||||
$script | Out-UTF8File "$shimdir\$alias_file.ps1"
|
||||
|
||||
# add alias to config
|
||||
$aliases | Add-Member -MemberType NoteProperty -Name $name -Value $alias_file
|
||||
@@ -96,7 +94,7 @@ function list_aliases {
|
||||
}
|
||||
|
||||
if (!$aliases.count) {
|
||||
warn 'No aliases founds.'
|
||||
info "No alias found."
|
||||
}
|
||||
$aliases = $aliases.GetEnumerator() | Sort-Object Name
|
||||
if ($verbose) {
|
||||
|
||||
@@ -19,48 +19,53 @@
|
||||
# scoop bucket known
|
||||
param($cmd, $name, $repo)
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
|
||||
reset_aliases
|
||||
|
||||
$usage_add = "usage: scoop bucket add <name> [<repo>]"
|
||||
$usage_rm = "usage: scoop bucket rm <name>"
|
||||
|
||||
function list_buckets {
|
||||
$buckets = @()
|
||||
|
||||
foreach ($bucket in Get-LocalBucket) {
|
||||
$source = Find-BucketDirectory $bucket -Root
|
||||
$manifests = (
|
||||
Get-ChildItem "$source\bucket" -Force -Recurse -ErrorAction SilentlyContinue |
|
||||
Measure-Object | Select-Object -ExpandProperty Count
|
||||
)
|
||||
$updated = 'N/A'
|
||||
if ((Test-Path (Join-Path $source '.git')) -and (Get-Command git -ErrorAction SilentlyContinue)) {
|
||||
$updated = git -C $source log --format='%aD' -n 1 | Get-Date
|
||||
$source = git -C $source config remote.origin.url
|
||||
} else {
|
||||
$updated = (Get-Item "$source\bucket").LastWriteTime
|
||||
$source = friendly_path $source
|
||||
}
|
||||
$buckets += New-Object PSObject -Property @{
|
||||
Name = $bucket
|
||||
Source = $source
|
||||
Updated = $updated
|
||||
Manifests = $manifests
|
||||
}
|
||||
}
|
||||
return $buckets | Select-Object Name, Source, Updated, Manifests
|
||||
}
|
||||
$usage_add = 'usage: scoop bucket add <name> [<repo>]'
|
||||
$usage_rm = 'usage: scoop bucket rm <name>'
|
||||
|
||||
switch ($cmd) {
|
||||
'add' { add_bucket $name $repo }
|
||||
'rm' { rm_bucket $name }
|
||||
'list' { list_buckets }
|
||||
'known' { known_buckets }
|
||||
default { "scoop bucket: cmd '$cmd' not supported"; my_usage; exit 1 }
|
||||
'add' {
|
||||
if (!$name) {
|
||||
'<name> missing'
|
||||
$usage_add
|
||||
exit 1
|
||||
}
|
||||
if (!$repo) {
|
||||
$repo = known_bucket_repo $name
|
||||
if (!$repo) {
|
||||
"Unknown bucket '$name'. Try specifying <repo>."
|
||||
$usage_add
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
$status = add_bucket $name $repo
|
||||
exit $status
|
||||
}
|
||||
'rm' {
|
||||
if (!$name) {
|
||||
'<name> missing'
|
||||
$usage_rm
|
||||
exit 1
|
||||
}
|
||||
$status = rm_bucket $name
|
||||
exit $status
|
||||
}
|
||||
'list' {
|
||||
$buckets = list_buckets
|
||||
if (!$buckets.Length) {
|
||||
warn "No bucket found. Please run 'scoop bucket add main' to add the default 'main' bucket."
|
||||
exit 2
|
||||
} else {
|
||||
$buckets
|
||||
exit 0
|
||||
}
|
||||
}
|
||||
'known' {
|
||||
known_buckets
|
||||
exit 0
|
||||
}
|
||||
default {
|
||||
"scoop bucket: cmd '$cmd' not supported"
|
||||
my_usage
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
#
|
||||
# To clear everything in your cache, use:
|
||||
# scoop cache rm *
|
||||
param($cmd)
|
||||
# You can also use the `-a/--all` switch in place of `*` here
|
||||
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
param($cmd)
|
||||
|
||||
function cacheinfo($file) {
|
||||
$app, $version, $url = $file.Name -split '#'
|
||||
@@ -38,7 +38,7 @@ function cacheremove($app) {
|
||||
'ERROR: <app(s)> missing'
|
||||
my_usage
|
||||
exit 1
|
||||
} elseif ($app -eq '*') {
|
||||
} elseif ($app -eq '*' -or $app -eq '-a' -or $app -eq '--all') {
|
||||
$files = @(Get-ChildItem $cachedir)
|
||||
} else {
|
||||
$app = '(' + ($app -join '|') + ')'
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
# 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)
|
||||
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
|
||||
reset_aliases
|
||||
. "$PSScriptRoot\..\lib\json.ps1" # 'ConvertToPrettyJson'
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
|
||||
|
||||
if (!$app) { error '<app> missing'; my_usage; exit 1 }
|
||||
|
||||
$app, $bucket, $null = parse_app $app
|
||||
$app, $manifest, $bucket, $url = Find-Manifest $app $bucket
|
||||
$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 {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
# Help: Performs a series of diagnostic tests to try to identify things that may
|
||||
# cause problems with Scoop.
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\diagnostic.ps1"
|
||||
|
||||
$issues = 0
|
||||
@@ -18,6 +17,7 @@ if ($adminPrivileges) {
|
||||
|
||||
$issues += !(check_main_bucket)
|
||||
$issues += !(check_long_paths)
|
||||
$issues += !(Get-WindowsDeveloperModeStatus)
|
||||
|
||||
if (!(Test-HelperInstalled -Helper 7zip)) {
|
||||
error "'7-Zip' is not installed! It's required for unpacking most programs. Please Run 'scoop install 7zip' or 'scoop install 7zip-zstd'."
|
||||
@@ -36,13 +36,13 @@ if (!(Test-HelperInstalled -Helper Dark)) {
|
||||
|
||||
$globaldir = New-Object System.IO.DriveInfo($globaldir)
|
||||
if ($globaldir.DriveFormat -ne 'NTFS') {
|
||||
error "Scoop requires an NTFS volume to work! Please point `$env:SCOOP_GLOBAL or 'globalPath' variable in '~/.config/scoop/config.json' to another Drive."
|
||||
error "Scoop requires an NTFS volume to work! Please point `$env:SCOOP_GLOBAL or 'global_path' variable in '~/.config/scoop/config.json' to another Drive."
|
||||
$issues++
|
||||
}
|
||||
|
||||
$scoopdir = New-Object System.IO.DriveInfo($scoopdir)
|
||||
if ($scoopdir.DriveFormat -ne 'NTFS') {
|
||||
error "Scoop requires an NTFS volume to work! Please point `$env:SCOOP or 'rootPath' variable in '~/.config/scoop/config.json' to another Drive."
|
||||
error "Scoop requires an NTFS volume to work! Please point `$env:SCOOP or 'root_path' variable in '~/.config/scoop/config.json' to another Drive."
|
||||
$issues++
|
||||
}
|
||||
|
||||
|
||||
@@ -3,28 +3,25 @@
|
||||
# Help: 'scoop cleanup' cleans Scoop apps by removing old versions.
|
||||
# 'scoop cleanup <app>' cleans up the old versions of that app if said versions exist.
|
||||
#
|
||||
# You can use '*' in place of <app> to cleanup all apps.
|
||||
# You can use '*' in place of <app> or `-a`/`--all` switch to cleanup all apps.
|
||||
#
|
||||
# Options:
|
||||
# -a, --all Cleanup all apps (alternative to '*')
|
||||
# -g, --global Cleanup a globally installed app
|
||||
# -k, --cache Remove outdated download cache
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Select-CurrentVersion' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
|
||||
. "$PSScriptRoot\..\lib\install.ps1" # persist related
|
||||
|
||||
reset_aliases
|
||||
|
||||
$opt, $apps, $err = getopt $args 'gk' 'global', 'cache'
|
||||
$opt, $apps, $err = getopt $args 'agk' 'all', 'global', 'cache'
|
||||
if ($err) { "scoop cleanup: $err"; exit 1 }
|
||||
$global = $opt.g -or $opt.global
|
||||
$cache = $opt.k -or $opt.cache
|
||||
$all = $opt.a -or $opt.all
|
||||
|
||||
if (!$apps) { 'ERROR: <app> missing'; my_usage; exit 1 }
|
||||
if (!$apps -and !$all) { 'ERROR: <app> missing'; my_usage; exit 1 }
|
||||
|
||||
if ($global -and !(is_admin)) {
|
||||
'ERROR: you need admin rights to cleanup global apps'; exit 1
|
||||
@@ -64,8 +61,8 @@ function cleanup($app, $global, $verbose, $cache) {
|
||||
Write-Host ''
|
||||
}
|
||||
|
||||
if ($apps) {
|
||||
if ($apps -eq '*') {
|
||||
if ($apps -or $all) {
|
||||
if ($apps -eq '*' -or $all) {
|
||||
$verbose = $false
|
||||
$apps = applist (installed_apps $false) $false
|
||||
if ($global) {
|
||||
|
||||
@@ -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,26 +64,26 @@
|
||||
# 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.
|
||||
#
|
||||
# checkver_token:
|
||||
# gh_token:
|
||||
# GitHub API token used to make authenticated requests.
|
||||
# This is essential for checkver and similar functions
|
||||
# to run without incurring rate limits.
|
||||
# This is essential for checkver and similar functions to run without
|
||||
# incurring rate limits and download from private repositories.
|
||||
#
|
||||
# virustotal_api_key:
|
||||
# API key used for uploading/scanning files using virustotal.
|
||||
@@ -96,6 +100,21 @@
|
||||
# any target app process is running. Procedure here refers to reset/uninstall/update.
|
||||
# When set to $true, Scoop only displays a warning message and continues procedure.
|
||||
#
|
||||
# private_hosts:
|
||||
# Array of private hosts that need additional authentication.
|
||||
# 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
|
||||
# -------------------
|
||||
#
|
||||
@@ -127,27 +146,44 @@
|
||||
|
||||
param($name, $value)
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
|
||||
reset_aliases
|
||||
|
||||
if (!$name) {
|
||||
$scoopConfig
|
||||
} 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +1,28 @@
|
||||
# Usage: scoop depends <app>
|
||||
# Summary: List dependencies for an app
|
||||
# Summary: List dependencies for an app, in the order they'll be installed
|
||||
|
||||
. "$PSScriptRoot\..\lib\depends.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\decompress.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
|
||||
reset_aliases
|
||||
. "$PSScriptRoot\..\lib\depends.ps1" # 'Get-Dependency'
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest' (indirectly)
|
||||
|
||||
$opt, $apps, $err = getopt $args 'a:' 'arch='
|
||||
$app = $apps[0]
|
||||
|
||||
if(!$app) { error '<app> missing'; my_usage; exit 1 }
|
||||
|
||||
$architecture = default_architecture
|
||||
$architecture = Get-DefaultArchitecture
|
||||
try {
|
||||
$architecture = ensure_architecture ($opt.a + $opt.arch)
|
||||
$architecture = Format-ArchitectureString ($opt.a + $opt.arch)
|
||||
} catch {
|
||||
abort "ERROR: $_"
|
||||
}
|
||||
|
||||
$deps = @(Get-Dependency $app $architecture) -ne $app
|
||||
if($deps) {
|
||||
$deps[($deps.length - 1)..0]
|
||||
$deps = @()
|
||||
Get-Dependency $app $architecture | ForEach-Object {
|
||||
$dep = [ordered]@{}
|
||||
$dep.Source, $dep.Name = $_ -split '/'
|
||||
$deps += [PSCustomObject]$dep
|
||||
}
|
||||
$deps
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
# Help: e.g. The usual way to download an app, without installing it (uses your local 'buckets'):
|
||||
# scoop download git
|
||||
#
|
||||
# To download a different version of the app
|
||||
# (note that this will auto-generate the manifest using current version):
|
||||
# scoop download gh@2.7.0
|
||||
#
|
||||
# To download an app from a manifest at a URL:
|
||||
# scoop download https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/runat.json
|
||||
#
|
||||
@@ -10,26 +14,25 @@
|
||||
# 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\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
|
||||
reset_aliases
|
||||
. "$PSScriptRoot\..\lib\json.ps1" # 'autoupdate.ps1' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\autoupdate.ps1" # 'generate_user_manifest' (indirectly)
|
||||
. "$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='
|
||||
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: $_"
|
||||
}
|
||||
@@ -52,9 +55,9 @@ foreach ($curr_app in $apps) {
|
||||
$bucket = $version = $app = $manifest = $url = $null
|
||||
|
||||
$app, $bucket, $version = parse_app $curr_app
|
||||
$app, $manifest, $bucket, $url = Find-Manifest $app $bucket
|
||||
$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)) {
|
||||
@@ -82,24 +85,26 @@ 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"
|
||||
$dl_failure = $true
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -126,7 +131,9 @@ foreach ($curr_app in $apps) {
|
||||
}
|
||||
}
|
||||
|
||||
success "'$app' ($version) was downloaded successfully!"
|
||||
if (!$dl_failure) {
|
||||
success "'$app' ($version) was downloaded successfully!"
|
||||
}
|
||||
}
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -1,60 +1,23 @@
|
||||
# Usage: scoop export > filename
|
||||
# Summary: Exports (an importable) list of installed apps
|
||||
# Help: Lists all installed apps.
|
||||
# Usage: scoop export > scoopfile.json
|
||||
# Summary: Exports installed apps, buckets (and optionally configs) in JSON format
|
||||
# Help: Options:
|
||||
# -c, --config Export the Scoop configuration file too
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\json.ps1" # 'ConvertToPrettyJson'
|
||||
|
||||
reset_aliases
|
||||
$def_arch = default_architecture
|
||||
$export = @{}
|
||||
|
||||
$local = installed_apps $false | ForEach-Object { @{ name = $_; global = $false } }
|
||||
$global = installed_apps $true | ForEach-Object { @{ name = $_; global = $true } }
|
||||
|
||||
$apps = @($local) + @($global)
|
||||
$count = 0
|
||||
|
||||
# json
|
||||
# echo "{["
|
||||
|
||||
if($apps) {
|
||||
$apps | Sort-Object { $_.name } | Where-Object { !$query -or ($_.name -match $query) } | ForEach-Object {
|
||||
$app = $_.name
|
||||
$global = $_.global
|
||||
$ver = Select-CurrentVersion -AppName $app -Global:$global
|
||||
$global_display = $null; if($global) { $global_display = ' *global*'}
|
||||
|
||||
$install_info = install_info $app $ver $global
|
||||
$bucket = ''
|
||||
if ($install_info.bucket) {
|
||||
$bucket = ' [' + $install_info.bucket + ']'
|
||||
} elseif ($install_info.url) {
|
||||
$bucket = ' [' + $install_info.url + ']'
|
||||
}
|
||||
if ($install_info.architecture -and $def_arch -ne $install_info.architecture) {
|
||||
$arch = ' {' + $install_info.architecture + '}'
|
||||
} else {
|
||||
$arch = ''
|
||||
}
|
||||
|
||||
# json
|
||||
# $val = "{ 'name': '$app', 'version': '$ver', 'global': $($global.toString().tolower()) }"
|
||||
# if($count -gt 0) {
|
||||
# " ," + $val
|
||||
# } else {
|
||||
# " " + $val
|
||||
# }
|
||||
|
||||
# "$app (v:$ver) global:$($global.toString().tolower())"
|
||||
"$app (v:$ver)$global_display$bucket$arch"
|
||||
|
||||
$count++
|
||||
if ($args[0] -eq '-c' -or $args[0] -eq '--config') {
|
||||
$export.config = $scoopConfig
|
||||
# Remove machine-specific properties
|
||||
foreach ($prop in 'last_update', 'root_path', 'global_path', 'cache_path', 'alias') {
|
||||
$export.config.PSObject.Properties.Remove($prop)
|
||||
}
|
||||
}
|
||||
|
||||
# json
|
||||
# echo "]}"
|
||||
$export.buckets = list_buckets
|
||||
$export.apps = @(& "$PSScriptRoot\scoop-list.ps1" 6>$null)
|
||||
|
||||
$export | ConvertToPrettyJSON
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -2,49 +2,43 @@
|
||||
# Summary: Show help for a command
|
||||
param($cmd)
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\commands.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
|
||||
reset_aliases
|
||||
|
||||
function print_help($cmd) {
|
||||
$file = Get-Content (command_path $cmd) -raw
|
||||
$file = Get-Content (command_path $cmd) -Raw
|
||||
|
||||
$usage = usage $file
|
||||
$summary = summary $file
|
||||
$help = scoop_help $file
|
||||
|
||||
if($usage) { "$usage`n" }
|
||||
if($help) { $help }
|
||||
if ($usage) { "$usage`n" }
|
||||
if ($help) { $help }
|
||||
}
|
||||
|
||||
function print_summaries {
|
||||
$commands = @{}
|
||||
$commands = @()
|
||||
|
||||
command_files | ForEach-Object {
|
||||
$command = command_name $_
|
||||
$summary = summary (Get-Content (command_path $command) -raw)
|
||||
if(!($summary)) { $summary = '' }
|
||||
$commands.add("$command ", $summary) # add padding
|
||||
$command = [ordered]@{}
|
||||
$command.Command = command_name $_
|
||||
$command.Summary = summary (Get-Content (command_path $command.Command))
|
||||
$commands += [PSCustomObject]$command
|
||||
}
|
||||
|
||||
$commands.getenumerator() | Sort-Object name | Format-Table -hidetablehead -autosize -wrap
|
||||
$commands
|
||||
}
|
||||
|
||||
$commands = commands
|
||||
|
||||
if(!($cmd)) {
|
||||
"Usage: scoop <command> [<args>]
|
||||
Write-Host "Usage: scoop <command> [<args>]
|
||||
|
||||
Some useful commands are:"
|
||||
Available commands are listed below.
|
||||
|
||||
Type 'scoop help <command>' to get more help for a specific command."
|
||||
print_summaries
|
||||
"Type 'scoop help <command>' to get help for a specific command."
|
||||
} elseif($commands -contains $cmd) {
|
||||
print_help $cmd
|
||||
} else {
|
||||
"scoop help: no such command '$cmd'"; exit 1
|
||||
warn "scoop help: no such command '$cmd'"
|
||||
exit 1
|
||||
}
|
||||
|
||||
exit 0
|
||||
|
||||
|
||||
@@ -1,28 +1,53 @@
|
||||
# Usage: scoop hold <apps>
|
||||
# Summary: Hold an app to disable updates
|
||||
# Help: To hold a user-scoped app:
|
||||
# scoop hold <app>
|
||||
#
|
||||
# To hold a global app:
|
||||
# scoop hold -g <app>
|
||||
#
|
||||
# Options:
|
||||
# -g, --global Hold globally installed apps
|
||||
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\json.ps1" # 'save_install_info' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'install_info' 'Select-CurrentVersion' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
|
||||
|
||||
reset_aliases
|
||||
$apps = $args
|
||||
$opt, $apps, $err = getopt $args 'g' 'global'
|
||||
if ($err) { "scoop hold: $err"; exit 1 }
|
||||
|
||||
if(!$apps) {
|
||||
$global = $opt.g -or $opt.global
|
||||
|
||||
if (!$apps) {
|
||||
my_usage
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($global -and !(is_admin)) {
|
||||
error 'You need admin rights to hold a global app.'
|
||||
exit 1
|
||||
}
|
||||
|
||||
$apps | ForEach-Object {
|
||||
$app = $_
|
||||
$global = installed $app $true
|
||||
|
||||
if (!(installed $app)) {
|
||||
error "'$app' is not installed."
|
||||
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."
|
||||
} else {
|
||||
error "'$app' is not installed."
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (get_config NO_JUNCTIONS) {
|
||||
if (get_config NO_JUNCTION){
|
||||
$version = Select-CurrentVersion -App $app -Global:$global
|
||||
} else {
|
||||
$version = 'current'
|
||||
@@ -30,7 +55,7 @@ $apps | ForEach-Object {
|
||||
$dir = versiondir $app $version $global
|
||||
$json = install_info $app $version $global
|
||||
$install = @{}
|
||||
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name))}
|
||||
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }
|
||||
$install.hold = $true
|
||||
save_install_info $install $dir
|
||||
success "$app is now held and can not be updated anymore."
|
||||
|
||||
@@ -2,24 +2,22 @@
|
||||
# Summary: Opens the app homepage
|
||||
param($app)
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
|
||||
|
||||
reset_aliases
|
||||
|
||||
if($app) {
|
||||
$manifest, $bucket = find_manifest $app
|
||||
if($manifest) {
|
||||
if([string]::isnullorempty($manifest.homepage)) {
|
||||
if ($app) {
|
||||
$null, $manifest, $bucket, $null = Get-Manifest $app
|
||||
if ($manifest) {
|
||||
if ($manifest.homepage) {
|
||||
Start-Process $manifest.homepage
|
||||
} else {
|
||||
abort "Could not find homepage in manifest for '$app'."
|
||||
}
|
||||
Start-Process $manifest.homepage
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
abort "Could not find manifest for '$app'."
|
||||
}
|
||||
} else { my_usage }
|
||||
} else {
|
||||
my_usage
|
||||
exit 1
|
||||
}
|
||||
|
||||
exit 0
|
||||
|
||||
66
libexec/scoop-import.ps1
Normal file
66
libexec/scoop-import.ps1
Normal file
@@ -0,0 +1,66 @@
|
||||
# Usage: scoop import <path/url to scoopfile.json>
|
||||
# Summary: Imports apps, buckets and configs from a Scoopfile in JSON format
|
||||
# Help: To replicate a Scoop installation from a file stored on Desktop, run
|
||||
# scoop import Desktop\scoopfile.json
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[String]
|
||||
$scoopfile
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
|
||||
$import = $null
|
||||
$bucket_names = @()
|
||||
$def_arch = Get-DefaultArchitecture
|
||||
|
||||
if (Test-Path $scoopfile) {
|
||||
$import = parse_json $scoopfile
|
||||
} elseif ($scoopfile -match '^(ht|f)tps?://|\\\\') {
|
||||
$import = url_manifest $scoopfile
|
||||
}
|
||||
|
||||
if (!$import) { abort 'Input file not a valid JSON.' }
|
||||
|
||||
foreach ($item in $import.config.PSObject.Properties) {
|
||||
set_config $item.Name $item.Value | Out-Null
|
||||
Write-Host "'$($item.Name)' has been set to '$($item.Value)'"
|
||||
}
|
||||
|
||||
foreach ($item in $import.buckets) {
|
||||
add_bucket $item.Name $item.Source | Out-Null
|
||||
$bucket_names += $item.Name
|
||||
}
|
||||
|
||||
foreach ($item in $import.apps) {
|
||||
$info = $item.Info -Split ', '
|
||||
$global = if ('Global install' -in $info) {
|
||||
' --global'
|
||||
} else {
|
||||
''
|
||||
}
|
||||
$arch = if ('64bit' -in $info -and '64bit' -ne $def_arch) {
|
||||
' --arch 64bit'
|
||||
} elseif ('32bit' -in $info -and '32bit' -ne $def_arch) {
|
||||
' --arch 32bit'
|
||||
} elseif ('arm64' -in $info -and 'arm64' -ne $def_arch) {
|
||||
' --arch arm64'
|
||||
} else {
|
||||
''
|
||||
}
|
||||
|
||||
$app = if ($item.Source -in $bucket_names) {
|
||||
"$($item.Source)/$($item.Name)"
|
||||
} elseif ($item.Source -eq '<auto-generated>') {
|
||||
"$($item.Name)@$($item.Version)"
|
||||
} else {
|
||||
$item.Source
|
||||
}
|
||||
|
||||
& "$PSScriptRoot\scoop-install.ps1" $app$global$arch
|
||||
|
||||
if ('Held package' -in $info) {
|
||||
& "$PSScriptRoot\scoop-hold.ps1" $($item.Name)$global
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,9 @@
|
||||
# Options:
|
||||
# -v, --verbose Show full paths and URLs
|
||||
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
|
||||
reset_aliases
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
|
||||
. "$PSScriptRoot\..\lib\versions.ps1" # 'Get-InstalledVersion'
|
||||
|
||||
$opt, $app, $err = getopt $args 'v' 'verbose'
|
||||
if ($err) { error "scoop info: $err"; exit 1 }
|
||||
@@ -17,31 +13,21 @@ $verbose = $opt.v -or $opt.verbose
|
||||
|
||||
if (!$app) { my_usage; exit 1 }
|
||||
|
||||
if ($app -match '^(ht|f)tps?://|\\\\') {
|
||||
# check if $app is a URL or UNC path
|
||||
$url = $app
|
||||
$app = appname_from_url $url
|
||||
$global = installed $app $true
|
||||
$status = app_status $app $global
|
||||
$manifest = url_manifest $url
|
||||
$manifest_file = $url
|
||||
} else {
|
||||
# else $app is a normal app name
|
||||
$global = installed $app $true
|
||||
$app, $bucket, $null = parse_app $app
|
||||
$status = app_status $app $global
|
||||
$app, $manifest, $bucket, $url = Find-Manifest $app $bucket
|
||||
}
|
||||
$app, $manifest, $bucket, $url = Get-Manifest $app
|
||||
|
||||
if (!$manifest) {
|
||||
abort "Could not find manifest for '$(show_app $app $bucket)'."
|
||||
abort "Could not find manifest for '$(show_app $app)' in local buckets."
|
||||
}
|
||||
|
||||
$global = installed $app $true
|
||||
$status = app_status $app $global
|
||||
$install = install_info $app $status.version $global
|
||||
$status.installed = $bucket -and $install.bucket -eq $bucket
|
||||
$version_output = $manifest.version
|
||||
if (!$manifest_file) {
|
||||
$manifest_file = if ($bucket) { manifest_path $app $bucket } else { $url }
|
||||
$manifest_file = if ($bucket) {
|
||||
manifest_path $app $bucket
|
||||
} else {
|
||||
$url
|
||||
}
|
||||
|
||||
if ($verbose) {
|
||||
@@ -98,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
|
||||
@@ -119,6 +105,85 @@ if ($status.installed) {
|
||||
$installed_output += if ($verbose) { versiondir $app $_ $global } else { "$_$(if ($global) { " *global*" })" }
|
||||
}
|
||||
$item.Installed = $installed_output -join "`n"
|
||||
|
||||
if ($verbose) {
|
||||
# Show size of installation
|
||||
$appsdir = appsdir $global
|
||||
|
||||
# Collect file list from each location
|
||||
$appFiles = Get-ChildItem $appsdir -Filter $app
|
||||
$currentFiles = Get-ChildItem $appFiles -Filter (Select-CurrentVersion $app $global)
|
||||
$persistFiles = Get-ChildItem $persist_dir -ErrorAction Ignore # Will fail if app does not persist data
|
||||
$cacheFiles = Get-ChildItem $cachedir -Filter "$app#*"
|
||||
|
||||
# Get the sum of each file list
|
||||
$fileTotals = @()
|
||||
foreach ($fileType in ($appFiles, $currentFiles, $persistFiles, $cacheFiles)) {
|
||||
if ($null -ne $fileType) {
|
||||
$fileSum = (Get-ChildItem $fileType -Recurse | Measure-Object -Property Length -Sum).Sum
|
||||
$fileTotals += coalesce $fileSum 0
|
||||
} else {
|
||||
$fileTotals += 0
|
||||
}
|
||||
}
|
||||
|
||||
# Old versions = app total - current version size
|
||||
$fileTotals += $fileTotals[0] - $fileTotals[1]
|
||||
|
||||
if ($fileTotals[2] + $fileTotals[3] + $fileTotals[4] -eq 0) {
|
||||
# Simple app size output if no old versions, persisted data, cached downloads
|
||||
$item.'Installed size' = filesize $fileTotals[1]
|
||||
} else {
|
||||
$fileSizes = [ordered] @{
|
||||
'Current version: ' = $fileTotals[1]
|
||||
'Old versions: ' = $fileTotals[4]
|
||||
'Persisted data: ' = $fileTotals[2]
|
||||
'Cached downloads: ' = $fileTotals[3]
|
||||
'Total: ' = $fileTotals[0] + $fileTotals[2] + $fileTotals[3]
|
||||
}
|
||||
|
||||
$fileSizeOutput = @()
|
||||
|
||||
# Don't output empty categories
|
||||
$fileSizes.GetEnumerator() | ForEach-Object {
|
||||
if ($_.Value -ne 0) {
|
||||
$fileSizeOutput += $_.Key + (filesize $_.Value)
|
||||
}
|
||||
}
|
||||
|
||||
$item.'Installed size' = $fileSizeOutput -join "`n"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($verbose) {
|
||||
# Get download size if app not installed
|
||||
$totalPackage = 0
|
||||
foreach ($url in @(url $manifest (Get-DefaultArchitecture))) {
|
||||
try {
|
||||
if (Test-Path (fullpath (cache_path $app $manifest.version $url))) {
|
||||
$cached = " (latest version is cached)"
|
||||
} else {
|
||||
$cached = $null
|
||||
}
|
||||
|
||||
[int]$urlLength = (Invoke-WebRequest $url -Method Head).Headers.'Content-Length'[0]
|
||||
$totalPackage += $urlLength
|
||||
} catch [System.Management.Automation.RuntimeException] {
|
||||
$totalPackage = 0
|
||||
$packageError = "the server at $(([System.Uri]$url).Host) did not send a Content-Length header"
|
||||
break
|
||||
} catch {
|
||||
$totalPackage = 0
|
||||
$packageError = "the server at $(([System.Uri]$url).Host) is down"
|
||||
break
|
||||
}
|
||||
}
|
||||
if ($totalPackage -ne 0) {
|
||||
$item.'Download size' = "$(filesize $totalPackage)$cached"
|
||||
} else {
|
||||
$item.'Download size' = "Unknown ($packageError)$cached"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$binaries = @(arch_specific 'bin' $manifest $install.architecture)
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
# Help: e.g. The usual way to install an app (uses your local 'buckets'):
|
||||
# scoop install git
|
||||
#
|
||||
# To install a different version of the app
|
||||
# (note that this will auto-generate the manifest using current version):
|
||||
# scoop install gh@2.7.0
|
||||
#
|
||||
# To install an app from a manifest at a URL:
|
||||
# scoop install https://raw.githubusercontent.com/ScoopInstaller/Main/master/bucket/runat.json
|
||||
#
|
||||
@@ -10,27 +14,24 @@
|
||||
# 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\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\decompress.ps1"
|
||||
. "$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" # 'generate_user_manifest' 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\decompress.ps1"
|
||||
. "$PSScriptRoot\..\lib\shortcuts.ps1"
|
||||
. "$PSScriptRoot\..\lib\psmodules.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\depends.ps1"
|
||||
|
||||
reset_aliases
|
||||
|
||||
$opt, $apps, $err = getopt $args 'gikusa:' 'global', 'independent', 'no-cache', 'no-update-scoop', 'skip', 'arch='
|
||||
if ($err) { "scoop install: $err"; exit 1 }
|
||||
|
||||
@@ -38,9 +39,9 @@ $global = $opt.g -or $opt.global
|
||||
$check_hash = !($opt.s -or $opt.skip)
|
||||
$independent = $opt.i -or $opt.independent
|
||||
$use_cache = !($opt.k -or $opt.'no-cache')
|
||||
$architecture = default_architecture
|
||||
$architecture = Get-DefaultArchitecture
|
||||
try {
|
||||
$architecture = ensure_architecture ($opt.a + $opt.arch)
|
||||
$architecture = Format-ArchitectureString ($opt.a + $opt.arch)
|
||||
} catch {
|
||||
abort "ERROR: $_"
|
||||
}
|
||||
|
||||
@@ -3,13 +3,10 @@
|
||||
# Help: Lists all installed apps, or the apps matching the supplied query.
|
||||
param($query)
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'parse_json' 'Select-CurrentVersion' (indirectly)
|
||||
|
||||
reset_aliases
|
||||
$def_arch = default_architecture
|
||||
$def_arch = Get-DefaultArchitecture
|
||||
if (-not (Get-FormatData ScoopApps)) {
|
||||
Update-FormatData "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
|
||||
}
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
# Summary: Returns the path to the specified app
|
||||
param($app)
|
||||
|
||||
. "$PSScriptRoot\..\lib\versions.ps1" # 'currentdir' (indirectly)
|
||||
|
||||
if (!$app) {
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
my_usage
|
||||
exit 1
|
||||
}
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
|
||||
$app_path = currentdir $app $false
|
||||
if (!(Test-Path $app_path)) {
|
||||
$app_path = currentdir $app$true
|
||||
$app_path = currentdir $app $true
|
||||
}
|
||||
|
||||
if (Test-Path $app_path) {
|
||||
|
||||
@@ -3,22 +3,22 @@
|
||||
# Help: Used to resolve conflicts in favor of a particular app. For example,
|
||||
# if you've installed 'python' and 'python27', you can use 'scoop reset' to switch between
|
||||
# using one or the other.
|
||||
#
|
||||
# You can use '*' in place of <app> or `-a`/`--all` switch to reset all apps.
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Select-CurrentVersion' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
|
||||
. "$PSScriptRoot\..\lib\shortcuts.ps1"
|
||||
|
||||
reset_aliases
|
||||
$opt, $apps, $err = getopt $args
|
||||
$opt, $apps, $err = getopt $args 'a' 'all'
|
||||
if($err) { "scoop reset: $err"; exit 1 }
|
||||
$all = $opt.a -or $opt.all
|
||||
|
||||
if(!$apps) { error '<app> missing'; my_usage; exit 1 }
|
||||
if(!$apps -and !$all) { error '<app> missing'; my_usage; exit 1 }
|
||||
|
||||
if($apps -eq '*') {
|
||||
if($apps -eq '*' -or $all) {
|
||||
$local = installed_apps $false | ForEach-Object { ,@($_, $false) }
|
||||
$global = installed_apps $true | ForEach-Object { ,@($_, $true) }
|
||||
$apps = @($local) + @($global)
|
||||
@@ -63,7 +63,7 @@ $apps | ForEach-Object {
|
||||
|
||||
write-host "Resetting $app ($version)."
|
||||
|
||||
$dir = resolve-path (versiondir $app $version $global)
|
||||
$dir = Convert-Path (versiondir $app $version $global)
|
||||
$original_dir = $dir
|
||||
$persist_dir = persistdir $app $global
|
||||
|
||||
|
||||
@@ -5,42 +5,47 @@
|
||||
# If used with [query], shows app names that match the query.
|
||||
# Without [query], shows all the available apps.
|
||||
param($query)
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
|
||||
reset_aliases
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'manifest'
|
||||
. "$PSScriptRoot\..\lib\versions.ps1" # 'Get-LatestVersion'
|
||||
|
||||
$list = @()
|
||||
|
||||
try {
|
||||
$query = New-Object Regex $query, 'IgnoreCase'
|
||||
} catch {
|
||||
abort "Invalid regular expression: $($_.Exception.InnerException.Message)"
|
||||
}
|
||||
|
||||
$githubtoken = Get-GitHubToken
|
||||
$authheader = @{}
|
||||
if ($githubtoken) {
|
||||
$authheader = @{'Authorization' = "token $githubtoken"}
|
||||
}
|
||||
|
||||
function bin_match($manifest, $query) {
|
||||
if(!$manifest.bin) { return $false }
|
||||
foreach($bin in $manifest.bin) {
|
||||
if (!$manifest.bin) { return $false }
|
||||
$bins = foreach ($bin in $manifest.bin) {
|
||||
$exe, $alias, $args = $bin
|
||||
$fname = split-path $exe -leaf -ea stop
|
||||
$fname = Split-Path $exe -Leaf -ErrorAction Stop
|
||||
|
||||
if((strip_ext $fname) -match $query) { return $fname }
|
||||
if($alias -match $query) { return $alias }
|
||||
if ((strip_ext $fname) -match $query) { $fname }
|
||||
elseif ($alias -match $query) { $alias }
|
||||
}
|
||||
$false
|
||||
if ($bins) { return $bins }
|
||||
else { return $false }
|
||||
}
|
||||
|
||||
function search_bucket($bucket, $query) {
|
||||
$apps = apps_in_bucket (Find-BucketDirectory $bucket) | ForEach-Object {
|
||||
@{ name = $_ }
|
||||
}
|
||||
|
||||
if($query) {
|
||||
try {
|
||||
$query = new-object regex $query, 'IgnoreCase'
|
||||
} catch {
|
||||
abort "Invalid regular expression: $($_.exception.innerexception.message)"
|
||||
}
|
||||
$apps = apps_in_bucket (Find-BucketDirectory $bucket) | ForEach-Object { @{ name = $_ } }
|
||||
|
||||
if ($query) {
|
||||
$apps = $apps | Where-Object {
|
||||
if($_.name -match $query) { return $true }
|
||||
if ($_.name -match $query) { return $true }
|
||||
$bin = bin_match (manifest $_.name $bucket) $query
|
||||
if($bin) {
|
||||
$_.bin = $bin; return $true;
|
||||
if ($bin) {
|
||||
$_.bin = $bin
|
||||
return $true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,14 +54,19 @@ function search_bucket($bucket, $query) {
|
||||
|
||||
function download_json($url) {
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
$result = Invoke-WebRequest $url -UseBasicParsing | Select-Object -ExpandProperty content | ConvertFrom-Json
|
||||
$result = Invoke-WebRequest $url -UseBasicParsing -Headers $authheader | Select-Object -ExpandProperty content | ConvertFrom-Json
|
||||
$ProgressPreference = 'Continue'
|
||||
$result
|
||||
}
|
||||
|
||||
function github_ratelimit_reached {
|
||||
$api_link = 'https://api.github.com/rate_limit'
|
||||
(download_json $api_link).rate.remaining -eq 0
|
||||
$ret = (download_json $api_link).rate.remaining -eq 0
|
||||
if ($ret) {
|
||||
Write-Host "GitHub API rate limit reached.
|
||||
Please try again later or configure your API token using 'scoop config gh_token <your token>'."
|
||||
}
|
||||
$ret
|
||||
}
|
||||
|
||||
function search_remote($bucket, $query) {
|
||||
@@ -66,7 +76,7 @@ function search_remote($bucket, $query) {
|
||||
$repo_name = $Matches[2]
|
||||
$api_link = "https://api.github.com/repos/$user/$repo_name/git/trees/HEAD?recursive=1"
|
||||
$result = download_json $api_link | Select-Object -ExpandProperty tree |
|
||||
Where-Object -Value "^(?:bucket/)?(.*$query.*)\.json$" -Property Path -Match |
|
||||
Where-Object -Value "^bucket/(.*$query.*)\.json$" -Property Path -Match |
|
||||
ForEach-Object { $Matches[1] }
|
||||
}
|
||||
|
||||
@@ -75,44 +85,59 @@ function search_remote($bucket, $query) {
|
||||
|
||||
function search_remotes($query) {
|
||||
$buckets = known_bucket_repos
|
||||
$names = $buckets | get-member -m noteproperty | Select-Object -exp name
|
||||
$names = $buckets | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name
|
||||
|
||||
$results = $names | Where-Object { !(test-path $(Find-BucketDirectory $_)) } | ForEach-Object {
|
||||
@{"bucket" = $_; "results" = (search_remote $_ $query)}
|
||||
$results = $names | Where-Object { !(Test-Path $(Find-BucketDirectory $_)) } | ForEach-Object {
|
||||
@{ "bucket" = $_; "results" = (search_remote $_ $query) }
|
||||
} | Where-Object { $_.results }
|
||||
|
||||
if ($results.count -gt 0) {
|
||||
"Results from other known buckets..."
|
||||
"(add them using 'scoop bucket add <name>')"
|
||||
""
|
||||
Write-Host "Results from other known buckets...
|
||||
(add them using 'scoop bucket add <bucket name>')"
|
||||
}
|
||||
|
||||
$results | ForEach-Object {
|
||||
"'$($_.bucket)' bucket (install using 'scoop install $($_.bucket)/<app>'):"
|
||||
$_.results | ForEach-Object { " $_" }
|
||||
""
|
||||
$name = $_.bucket
|
||||
$_.results | ForEach-Object {
|
||||
$item = [ordered]@{}
|
||||
$item.Name = $_
|
||||
$item.Source = $name
|
||||
$list += [PSCustomObject]$item
|
||||
}
|
||||
}
|
||||
|
||||
$list
|
||||
}
|
||||
|
||||
Get-LocalBucket | ForEach-Object {
|
||||
$res = search_bucket $_ $query
|
||||
$local_results = $local_results -or $res
|
||||
if($res) {
|
||||
if ($res) {
|
||||
$name = "$_"
|
||||
|
||||
Write-Host "'$name' bucket:"
|
||||
$res | ForEach-Object {
|
||||
$item = " $($_.name) ($($_.version))"
|
||||
if($_.bin) { $item += " --> includes '$($_.bin)'" }
|
||||
$item
|
||||
$item = [ordered]@{}
|
||||
$item.Name = $_.name
|
||||
$item.Version = $_.version
|
||||
$item.Source = $name
|
||||
$item.Binaries = ""
|
||||
if ($_.bin) { $item.Binaries = $_.bin -join ' | ' }
|
||||
$list += [PSCustomObject]$item
|
||||
}
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
if ($list.Length -gt 0) {
|
||||
Write-Host "Results from local buckets..."
|
||||
$list
|
||||
}
|
||||
|
||||
if (!$local_results -and !(github_ratelimit_reached)) {
|
||||
$remote_results = search_remotes $query
|
||||
if(!$remote_results) { [console]::error.writeline("No matches found."); exit 1 }
|
||||
if (!$remote_results) {
|
||||
warn "No matches found."
|
||||
exit 1
|
||||
}
|
||||
$remote_results
|
||||
}
|
||||
|
||||
|
||||
@@ -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 costom shim, use the 'add' subcommand:
|
||||
# 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,59 +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\help.ps1"
|
||||
. "$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)) {
|
||||
@@ -109,22 +88,18 @@ function Get-ShimPath($ShimName, $Global) {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ShimTarget($ShimPath) {
|
||||
if ($ShimPath) {
|
||||
$shimTarget = if ($ShimPath.EndsWith('.shim')) {
|
||||
(Get-Content -Path $ShimPath | Select-Object -First 1).Replace('path = ', '').Replace('"', '')
|
||||
} else {
|
||||
((Select-String -Path $ShimPath -Pattern '^(?:@rem|#)\s*(.*)$').Matches.Groups | Select-Object -Index 1).Value
|
||||
}
|
||||
if (!$shimTarget) {
|
||||
$shimTarget = ((Select-String -Path $ShimPath -Pattern '[''"]([^@&]*?)[''"]' -AllMatches).Matches.Groups | Select-Object -Last 1).Value
|
||||
}
|
||||
$shimTarget | Convert-Path
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -141,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 {
|
||||
@@ -154,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
|
||||
@@ -226,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,104 +1,83 @@
|
||||
# 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\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\depends.ps1"
|
||||
|
||||
reset_aliases
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'manifest' 'parse_json' "install_info"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
|
||||
|
||||
# check if scoop needs updating
|
||||
$currentdir = fullpath $(versiondir 'scoop' 'current')
|
||||
$needs_update = $false
|
||||
|
||||
if(Test-Path "$currentdir\.git") {
|
||||
git_cmd -C "`"$currentdir`"" fetch -q origin
|
||||
$commits = $(git -C $currentdir log "HEAD..origin/$(scoop config SCOOP_BRANCH)" --oneline)
|
||||
if($commits) { $needs_update = $true }
|
||||
}
|
||||
else {
|
||||
$needs_update = $true
|
||||
$bucket_needs_update = $false
|
||||
$script:network_failure = $false
|
||||
$no_remotes = $args[0] -eq '-l' -or $args[0] -eq '--local'
|
||||
if (!(Get-Command git -ErrorAction SilentlyContinue)) { $no_remotes = $true }
|
||||
$list = @()
|
||||
if (!(Get-FormatData ScoopStatus)) {
|
||||
Update-FormatData "$PSScriptRoot\..\supporting\formats\ScoopTypes.Format.ps1xml"
|
||||
}
|
||||
|
||||
if($needs_update) {
|
||||
warn "Scoop is out of date. Run 'scoop update' to get the latest changes."
|
||||
function Test-UpdateStatus($repopath) {
|
||||
if (Test-Path "$repopath\.git") {
|
||||
Invoke-Git -Path $repopath -ArgumentList @('fetch', '-q', 'origin')
|
||||
$script:network_failure = 128 -eq $LASTEXITCODE
|
||||
$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 {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
else { success "Scoop is up to date."}
|
||||
|
||||
$failed = @()
|
||||
$outdated = @()
|
||||
$removed = @()
|
||||
$missing_deps = @()
|
||||
$onhold = @()
|
||||
if (!$no_remotes) {
|
||||
$needs_update = Test-UpdateStatus $currentdir
|
||||
foreach ($bucket in Get-LocalBucket) {
|
||||
if (Test-UpdateStatus (Find-BucketDirectory $bucket -Root)) {
|
||||
$bucket_needs_update = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($needs_update) {
|
||||
warn "Scoop out of date. Run 'scoop update' to get the latest changes."
|
||||
} elseif ($bucket_needs_update) {
|
||||
warn "Scoop bucket(s) out of date. Run 'scoop update' to get the latest changes."
|
||||
} elseif (!$script:network_failure -and !$no_remotes) {
|
||||
success 'Scoop is up to date.'
|
||||
}
|
||||
|
||||
$true, $false | ForEach-Object { # local and global apps
|
||||
$global = $_
|
||||
$dir = appsdir $global
|
||||
if(!(Test-Path $dir)) { return }
|
||||
if (!(Test-Path $dir)) { return }
|
||||
|
||||
Get-ChildItem $dir | Where-Object name -ne 'scoop' | ForEach-Object {
|
||||
Get-ChildItem $dir | Where-Object name -NE 'scoop' | ForEach-Object {
|
||||
$app = $_.name
|
||||
$status = app_status $app $global
|
||||
if($status.failed) {
|
||||
$failed += @{ $app = $status.version }
|
||||
}
|
||||
if($status.removed) {
|
||||
$removed += @{ $app = $status.version }
|
||||
}
|
||||
if($status.outdated) {
|
||||
$outdated += @{ $app = @($status.version, $status.latest_version) }
|
||||
if($status.hold) {
|
||||
$onhold += @{ $app = @($status.version, $status.latest_version) }
|
||||
}
|
||||
}
|
||||
if($status.missing_deps) {
|
||||
$missing_deps += ,(@($app) + @($status.missing_deps))
|
||||
}
|
||||
if (!$status.outdated -and !$status.failed -and !$status.removed -and !$status.missing_deps) { return }
|
||||
|
||||
$item = [ordered]@{}
|
||||
$item.Name = $app
|
||||
$item.'Installed Version' = $status.version
|
||||
$item.'Latest Version' = if ($status.outdated) { $status.latest_version } else { "" }
|
||||
$item.'Missing Dependencies' = $status.missing_deps -Split ' ' -Join ' | '
|
||||
$info = @()
|
||||
if ($status.failed) { $info += 'Install failed' }
|
||||
if ($status.hold) { $info += 'Held package' }
|
||||
if ($status.removed) { $info += 'Manifest removed' }
|
||||
$item.Info = $info -join ', '
|
||||
$list += [PSCustomObject]$item
|
||||
}
|
||||
}
|
||||
|
||||
if($outdated) {
|
||||
Write-Host -f DarkCyan 'Updates are available for:'
|
||||
$outdated.keys | ForEach-Object {
|
||||
$versions = $outdated.$_
|
||||
" $_`: $($versions[0]) -> $($versions[1])"
|
||||
}
|
||||
if ($list.Length -eq 0 -and !$needs_update -and !$bucket_needs_update -and !$script:network_failure) {
|
||||
success 'Everything is ok!'
|
||||
}
|
||||
|
||||
if($onhold) {
|
||||
Write-Host -f DarkCyan 'These apps are outdated and on hold:'
|
||||
$onhold.keys | ForEach-Object {
|
||||
$versions = $onhold.$_
|
||||
" $_`: $($versions[0]) -> $($versions[1])"
|
||||
}
|
||||
}
|
||||
|
||||
if($removed) {
|
||||
Write-Host -f DarkCyan 'These app manifests have been removed:'
|
||||
$removed.keys | ForEach-Object {
|
||||
" $_"
|
||||
}
|
||||
}
|
||||
|
||||
if($failed) {
|
||||
Write-Host -f DarkCyan 'These apps failed to install:'
|
||||
$failed.keys | ForEach-Object {
|
||||
" $_"
|
||||
}
|
||||
}
|
||||
|
||||
if($missing_deps) {
|
||||
Write-Host -f DarkCyan 'Missing runtime dependencies:'
|
||||
$missing_deps | ForEach-Object {
|
||||
$app, $deps = $_
|
||||
" '$app' requires '$([string]::join("', '", $deps))'"
|
||||
}
|
||||
}
|
||||
|
||||
if(!$old -and !$removed -and !$failed -and !$missing_deps -and !$needs_update) {
|
||||
success "Everything is ok!"
|
||||
}
|
||||
$list | Add-Member -TypeName ScoopStatus -PassThru
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -1,28 +1,52 @@
|
||||
# Usage: scoop unhold <app>
|
||||
# Summary: Unhold an app to enable updates
|
||||
# Help: To unhold a user-scoped app:
|
||||
# scoop unhold <app>
|
||||
#
|
||||
# To unhold a global app:
|
||||
# scoop unhold -g <app>
|
||||
#
|
||||
# Options:
|
||||
# -g, --global Unhold globally installed apps
|
||||
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\json.ps1" # 'save_install_info' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'install_info' 'Select-CurrentVersion' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
|
||||
|
||||
reset_aliases
|
||||
$apps = $args
|
||||
$opt, $apps, $err = getopt $args 'g' 'global'
|
||||
if ($err) { "scoop unhold: $err"; exit 1 }
|
||||
|
||||
if(!$apps) {
|
||||
$global = $opt.g -or $opt.global
|
||||
|
||||
if (!$apps) {
|
||||
my_usage
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($global -and !(is_admin)) {
|
||||
error 'You need admin rights to unhold a global app.'
|
||||
exit 1
|
||||
}
|
||||
|
||||
$apps | ForEach-Object {
|
||||
$app = $_
|
||||
$global = installed $app $true
|
||||
|
||||
if (!(installed $app)) {
|
||||
error "'$app' is not installed."
|
||||
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."
|
||||
} else {
|
||||
error "'$app' is not installed."
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (get_config NO_JUNCTIONS) {
|
||||
if (get_config NO_JUNCTION){
|
||||
$version = Select-CurrentVersion -App $app -Global:$global
|
||||
} else {
|
||||
$version = 'current'
|
||||
@@ -30,7 +54,7 @@ $apps | ForEach-Object {
|
||||
$dir = versiondir $app $version $global
|
||||
$json = install_info $app $version $global
|
||||
$install = @{}
|
||||
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name))}
|
||||
$json | Get-Member -MemberType Properties | ForEach-Object { $install.Add($_.Name, $json.($_.Name)) }
|
||||
$install.hold = $null
|
||||
save_install_info $install $dir
|
||||
success "$app is no longer held and can be updated again."
|
||||
|
||||
@@ -6,16 +6,12 @@
|
||||
# -g, --global Uninstall a globally installed app
|
||||
# -p, --purge Remove all persistent data
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest' 'Select-CurrentVersion' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\shortcuts.ps1"
|
||||
. "$PSScriptRoot\..\lib\psmodules.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
|
||||
reset_aliases
|
||||
. "$PSScriptRoot\..\lib\versions.ps1" # 'Select-CurrentVersion'
|
||||
|
||||
# options
|
||||
$opt, $apps, $err = getopt $args 'gp' 'global', 'purge'
|
||||
@@ -58,6 +54,12 @@ if (!$apps) { exit 0 }
|
||||
$dir = versiondir $app $version $global
|
||||
$persist_dir = persistdir $app $global
|
||||
|
||||
$manifest = installed_manifest $app $version $global
|
||||
$install = install_info $app $version $global
|
||||
$architecture = $install.architecture
|
||||
|
||||
Invoke-HookScript -HookType 'pre_uninstall' -Manifest $manifest -Arch $architecture
|
||||
|
||||
#region Workaround for #2952
|
||||
if (test_running_process $app $global) {
|
||||
continue
|
||||
@@ -71,10 +73,6 @@ if (!$apps) { exit 0 }
|
||||
continue
|
||||
}
|
||||
|
||||
$manifest = installed_manifest $app $version $global
|
||||
$install = install_info $app $version $global
|
||||
$architecture = $install.architecture
|
||||
|
||||
run_uninstaller $manifest $architecture $dir
|
||||
rm_shims $app $manifest $global $architecture
|
||||
rm_startmenu_shortcuts $manifest $global $architecture
|
||||
@@ -99,6 +97,8 @@ if (!$apps) { exit 0 }
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
Invoke-HookScript -HookType 'post_uninstall' -Manifest $manifest -Arch $architecture
|
||||
}
|
||||
# remove older versions
|
||||
$oldVersions = @(Get-ChildItem $appDir -Name -Exclude 'current')
|
||||
|
||||
@@ -14,19 +14,16 @@
|
||||
# -q, --quiet Hide extraneous messages
|
||||
# -a, --all Update all apps (alternative to '*')
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\json.ps1" # 'save_install_info' in 'manifest.ps1' (indirectly)
|
||||
. "$PSScriptRoot\..\lib\shortcuts.ps1"
|
||||
. "$PSScriptRoot\..\lib\psmodules.ps1"
|
||||
. "$PSScriptRoot\..\lib\decompress.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\depends.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
|
||||
reset_aliases
|
||||
|
||||
$opt, $apps, $err = getopt $args 'gfiksqa' 'global', 'force', 'independent', 'no-cache', 'skip', 'quiet', 'all'
|
||||
if ($err) { "scoop update: $err"; exit 1 }
|
||||
$global = $opt.g -or $opt.global
|
||||
@@ -57,61 +54,89 @@ 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 = fullpath $(versiondir 'scoop' 'new')
|
||||
$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")) {
|
||||
abort 'Scoop update failed.'
|
||||
if (!(Test-Path "$newdir\bin\scoop.ps1")) {
|
||||
Remove-Item $newdir -Force -Recurse
|
||||
abort "Scoop download failed. If this appears several times, try removing SCOOP_REPO by 'scoop config rm SCOOP_REPO'"
|
||||
} else {
|
||||
# replace non-git scoop with the git version
|
||||
try {
|
||||
Rename-Item $currentdir 'old' -ErrorAction Stop
|
||||
Rename-Item $newdir 'current' -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Warning $_
|
||||
abort "Scoop update failed. Folder in use. Paste $newdir into $currentdir."
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Test-Path "$currentdir\..\old") {
|
||||
Remove-Item "$currentdir\..\old" -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
# replace non-git scoop with the git version
|
||||
Remove-Item -r -force $currentdir -ea stop
|
||||
Move-Item $newdir $currentdir
|
||||
} else {
|
||||
$previousCommit = Invoke-Expression "git -C '$currentdir' rev-parse HEAD"
|
||||
$currentRepo = Invoke-Expression "git -C '$currentdir' config remote.origin.url"
|
||||
$currentBranch = Invoke-Expression "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) {
|
||||
Invoke-Expression "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)
|
||||
Invoke-Expression "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
|
||||
Invoke-Expression "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) {
|
||||
Invoke-Expression "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) {
|
||||
@@ -119,41 +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..."
|
||||
rm_bucket 'main'
|
||||
add_bucket 'main'
|
||||
} 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 = (Invoke-Expression "git -C '$bucketLoc' rev-parse HEAD")
|
||||
git_cmd -C "`"$bucketLoc`"" pull -q
|
||||
if ($show_update_log) {
|
||||
Invoke-Expression "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) {
|
||||
@@ -162,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'
|
||||
@@ -173,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
|
||||
}
|
||||
|
||||
@@ -196,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
|
||||
@@ -229,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
|
||||
@@ -246,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"
|
||||
@@ -258,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"
|
||||
@@ -286,25 +341,36 @@ 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)) {
|
||||
'ERROR: You need admin rights to update global apps.'; exit 1
|
||||
}
|
||||
|
||||
if (is_scoop_outdated) {
|
||||
update_scoop
|
||||
}
|
||||
$outdated = @()
|
||||
$updateScoop = $null -ne ($apps | Where-Object { $_ -eq 'scoop' }) -or (is_scoop_outdated)
|
||||
$apps = $apps | Where-Object { $_ -ne 'scoop' }
|
||||
$apps_param = $apps
|
||||
|
||||
if ($updateScoop) {
|
||||
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) {
|
||||
$apps = applist (installed_apps $false) $false
|
||||
if ($global) {
|
||||
$apps += applist (installed_apps $true) $true
|
||||
}
|
||||
} else {
|
||||
$apps = Confirm-InstallationStatus $apps_param -Global:$global
|
||||
if ($apps_param) {
|
||||
$apps = Confirm-InstallationStatus $apps_param -Global:$global
|
||||
}
|
||||
}
|
||||
if ($apps) {
|
||||
$apps | ForEach-Object {
|
||||
@@ -317,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
|
||||
|
||||
@@ -1,59 +1,47 @@
|
||||
# Usage: scoop virustotal [* | app1 app2 ...] [options]
|
||||
# Summary: Look for app's hash on virustotal.com
|
||||
# Help: Look for app's hash (MD5, SHA1 or SHA256) on virustotal.com
|
||||
# Summary: Look for app's hash or url on virustotal.com
|
||||
# Help: Look for app's hash or url on virustotal.com
|
||||
#
|
||||
# Use a single '*' for app to check all installed apps.
|
||||
# Use a single '*' or the '-a/--all' switch to check all installed apps.
|
||||
#
|
||||
# The download's hash is also a key to access VirusTotal's scan results.
|
||||
# This allows to check the safety of the files without even downloading
|
||||
# them in many cases. If the hash is unknown to VirusTotal, the
|
||||
# download link is printed to submit it to VirusTotal.
|
||||
#
|
||||
# If you have signed up to VirusTotal's community, you have an API key
|
||||
# that this script can use to submit unknown packages for inspection
|
||||
# if you use the `--scan' flag. Tell scoop about your API key with:
|
||||
# To use this command, you have to sign up to VirusTotal's community,
|
||||
# and get an API key. Then, tell scoop about your API key with:
|
||||
#
|
||||
# scoop config virustotal_api_key <your API key: 64 lower case hex digits>
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 -> success
|
||||
# 1 -> problem parsing arguments
|
||||
# 2 -> at least one package was marked unsafe by VirusTotal
|
||||
# 4 -> at least one exception was raised while looking for info
|
||||
# 8 -> at least one package couldn't be queried because its hash type
|
||||
# isn't supported by VirusTotal, the manifest couldn't be found
|
||||
# or didn't contain a hash
|
||||
# 0 -> success
|
||||
# 1 -> problem parsing arguments
|
||||
# 2 -> at least one package was marked unsafe by VirusTotal
|
||||
# 4 -> at least one exception was raised while looking for info
|
||||
# 8 -> at least one package couldn't be queried because the manifest couldn't be found
|
||||
# 16 -> VirusTotal API key is not configured
|
||||
# Note: the exit codes (2, 4 & 8) may be combined, e.g. 6 -> exit codes
|
||||
# 2 & 4 combined
|
||||
#
|
||||
# Options:
|
||||
# -a, --arch <32bit|64bit> Use the specified architecture, if the app supports it
|
||||
# -a, --all Check for all installed apps
|
||||
# -s, --scan For packages where VirusTotal has no information, send download URL
|
||||
# for analysis (and future retrieval). This requires you to configure
|
||||
# your virustotal_api_key.
|
||||
# -n, --no-depends By default, all dependencies are checked too. This flag avoids it.
|
||||
# -u, --no-update-scoop Don't update Scoop before checking if it's outdated
|
||||
# -p, --passthru Return reports as objects
|
||||
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\buckets.ps1"
|
||||
. "$PSScriptRoot\..\lib\json.ps1"
|
||||
. "$PSScriptRoot\..\lib\decompress.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\depends.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1" # 'Get-Manifest'
|
||||
. "$PSScriptRoot\..\lib\json.ps1" # 'json_path'
|
||||
. "$PSScriptRoot\..\lib\install.ps1" # 'hash_for_url'
|
||||
. "$PSScriptRoot\..\lib\depends.ps1" # 'Get-Dependency'
|
||||
|
||||
reset_aliases
|
||||
|
||||
$opt, $apps, $err = getopt $args 'a:snu' @('arch=', 'scan', 'no-depends', 'no-update-scoop')
|
||||
if($err) { "scoop virustotal: $err"; exit 1 }
|
||||
if(!$apps) { my_usage; exit 1 }
|
||||
$architecture = ensure_architecture ($opt.a + $opt.arch)
|
||||
$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 = Format-ArchitectureString
|
||||
|
||||
if (is_scoop_outdated) {
|
||||
if ($opt.u -or $opt.'no-update-scoop') {
|
||||
warn "Scoop is out of date."
|
||||
warn 'Scoop is out of date.'
|
||||
} else {
|
||||
scoop update
|
||||
}
|
||||
@@ -61,7 +49,7 @@ if (is_scoop_outdated) {
|
||||
|
||||
$apps_param = $apps
|
||||
|
||||
if($apps_param -eq '*') {
|
||||
if ($apps_param -eq '*' -or $opt.a -or $opt.all) {
|
||||
$apps = installed_apps $false
|
||||
$apps += installed_apps $true
|
||||
}
|
||||
@@ -73,11 +61,17 @@ if (!$opt.n -and !$opt.'no-depends') {
|
||||
$_ERR_UNSAFE = 2
|
||||
$_ERR_EXCEPTION = 4
|
||||
$_ERR_NO_INFO = 8
|
||||
$_ERR_NO_API_KEY = 16
|
||||
|
||||
$exit_code = 0
|
||||
|
||||
# Global flag to warn only once about missing API key:
|
||||
$warned_no_api_key = $False
|
||||
# Global 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" +
|
||||
" scoop config virustotal_api_key <API key>") $_ERR_NO_API_KEY
|
||||
}
|
||||
|
||||
# Global flag to explain only once about sleep between requests
|
||||
$explained_rate_limit_sleeping = $False
|
||||
@@ -86,66 +80,132 @@ $explained_rate_limit_sleeping = $False
|
||||
# script execution progresses
|
||||
$requests = 0
|
||||
|
||||
Function Get-VirusTotalResult($hash, $app) {
|
||||
$hash = $hash.ToLower()
|
||||
$url = "https://www.virustotal.com/ui/files/$hash"
|
||||
$wc = New-Object Net.Webclient
|
||||
$wc.Headers.Add('User-Agent', (Get-UserAgent))
|
||||
$result = $wc.downloadstring($url)
|
||||
$stats = json_path $result '$.data.attributes.last_analysis_stats'
|
||||
$malicious = json_path $stats '$.malicious'
|
||||
$suspicious = json_path $stats '$.suspicious'
|
||||
$undetected = json_path $stats '$.undetected'
|
||||
$unsafe = [int]$malicious + [int]$suspicious
|
||||
$see_url = "see https://www.virustotal.com/#/file/$hash/detection"
|
||||
switch ($unsafe) {
|
||||
0 { if ($undetected -eq 0) { $fg = "Yellow" } else { $fg = "DarkGreen" } }
|
||||
1 { $fg = "DarkYellow" }
|
||||
2 { $fg = "Yellow" }
|
||||
default { $fg = "Red" }
|
||||
}
|
||||
write-host -f $fg "$app`: $unsafe/$undetected, $see_url"
|
||||
if($unsafe -gt 0) {
|
||||
return $_ERR_UNSAFE
|
||||
}
|
||||
return 0
|
||||
Function ConvertTo-VirusTotalUrlId ($url) {
|
||||
$url_id = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($url))
|
||||
$url_id = $url_id -replace '\+', '-'
|
||||
$url_id = $url_id -replace '/', '_'
|
||||
$url_id = $url_id -replace '=', ''
|
||||
$url_id
|
||||
}
|
||||
|
||||
Function Search-VirusTotal ($hash, $app) {
|
||||
if ($hash -match '(?<algo>[^:]+):(?<hash>.*)') {
|
||||
$hash = $matches['hash']
|
||||
if ($matches['algo'] -match '(md5|sha1|sha256)') {
|
||||
return Get-VirusTotalResult $hash $app
|
||||
} else {
|
||||
warn "$app`: Unsupported hash $($matches['algo']). VirusTotal needs md5, sha1 or sha256."
|
||||
return $_ERR_NO_INFO
|
||||
Function Get-RemoteFileSize ($url) {
|
||||
$response = Invoke-WebRequest -Uri $url -Method HEAD -UseBasicParsing
|
||||
$response.Headers.'Content-Length' | ForEach-Object { [System.Convert]::ToInt32($_) }
|
||||
}
|
||||
|
||||
Function Get-VirusTotalResultByHash ($hash, $url, $app) {
|
||||
$hash = $hash.ToLower()
|
||||
$api_url = "https://www.virustotal.com/api/v3/files/$hash"
|
||||
$headers = @{}
|
||||
$headers.Add('Accept', 'application/json')
|
||||
$headers.Add('x-apikey', $api_key)
|
||||
$response = Invoke-WebRequest -Uri $api_url -Method GET -Headers $headers -UseBasicParsing
|
||||
$result = $response.Content
|
||||
$stats = json_path $result '$.data.attributes.last_analysis_stats'
|
||||
[int]$malicious = json_path $stats '$.malicious'
|
||||
[int]$suspicious = json_path $stats '$.suspicious'
|
||||
[int]$timeout = json_path $stats '$.timeout'
|
||||
[int]$undetected = json_path $stats '$.undetected'
|
||||
[int]$unsafe = $malicious + $suspicious
|
||||
[int]$total = $unsafe + $undetected
|
||||
[int]$fileSize = json_path $result '$.data.attributes.size'
|
||||
$report_hash = json_path $result '$.data.attributes.sha256'
|
||||
$report_url = "https://www.virustotal.com/gui/file/$report_hash"
|
||||
if ($total -eq 0) {
|
||||
info "$app`: Analysis in progress."
|
||||
[PSCustomObject] @{
|
||||
'App.Name' = $app
|
||||
'App.Url' = $url
|
||||
'App.Hash' = $hash
|
||||
'App.HashType' = $null
|
||||
'App.Size' = filesize $fileSize
|
||||
'FileReport.Url' = $report_url
|
||||
'FileReport.Hash' = $report_hash
|
||||
'UrlReport.Url' = $null
|
||||
}
|
||||
} else {
|
||||
$vendorResults = (ConvertFrom-Json((json_path $result '$.data.attributes.last_analysis_results'))).PSObject.Properties.Value
|
||||
switch ($unsafe) {
|
||||
0 {
|
||||
success "$app`: $unsafe/$total, see $report_url"
|
||||
}
|
||||
1 {
|
||||
warn "$app`: $unsafe/$total, see $report_url"
|
||||
}
|
||||
2 {
|
||||
warn "$app`: $unsafe/$total, see $report_url"
|
||||
}
|
||||
Default {
|
||||
warn "`e[31m$app`: $unsafe/$total, see $report_url`e[0m"
|
||||
}
|
||||
}
|
||||
$maliciousResults = $vendorResults |
|
||||
Where-Object -Property category -EQ 'malicious' |
|
||||
Select-Object -ExpandProperty engine_name
|
||||
$suspiciousResults = $vendorResults |
|
||||
Where-Object -Property category -EQ 'suspicious' |
|
||||
Select-Object -ExpandProperty engine_name
|
||||
[PSCustomObject] @{
|
||||
'App.Name' = $app
|
||||
'App.Url' = $url
|
||||
'App.Hash' = $hash
|
||||
'App.HashType' = $null
|
||||
'App.Size' = filesize $fileSize
|
||||
'FileReport.Url' = $report_url
|
||||
'FileReport.Hash' = $report_hash
|
||||
'FileReport.Malicious' = if ($maliciousResults) { $maliciousResults } else { 0 }
|
||||
'FileReport.Suspicious' = if ($suspiciousResults) { $suspiciousResults } else { 0 }
|
||||
'FileReport.Timeout' = $timeout
|
||||
'FileReport.Undetected' = $undetected
|
||||
'UrlReport.Url' = $null
|
||||
}
|
||||
}
|
||||
|
||||
return Get-VirusTotalResult $hash $app
|
||||
if ($unsafe -gt 0) {
|
||||
$Script:exit_code = $exit_code -bor $_ERR_UNSAFE
|
||||
}
|
||||
}
|
||||
|
||||
Function Submit-RedirectedUrl {
|
||||
# Follow up to one level of HTTP redirection
|
||||
#
|
||||
# Copied from http://www.powershellmagazine.com/2013/01/29/pstip-retrieve-a-redirected-url/
|
||||
# Adapted according to Roy's response (January 23, 2014 at 11:59 am)
|
||||
# Adapted to always return an URL
|
||||
Param (
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String]$URL
|
||||
)
|
||||
$request = [System.Net.WebRequest]::Create($url)
|
||||
$request.AllowAutoRedirect=$false
|
||||
$response=$request.GetResponse()
|
||||
if (([int]$response.StatusCode -ge 300) -and ([int]$response.StatusCode -lt 400)) {
|
||||
$redir = $response.GetResponseHeader("Location")
|
||||
Function Get-VirusTotalResultByUrl ($url, $app) {
|
||||
$id = ConvertTo-VirusTotalUrlId $url
|
||||
$api_url = "https://www.virustotal.com/api/v3/urls/$id"
|
||||
$headers = @{}
|
||||
$headers.Add('Accept', 'application/json')
|
||||
$headers.Add('x-apikey', $api_key)
|
||||
$response = Invoke-WebRequest -Uri $api_url -Method GET -Headers $headers -UseBasicParsing
|
||||
$result = $response.Content
|
||||
$id = json_path $result '$.data.id'
|
||||
$hash = json_path $result '$.data.attributes.last_http_response_content_sha256' 6>$null
|
||||
$last_analysis_date = json_path $result '$.data.attributes.last_analysis_date' 6>$null
|
||||
$url_report_url = "https://www.virustotal.com/gui/url/$id"
|
||||
info "$app`: Url report found."
|
||||
if (!$hash) {
|
||||
if (!$last_analysis_date) {
|
||||
info "$app`: Analysis in progress."
|
||||
} else {
|
||||
info "$app`: Related file report not found."
|
||||
warn "$app`: Manual file upload is required (instead of url submission)."
|
||||
}
|
||||
[PSCustomObject] @{
|
||||
'App.Name' = $app
|
||||
'App.Url' = $url
|
||||
'App.Hash' = $null
|
||||
'App.HashType' = $null
|
||||
'FileReport.Url' = $null
|
||||
'UrlReport.Url' = $url_report_url
|
||||
'UrlReport.Hash' = $null
|
||||
}
|
||||
} else {
|
||||
info "$app`: Related file report found."
|
||||
[PSCustomObject] @{
|
||||
'App.Name' = $app
|
||||
'App.Url' = $url
|
||||
'App.Hash' = $null
|
||||
'App.HashType' = $null
|
||||
'FileReport.Url' = $null
|
||||
'UrlReport.Url' = $url_report_url
|
||||
'UrlReport.Hash' = $hash
|
||||
}
|
||||
}
|
||||
else {
|
||||
$redir = $URL
|
||||
}
|
||||
$response.Close()
|
||||
return $redir
|
||||
}
|
||||
|
||||
# Submit-ToVirusTotal
|
||||
@@ -158,33 +218,39 @@ Function Submit-RedirectedUrl {
|
||||
# submitting the file after a delay if the rate limit is
|
||||
# exceeded, without risking an infinite loop (as stack
|
||||
# overflow) if the submission keeps failing.
|
||||
Function Submit-ToVirusTotal ($url, $app, $do_scan, $retrying=$False) {
|
||||
$api_key = get_config("virustotal_api_key")
|
||||
if ($do_scan -and !$api_key -and !$warned_no_api_key) {
|
||||
$warned_no_api_key = $true
|
||||
info "Submitting unknown apps needs a VirusTotal API key. " +
|
||||
"Set it up with`n`tscoop config virustotal_api_key <API key>"
|
||||
|
||||
}
|
||||
if (!$do_scan -or !$api_key) {
|
||||
warn "$app`: not found`: manually submit $url"
|
||||
Function Submit-ToVirusTotal ($url, $app, $do_scan, $retrying = $False) {
|
||||
if (!$do_scan) {
|
||||
warn "$app`: not found`: you can manually submit $url"
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
# Follow redirections (for e.g. sourceforge URLs) because
|
||||
# VirusTotal analyzes only "direct" download links
|
||||
$url = $url.Split("#").GetValue(0)
|
||||
$new_redir = $url
|
||||
do {
|
||||
$orig_redir = $new_redir
|
||||
$new_redir = Submit-RedirectedUrl $orig_redir
|
||||
} while ($orig_redir -ne $new_redir)
|
||||
$requests += 1
|
||||
$result = Invoke-WebRequest -Uri "https://www.virustotal.com/vtapi/v2/url/scan" -Body @{apikey=$api_key;url=$new_redir} -Method Post -UseBasicParsing
|
||||
$submitted = $result.StatusCode -eq 200
|
||||
if ($submitted) {
|
||||
warn "$app`: not found`: submitted $url"
|
||||
|
||||
$encoded_url = [System.Web.HttpUtility]::UrlEncode($url)
|
||||
$api_url = 'https://www.virustotal.com/api/v3/urls'
|
||||
$content_type = 'application/x-www-form-urlencoded'
|
||||
$headers = @{}
|
||||
$headers.Add('Accept', 'application/json')
|
||||
$headers.Add('x-apikey', $api_key)
|
||||
$headers.Add('Content-Type', $content_type)
|
||||
$body = "url=$encoded_url"
|
||||
$result = Invoke-WebRequest -Uri $api_url -Method POST -Headers $headers -ContentType $content_type -Body $body -UseBasicParsing
|
||||
if ($result.StatusCode -eq 200) {
|
||||
$id = ((json_path $result '$.data.id') -split '-')[1]
|
||||
$url_report_url = "https://www.virustotal.com/gui/url/$id"
|
||||
$fileSize = Get-RemoteFileSize $url
|
||||
if ($fileSize -gt 80000000) {
|
||||
info "$app`: Remote file size: $(filesize $fileSize). Large files might require manual file upload instead of url submission."
|
||||
}
|
||||
info "$app`: Analysis in progress."
|
||||
[PSCustomObject] @{
|
||||
'App.Name' = $app
|
||||
'App.Url' = $url
|
||||
'App.Size' = filesize $fileSize
|
||||
'FileReport.Url' = $null
|
||||
'UrlReport.Url' = $url_report_url
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -195,10 +261,10 @@ Function Submit-ToVirusTotal ($url, $app, $do_scan, $retrying=$False) {
|
||||
info "Sleeping 60+ seconds between requests due to VirusTotal's 4/min limit"
|
||||
}
|
||||
Start-Sleep -s (60 + $requests)
|
||||
Submit-ToVirusTotal $new_redir $app $do_scan $True
|
||||
Submit-ToVirusTotal $url $app $do_scan $True
|
||||
} else {
|
||||
warn "$app`: VirusTotal submission of $url failed`:`n" +
|
||||
"`tAPI returned $($result.StatusCode) after retrying"
|
||||
"`tAPI returned $($result.StatusCode) after retrying"
|
||||
}
|
||||
} catch [Exception] {
|
||||
warn "$app`: VirusTotal submission failed`: $($_.Exception.Message)"
|
||||
@@ -206,39 +272,120 @@ Function Submit-ToVirusTotal ($url, $app, $do_scan, $retrying=$False) {
|
||||
}
|
||||
}
|
||||
|
||||
$apps | ForEach-Object {
|
||||
$reports = $apps | ForEach-Object {
|
||||
$app = $_
|
||||
# write-host $app
|
||||
$manifest, $bucket = find_manifest $app
|
||||
if(!$manifest) {
|
||||
$null, $manifest, $bucket, $null = Get-Manifest $app
|
||||
if (!$manifest) {
|
||||
$exit_code = $exit_code -bor $_ERR_NO_INFO
|
||||
warn "$app`: manifest not found"
|
||||
return
|
||||
}
|
||||
|
||||
[int]$index = 0
|
||||
$urls = script:url $manifest $architecture
|
||||
$urls | ForEach-Object {
|
||||
$url = $_
|
||||
$index++
|
||||
if ($urls.GetType().IsArray) {
|
||||
info "$app`: url $index"
|
||||
}
|
||||
$hash = hash_for_url $manifest $url $architecture
|
||||
|
||||
try {
|
||||
if($hash) {
|
||||
$exit_code = $exit_code -bor (Search-VirusTotal $hash $app)
|
||||
} else {
|
||||
warn "$app`: Can't find hash for $url"
|
||||
$isHashUnsupported = $false
|
||||
if ($hash -match '(?<algo>[^:]+):(?<hash>.*)') {
|
||||
$algo = $matches.algo
|
||||
$hash = $matches.hash
|
||||
if ($matches.algo -inotin 'md5', 'sha1', 'sha256') {
|
||||
$hash = $null
|
||||
$isHashUnsupported = $true
|
||||
warn "$app`: Unsupported hash $($matches.algo). Will search by url instead."
|
||||
}
|
||||
} elseif ($hash) {
|
||||
$algo = 'sha256'
|
||||
}
|
||||
if ($hash) {
|
||||
$file_report = Get-VirusTotalResultByHash $hash $url $app
|
||||
$file_report.'App.HashType' = $algo
|
||||
$file_report
|
||||
return
|
||||
} elseif (!$isHashUnsupported) {
|
||||
warn "$app`: Hash not found. Will search by url instead."
|
||||
}
|
||||
} catch [Exception] {
|
||||
$exit_code = $exit_code -bor $_ERR_EXCEPTION
|
||||
if ($_.Exception.Message -like "*(404)*") {
|
||||
Submit-ToVirusTotal $url $app ($opt.scan -or $opt.s)
|
||||
if ($_.Exception.Response.StatusCode -eq 404) {
|
||||
$file_report_not_found = $true
|
||||
warn "$app`: File report not found. Will search by url instead."
|
||||
} else {
|
||||
if ($_.Exception.Message -match "\(204|429\)") {
|
||||
abort "$app`: VirusTotal request failed`: $($_.Exception.Message)", $exit_code
|
||||
if ($_.Exception.Response.StatusCode -in 204, 429) {
|
||||
abort "$app`: VirusTotal request failed`: $($_.Exception.Message)" $exit_code
|
||||
}
|
||||
warn "$app`: VirusTotal request failed`: $($_.Exception.Message)"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$url_report = Get-VirusTotalResultByUrl $url $app
|
||||
$url_report.'App.Hash' = $hash
|
||||
$url_report.'App.HashType' = $algo
|
||||
if ($url_report.'UrlReport.Hash' -and ($file_report_not_found -eq $true) -and $hash) {
|
||||
if ($algo -eq 'sha256') {
|
||||
if ($url_report.'UrlReport.Hash' -eq $hash) {
|
||||
warn "$app`: Manual file upload is required (instead of url submission) for $url"
|
||||
} else {
|
||||
error "$app`: Hash not matched for $url"
|
||||
}
|
||||
} else {
|
||||
error "$app`: Hash not matched or manual file upload is required (instead of url submission) for $url"
|
||||
}
|
||||
$url_report
|
||||
return
|
||||
}
|
||||
if (!$url_report.'UrlReport.Hash') {
|
||||
$url_report
|
||||
return
|
||||
}
|
||||
} catch [Exception] {
|
||||
$exit_code = $exit_code -bor $_ERR_EXCEPTION
|
||||
if ($_.Exception.Response.StatusCode -eq 404) {
|
||||
warn "$app`: Url report not found. Will submit $url"
|
||||
Submit-ToVirusTotal $url $app ($opt.scan -or $opt.s)
|
||||
return
|
||||
} else {
|
||||
if ($_.Exception.Response.StatusCode -in 204, 429) {
|
||||
abort "$app`: VirusTotal request failed`: $($_.Exception.Message)" $exit_code
|
||||
}
|
||||
warn "$app`: VirusTotal request failed`: $($_.Exception.Message)"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$file_report = Get-VirusTotalResultByHash $url_report.'UrlReport.Hash' $url $app
|
||||
$file_report.'App.Hash' = $hash
|
||||
$file_report.'App.HashType' = $algo
|
||||
$file_report.'UrlReport.Url' = $url_report.'UrlReport.Url'
|
||||
$file_report
|
||||
warn "$app`: Unable to check hash match for $url"
|
||||
} catch [Exception] {
|
||||
$exit_code = $exit_code -bor $_ERR_EXCEPTION
|
||||
if ($_.Exception.Response.StatusCode -eq 404) {
|
||||
warn "$app`: File report not found for unknown reason. Manual file upload is required (instead of url submission)."
|
||||
$url_report
|
||||
} else {
|
||||
if ($_.Exception.Response.StatusCode -in 204, 429) {
|
||||
abort "$app`: VirusTotal request failed`: $($_.Exception.Message)" $exit_code
|
||||
}
|
||||
warn "$app`: VirusTotal request failed`: $($_.Exception.Message)"
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($opt.p -or $opt.'passthru') {
|
||||
$reports
|
||||
}
|
||||
|
||||
exit $exit_code
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
# Summary: Locate a shim/executable (similar to 'which' on Linux)
|
||||
# Help: Locate the path to a shim/executable that was installed with Scoop (similar to 'which' on Linux)
|
||||
param($command)
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\help.ps1"
|
||||
|
||||
reset_aliases
|
||||
|
||||
if (!$command) {
|
||||
'ERROR: <command> missing'
|
||||
@@ -13,40 +9,12 @@ if (!$command) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
try {
|
||||
$gcm = Get-Command "$command" -ErrorAction Stop
|
||||
} catch {
|
||||
abort "'$command' not found" 3
|
||||
}
|
||||
$path = Get-CommandPath $command
|
||||
|
||||
$path = $gcm.Path
|
||||
$usershims = Convert-Path (shimdir $false)
|
||||
$globalshims = fullpath (shimdir $true) # don't resolve: may not exist
|
||||
|
||||
if ($path -like "$usershims*" -or $path -like "$globalshims*") {
|
||||
$exepath = if ($path.EndsWith('.exe') -or $path.EndsWith('.shim')) {
|
||||
(Get-Content ($path -replace '\.exe$', '.shim') | Select-Object -First 1).Replace('path = ', '').Replace('"', '')
|
||||
} else {
|
||||
((Select-String -Path $path -Pattern '^(?:@rem|#)\s*(.*)$').Matches.Groups | Select-Object -Index 1).Value
|
||||
}
|
||||
if (!$exepath) {
|
||||
$exepath = ((Select-String -Path $path -Pattern '[''"]([^@&]*?)[''"]' -AllMatches).Matches.Groups | Select-Object -Last 1).Value
|
||||
}
|
||||
|
||||
if (![System.IO.Path]::IsPathRooted($exepath)) {
|
||||
# Expand relative path
|
||||
$exepath = Convert-Path $exepath
|
||||
}
|
||||
|
||||
friendly_path $exepath
|
||||
} elseif ($gcm.CommandType -eq 'Application') {
|
||||
$gcm.Source
|
||||
} elseif ($gcm.CommandType -eq 'Alias') {
|
||||
scoop which $gcm.ResolvedCommandName
|
||||
} else {
|
||||
Write-Host 'Not a scoop shim.'
|
||||
$path
|
||||
if ($null -eq $path) {
|
||||
Write-Host "'$command' not found / not a scoop shim."
|
||||
exit 2
|
||||
} else {
|
||||
friendly_path $path
|
||||
exit 0
|
||||
}
|
||||
|
||||
exit 0
|
||||
|
||||
181
schema.json
181
schema.json
@@ -134,9 +134,15 @@
|
||||
"post_install": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
},
|
||||
"post_uninstall": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
},
|
||||
"pre_install": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
},
|
||||
"pre_uninstall": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
},
|
||||
"shortcuts": {
|
||||
"$ref": "#/definitions/shortcutsArray"
|
||||
},
|
||||
@@ -173,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"
|
||||
@@ -193,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": [
|
||||
@@ -309,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"
|
||||
@@ -502,6 +545,9 @@
|
||||
},
|
||||
"64bit": {
|
||||
"$ref": "#/definitions/architecture"
|
||||
},
|
||||
"arm64": {
|
||||
"$ref": "#/definitions/architecture"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -567,9 +613,15 @@
|
||||
"post_install": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
},
|
||||
"post_uninstall": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
},
|
||||
"pre_install": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
},
|
||||
"pre_uninstall": {
|
||||
"$ref": "#/definitions/stringOrArrayOfStrings"
|
||||
},
|
||||
"psmodule": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -602,9 +654,36 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"if": {
|
||||
"properties": {
|
||||
"architecture": {
|
||||
"properties": {
|
||||
"64bit": {
|
||||
"properties": {
|
||||
"url": false
|
||||
}
|
||||
},
|
||||
"32bit": {
|
||||
"properties": {
|
||||
"url": false
|
||||
}
|
||||
},
|
||||
"arm64": {
|
||||
"properties": {
|
||||
"url": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"url"
|
||||
]
|
||||
},
|
||||
"required": [
|
||||
"version",
|
||||
"description",
|
||||
"homepage",
|
||||
"license"
|
||||
],
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Updated</PropertyName>
|
||||
<FormatString>yyyy-MM-dd HH:MM:ss</FormatString>
|
||||
<FormatString>yyyy-MM-dd HH:mm:ss</FormatString>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Info</PropertyName>
|
||||
@@ -60,5 +60,34 @@
|
||||
</TableRowEntries>
|
||||
</TableControl>
|
||||
</View>
|
||||
<View>
|
||||
<Name>ScoopStatusType</Name>
|
||||
<ViewSelectedBy>
|
||||
<TypeName>ScoopStatus</TypeName>
|
||||
</ViewSelectedBy>
|
||||
<TableControl>
|
||||
<TableRowEntries>
|
||||
<TableRowEntry>
|
||||
<TableColumnItems>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Name</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Installed Version</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Latest Version</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Missing Dependencies</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Info</PropertyName>
|
||||
</TableColumnItem>
|
||||
</TableColumnItems>
|
||||
</TableRowEntry>
|
||||
</TableRowEntries>
|
||||
</TableControl>
|
||||
</View>
|
||||
</ViewDefinitions>
|
||||
</Configuration>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
Param([Switch]$Fast)
|
||||
Push-Location $PSScriptRoot
|
||||
. "$PSScriptRoot\..\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\..\lib\install.ps1"
|
||||
|
||||
if (!$Fast) {
|
||||
Write-Host "Install dependencies ..."
|
||||
Invoke-Expression "$PSScriptRoot\install.ps1"
|
||||
& "$PSScriptRoot\install.ps1"
|
||||
}
|
||||
|
||||
$output = "$PSScriptRoot\bin"
|
||||
@@ -15,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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Net.Compilers.Toolset" version="4.0.1" targetFramework="net45" developmentDependency="true" />
|
||||
<package id="Microsoft.Net.Compilers.Toolset" version="4.2.0" targetFramework="net45" developmentDependency="true" />
|
||||
</packages>
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{381F9D2E-2355-4F84-9206-06BB9175F97B}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>Scoop.Shim</RootNamespace>
|
||||
<AssemblyName>Scoop.Shim</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="shim.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<Import Project="packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{381F9D2E-2355-4F84-9206-06BB9175F97B}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>Scoop.Shim</RootNamespace>
|
||||
<AssemblyName>Scoop.Shim</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props'))" />
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="shim.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
VER?=2.2.1
|
||||
ZIP=shimexe-$(VER).zip
|
||||
ZIP=shimexe.zip
|
||||
URL?=https://github.com/kiennq/scoop-better-shimexe/releases/download/$(VER)/$(ZIP)
|
||||
LATEST_URL?=https://github.com/kiennq/scoop-better-shimexe/releases/latest
|
||||
NEWVER=$(shell cat version.txt)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
7f912b28a07c226e0be3acfb2f57f050538aba0100fa1f0bf2c39f1a1f1da814 *Newtonsoft.Json.dll
|
||||
cff8fc4ce358d7daff84ab47129a776797a4ec819c1586a15bd5e63144f5b73f *Newtonsoft.Json.Schema.dll
|
||||
0d6b228378cbabff23a30456d22f1a31337c466f90cf8b7997cc48bd171155f3 *Scoop.Validator.dll
|
||||
b624949df8b0e3a6153fdfb730a7c6f4990b6592ee0d922e1788433d276610f3 *Newtonsoft.Json.dll
|
||||
9abb57d73d82a2d77008321a85aff2b62e5ac68bebb54ece8668c96cc112e36b *Newtonsoft.Json.Schema.dll
|
||||
0318c8221ce4d44806f8def619bcc02886be0902aab80080e6251c50c6ca53a9 *Scoop.Validator.dll
|
||||
40a70bee96d108701f8f2e81392f9b79fd003f1cb4e1653ad2429753153fd7ee *validator.exe
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
3398094ce429ab5dcdecf2ad04803230669bb4accaef7083992e9b87afac55841ba8def2a5168358bd17e60799e55d076b0e5ca44c86b9e6c91150d3dc37c721 *Newtonsoft.Json.dll
|
||||
298d3d0b656acbb1fe5ed0c3abb49a640c47889184ab7bd4b594e51a7d7f829d5c8685edbd10a286fd56bfd8d601b9f187da463a5a9c8509365eddaea280642f *Newtonsoft.Json.Schema.dll
|
||||
afabe1df6ab837395a5da5ec8dd12bf3f36a8512b76e6f751c14045544246980e9d4061d437792836db792864b7db2761e84f1bf65bac688657a862b68fc7b45 *Scoop.Validator.dll
|
||||
2fdf035661f349206f58ea1feed8805b7f9517a21f9c113e7301c69de160f184c774350a12a710046e3ff6baa37345d319b6f47fd24fbba4e042d54014bee511 *Newtonsoft.Json.dll
|
||||
855ab2e30c9d523c9f321ae861c5969244185f660fa47e05cec96df8e2970d19843dbd3d89a0fca845544641915d1adf4b4a2145ef568dd99da7791e5064d70e *Newtonsoft.Json.Schema.dll
|
||||
338793e6127330c0b05728291fcf18441127ffb56e1bd5c0f0588cd7436605f4b852f4bb622f655896a7eb7b1262add142b200fd5f37391b47d1401becb6b81c *Scoop.Validator.dll
|
||||
d497c27b48f44f4cff270d3c8801b0cecc74108f8786a4a7c40e57541308ae33a69f5456cfc43ae1ce4214038d20da9fbeac1bcf76cc58d972863b58dab18401 *validator.exe
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
Param([Switch]$Fast)
|
||||
Push-Location $PSScriptRoot
|
||||
. "$PSScriptRoot\..\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\..\lib\install.ps1"
|
||||
|
||||
if (!$Fast) {
|
||||
Write-Host "Install dependencies ..."
|
||||
Invoke-Expression "$PSScriptRoot\install.ps1"
|
||||
& "$PSScriptRoot\install.ps1"
|
||||
}
|
||||
|
||||
$output = "$PSScriptRoot\bin"
|
||||
@@ -20,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="12.0.3" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json.Schema" version="3.0.14" targetFramework="net45" />
|
||||
<package id="Microsoft.Net.Compilers.Toolset" version="4.0.1" targetFramework="net45" developmentDependency="true" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json.Schema" version="3.0.15-beta2" targetFramework="net45" />
|
||||
<package id="Microsoft.Net.Compilers.Toolset" version="4.2.0" targetFramework="net45" developmentDependency="true" />
|
||||
</packages>
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{8EB9B38A-1BAB-4D89-B4CB-ACE5E7C1073B}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>Scoop.Validator</RootNamespace>
|
||||
<AssemblyName>Scoop.Validator</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
|
||||
<HintPath>packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json.Schema, Version=3.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
|
||||
<HintPath>packages\Newtonsoft.Json.Schema.3.0.14\lib\net45\Newtonsoft.Json.Schema.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="validator.cs" />
|
||||
<Compile Include="Scoop.Validator.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<Import Project="packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props" Condition="Exists('packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{8EB9B38A-1BAB-4D89-B4CB-ACE5E7C1073B}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>Scoop.Validator</RootNamespace>
|
||||
<AssemblyName>Scoop.Validator</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.0.1\build\Microsoft.Net.Compilers.Toolset.props'))" />
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
|
||||
<HintPath>packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json.Schema, Version=3.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
|
||||
<HintPath>packages\Newtonsoft.Json.Schema.3.0.15-beta2\lib\net45\Newtonsoft.Json.Schema.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="validator.cs" />
|
||||
<Compile Include="Scoop.Validator.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.Toolset.4.2.0\build\Microsoft.Net.Compilers.Toolset.props'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
@@ -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,128 +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, description\.'
|
||||
}
|
||||
}
|
||||
}
|
||||
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 '(unformated)' }
|
||||
)
|
||||
|
||||
$files_exist = ($files.Count -gt 0)
|
||||
|
||||
It $('non-binary project files exist ({0} found)' -f $files.Count) -Skip:$(-not $files_exist) {
|
||||
if (-not ($files.Count -gt 0)) {
|
||||
throw 'No non-binary project were found'
|
||||
}
|
||||
}
|
||||
|
||||
It 'files do not contain leading UTF-8 BOM' -Skip:$(-not $files_exist) {
|
||||
# UTF-8 BOM == 0xEF 0xBB 0xBF
|
||||
# see http://www.powershellmagazine.com/2012/12/17/pscxtip-how-to-determine-the-byte-order-mark-of-a-text-file @@ https://archive.is/RgT42
|
||||
# ref: http://poshcode.org/2153 @@ https://archive.is/sGnnu
|
||||
$badFiles = @(
|
||||
foreach ($file in $files) {
|
||||
if ((Get-Command Get-Content).parameters.ContainsKey('AsByteStream')) {
|
||||
# PowerShell Core (6.0+) '-Encoding byte' is replaced by '-AsByteStream'
|
||||
$content = ([char[]](Get-Content $file.FullName -AsByteStream -TotalCount 3) -join '')
|
||||
} else {
|
||||
$content = ([char[]](Get-Content $file.FullName -Encoding byte -TotalCount 3) -join '')
|
||||
}
|
||||
if ([regex]::match($content, '(?ms)^\xEF\xBB\xBF').success) {
|
||||
$file.FullName
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if ($badFiles.Count -gt 0) {
|
||||
throw "The following files have utf-8 BOM: `r`n`r`n$($badFiles -join "`r`n")"
|
||||
}
|
||||
}
|
||||
|
||||
It 'files end with a newline' -Skip:$(-not $files_exist) {
|
||||
$badFiles = @(
|
||||
foreach ($file in $files) {
|
||||
# Ignore previous TestResults.xml
|
||||
if ($file -match 'TestResults.xml') {
|
||||
continue
|
||||
}
|
||||
$string = [System.IO.File]::ReadAllText($file.FullName)
|
||||
if ($string.Length -gt 0 -and $string[-1] -ne "`n") {
|
||||
$file.FullName
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if ($badFiles.Count -gt 0) {
|
||||
throw "The following files do not end with a newline: `r`n`r`n$($badFiles -join "`r`n")"
|
||||
}
|
||||
}
|
||||
|
||||
It 'file newlines are CRLF' -Skip:$(-not $files_exist) {
|
||||
$badFiles = @(
|
||||
foreach ($file in $files) {
|
||||
$content = Get-Content -Raw $file.FullName
|
||||
if (!$content) {
|
||||
throw "File contents are null: $($file.FullName)"
|
||||
}
|
||||
$lines = [regex]::split($content, '\r\n')
|
||||
$lineCount = $lines.Count
|
||||
|
||||
for ($i = 0; $i -lt $lineCount; $i++) {
|
||||
if ( [regex]::match($lines[$i], '\r|\n').success ) {
|
||||
$file.FullName
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if ($badFiles.Count -gt 0) {
|
||||
throw "The following files have non-CRLF line endings: `r`n`r`n$($badFiles -join "`r`n")"
|
||||
}
|
||||
}
|
||||
|
||||
It 'files have no lines containing trailing whitespace' -Skip:$(-not $files_exist) {
|
||||
$badLines = @(
|
||||
foreach ($file in $files) {
|
||||
# Ignore previous TestResults.xml
|
||||
if ($file -match 'TestResults.xml') {
|
||||
continue
|
||||
}
|
||||
$lines = [System.IO.File]::ReadAllLines($file.FullName)
|
||||
$lineCount = $lines.Count
|
||||
|
||||
for ($i = 0; $i -lt $lineCount; $i++) {
|
||||
if ($lines[$i] -match '\s+$') {
|
||||
'File: {0}, Line: {1}' -f $file.FullName, ($i + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if ($badLines.Count -gt 0) {
|
||||
throw "The following $($badLines.Count) lines contain trailing whitespace: `r`n`r`n$($badLines -join "`r`n")"
|
||||
}
|
||||
}
|
||||
|
||||
It 'any leading whitespace consists only of spaces (excepting makefiles)' -Skip:$(-not $files_exist) {
|
||||
$badLines = @(
|
||||
foreach ($file in $files) {
|
||||
if ($file.fullname -inotmatch '(^|.)makefile$') {
|
||||
$lines = [System.IO.File]::ReadAllLines($file.FullName)
|
||||
$lineCount = $lines.Count
|
||||
|
||||
for ($i = 0; $i -lt $lineCount; $i++) {
|
||||
if ($lines[$i] -notmatch '^[ ]*(\S|$)') {
|
||||
'File: {0}, Line: {1}' -f $file.FullName, ($i + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if ($badLines.Count -gt 0) {
|
||||
throw "The following $($badLines.Count) lines contain TABs within leading whitespace: `r`n`r`n$($badLines -join "`r`n")"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
189
test/Scoop-00File.Tests.ps1
Normal file
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,55 +1,45 @@
|
||||
. "$PSScriptRoot\..\libexec\scoop-alias.ps1" | Out-Null
|
||||
|
||||
reset_aliases
|
||||
|
||||
Describe 'add_alias' -Tag 'Scoop' {
|
||||
Mock shimdir { 'TestDrive:\shim' }
|
||||
Mock set_config { }
|
||||
Mock get_config { @{} }
|
||||
|
||||
$shimdir = shimdir
|
||||
mkdir $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!"'
|
||||
Invoke-Expression $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
|
||||
$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 { 'TestDrive:\shim' }
|
||||
Mock set_config { }
|
||||
Mock get_config { @{} }
|
||||
Describe 'Manipulate Alias' -Tag 'Scoop' {
|
||||
BeforeAll {
|
||||
Mock shimdir { "$TestDrive\shims" }
|
||||
Mock set_config { }
|
||||
Mock get_config { @{} }
|
||||
|
||||
$shimdir = shimdir
|
||||
mkdir $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,16 +1,16 @@
|
||||
. "$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\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\unix.ps1"
|
||||
. "$PSScriptRoot\Scoop-TestLib.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,24 +224,24 @@ 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 {
|
||||
mkdir -p "$working_dir/mockapp/current/"
|
||||
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'
|
||||
$shim_path1 = (Get-Command 'shim-test1.ps1').Path
|
||||
get_app_name_from_shim "$shim_path1" | Should -Be 'mockapp'
|
||||
mkdir -p "$working_dir/mockapp/1.0.0/"
|
||||
ensure "$working_dir/mockapp/1.0.0/"
|
||||
Write-Output '' | Out-File "$working_dir/mockapp/1.0.0/mockapp2.ps1"
|
||||
shim "$working_dir/mockapp/1.0.0/mockapp2.ps1" $false 'shim-test2'
|
||||
$shim_path2 = (Get-Command 'shim-test2.ps1').Path
|
||||
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,39 +258,33 @@ Describe 'get_app_name_from_shim' -Tag 'Scoop' {
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'ensure_robocopy_in_path' -Tag 'Scoop' {
|
||||
$shimdir = shimdir $false
|
||||
Mock versiondir { $repo_dir }
|
||||
|
||||
Describe 'ensure_robocopy_in_path' -Tag 'Scoop', 'Windows' {
|
||||
BeforeAll {
|
||||
reset_aliases
|
||||
$shimdir = shimdir $false
|
||||
Mock versiondir { "$PSScriptRoot\.." }
|
||||
}
|
||||
|
||||
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
|
||||
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 -exist
|
||||
"$shimdir/robocopy.exe" | Should -Exist
|
||||
# "$shimdir/robocopy.ps1" | should -exist
|
||||
"$shimdir/robocopy.exe" | Should -Exist
|
||||
|
||||
# clean up
|
||||
rm_shim robocopy $(shimdir $false) | Out-Null
|
||||
}
|
||||
# clean up
|
||||
rm_shim robocopy $(shimdir $false) | Out-Null
|
||||
}
|
||||
|
||||
Context 'robocopy is in path' {
|
||||
It 'does not shim robocopy when it is in path' -Skip:$isUnix {
|
||||
Mock Test-CommandAvailable { $true }
|
||||
Test-CommandAvailable robocopy | Should -Be $true
|
||||
It 'does not shim robocopy when it is in path' {
|
||||
Mock Test-CommandAvailable { $true }
|
||||
Test-CommandAvailable robocopy | Should -Be $true
|
||||
|
||||
ensure_robocopy_in_path
|
||||
ensure_robocopy_in_path
|
||||
|
||||
# "$shimdir/robocopy.ps1" | should -not -exist
|
||||
"$shimdir/robocopy.exe" | Should -Not -Exist
|
||||
}
|
||||
# "$shimdir/robocopy.ps1" | should -not -exist
|
||||
"$shimdir/robocopy.exe" | Should -Not -Exist
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,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,13 +1,13 @@
|
||||
. "$PSScriptRoot\Scoop-TestLib.ps1"
|
||||
. "$PSScriptRoot\..\lib\decompress.ps1"
|
||||
. "$PSScriptRoot\..\lib\unix.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\versions.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'
|
||||
@@ -18,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 '3a442e85b466833eeafbd08c57d8f51bf7ff041867ee0bdb7db1f12480b3624a'
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,16 +44,22 @@ Describe 'Decompression function' -Tag 'Scoop', 'Decompress' {
|
||||
$test2 = "$working_dir\7ZipTest2.tgz"
|
||||
$test3 = "$working_dir\7ZipTest3.tar.bz2"
|
||||
$test4 = "$working_dir\7ZipTest4.tar.gz"
|
||||
$test5_1 = "$working_dir\7ZipTest5.7z.001"
|
||||
$test5_2 = "$working_dir\7ZipTest5.7z.002"
|
||||
$test5_3 = "$working_dir\7ZipTest5.7z.003"
|
||||
$test6_1 = "$working_dir\7ZipTest6.part01.rar"
|
||||
$test6_2 = "$working_dir\7ZipTest6.part02.rar"
|
||||
$test6_3 = "$working_dir\7ZipTest6.part03.rar"
|
||||
}
|
||||
|
||||
It 'extract normal compressed file' -Skip:$isUnix {
|
||||
It 'extract normal compressed file' {
|
||||
$to = test_extract 'Expand-7zipArchive' $test1
|
||||
$to | Should -Exist
|
||||
"$to\empty" | Should -Exist
|
||||
(Get-ChildItem $to).Count | Should -Be 1
|
||||
}
|
||||
|
||||
It 'extract nested compressed file' -Skip:$isUnix {
|
||||
It 'extract nested compressed file' {
|
||||
# file ext: tgz
|
||||
$to = test_extract 'Expand-7zipArchive' $test2
|
||||
$to | Should -Exist
|
||||
@@ -63,17 +73,45 @@ 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 'works with "-Removal" switch ($removal param)' -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, ...)' {
|
||||
$to = test_extract 'Expand-7zipArchive' $test6_1
|
||||
$to | Should -Exist
|
||||
"$to\dummy" | Should -Exist
|
||||
(Get-ChildItem $to).Count | Should -Be 1
|
||||
}
|
||||
|
||||
It 'works with "-Removal" switch ($removal param)' {
|
||||
$test1 | Should -Exist
|
||||
test_extract 'Expand-7zipArchive' $test1 $true
|
||||
$test1 | Should -Not -Exist
|
||||
$test5_1 | Should -Exist
|
||||
$test5_2 | Should -Exist
|
||||
$test5_3 | Should -Exist
|
||||
test_extract 'Expand-7zipArchive' $test5_1 $true
|
||||
$test5_1 | Should -Not -Exist
|
||||
$test5_2 | Should -Not -Exist
|
||||
$test5_3 | Should -Not -Exist
|
||||
$test6_1 | Should -Exist
|
||||
$test6_2 | Should -Exist
|
||||
$test6_3 | Should -Exist
|
||||
test_extract 'Expand-7zipArchive' $test6_1 $true
|
||||
$test6_1 | Should -Not -Exist
|
||||
$test6_2 | Should -Not -Exist
|
||||
$test6_3 | Should -Not -Exist
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,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
|
||||
@@ -124,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
|
||||
@@ -132,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
|
||||
@@ -157,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
|
||||
@@ -177,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,7 +1,11 @@
|
||||
. "$PSScriptRoot\Scoop-TestLib.ps1"
|
||||
. "$PSScriptRoot\..\lib\depends.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' {
|
||||
@@ -30,7 +34,7 @@ Describe 'Package Dependencies' -Tag 'Scoop' {
|
||||
|
||||
Context 'InstallationHelper function' {
|
||||
BeforeAll {
|
||||
$working_dir = setup_working 'format/formated'
|
||||
$working_dir = setup_working 'format/formatted'
|
||||
$manifest1 = parse_json (Join-Path $working_dir '3-array-with-single-and-multi.json')
|
||||
$manifest2 = parse_json (Join-Path $working_dir '4-script-block.json')
|
||||
Mock Test-HelperInstalled { $false }
|
||||
@@ -44,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')
|
||||
@@ -66,31 +70,31 @@ Describe 'Package Dependencies' -Tag 'Scoop' {
|
||||
Context 'Dependencies resolution' {
|
||||
BeforeAll {
|
||||
Mock Test-HelperInstalled { $false }
|
||||
Mock get_config { $true } -ParameterFilter { $name -eq 'MSIEXTRACT_USE_LESSMSI' }
|
||||
Mock Find-Manifest { $null, @{}, $null, $null } -ParameterFilter { $AppName -eq 'lessmsi' }
|
||||
Mock Find-Manifest { $null, @{ url = 'test.msi' }, $null, $null } -ParameterFilter { $AppName -eq '7zip' }
|
||||
Mock Find-Manifest { $null, @{}, $null, $null } -ParameterFilter { $AppName -eq 'innounp' }
|
||||
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' }
|
||||
}
|
||||
|
||||
It 'Resolve install dependencies' {
|
||||
Mock Find-Manifest { $null, @{ url = 'test.7z' }, $null, $null }
|
||||
Mock Get-Manifest { 'test', @{ url = 'test.7z' }, $null, $null }
|
||||
Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('lessmsi', '7zip', 'test')
|
||||
Mock Find-Manifest { $null, @{ innosetup = $true }, $null, $null }
|
||||
Mock Get-Manifest { 'test', @{ innosetup = $true }, $null, $null }
|
||||
Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('innounp', 'test')
|
||||
}
|
||||
It 'Resolve script dependencies' {
|
||||
Mock Find-Manifest { $null, @{ pre_install = 'Expand-7zipArchive ' }, $null, $null }
|
||||
Mock Get-Manifest { 'test', @{ pre_install = 'Expand-7zipArchive ' }, $null, $null }
|
||||
Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('lessmsi', '7zip', 'test')
|
||||
}
|
||||
It 'Resolve runtime dependencies' {
|
||||
Mock Find-Manifest { $null, @{}, $null, $null } -ParameterFilter { $AppName -eq 'depends' }
|
||||
Mock Find-Manifest { $null, @{ depends = 'depends' }, $null, $null }
|
||||
Mock Get-Manifest { 'depends', @{}, $null, $null } -ParameterFilter { $app -eq 'depends' }
|
||||
Mock Get-Manifest { 'test', @{ depends = 'depends' }, $null, $null }
|
||||
Get-Dependency -AppName 'test' -Architecture '32bit' | Should -Be @('depends', 'test')
|
||||
}
|
||||
It 'Keep bucket name of app' {
|
||||
Mock Find-Manifest { $null, @{}, $null, $null } -ParameterFilter { $AppName -eq 'bucket/depends' }
|
||||
Mock Find-Manifest { $null, @{ depends = 'bucket/depends' }, $null, $null }
|
||||
Get-Dependency -AppName 'anotherbucket/test' -Architecture '32bit' | Should -Be @('bucket/depends', 'anotherbucket/test')
|
||||
Mock Get-Manifest { 'depends', @{}, 'anotherbucket', $null } -ParameterFilter { $app -eq 'anotherbucket/depends' }
|
||||
Mock Get-Manifest { 'test', @{ depends = 'anotherbucket/depends' }, 'bucket', $null }
|
||||
Get-Dependency -AppName 'bucket/test' -Architecture '32bit' | Should -Be @('anotherbucket/depends', 'bucket/test')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
. "$PSScriptRoot\Scoop-TestLib.ps1"
|
||||
. "$PSScriptRoot\..\lib\json.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
|
||||
Describe 'Pretty json formating' -Tag 'Scoop' {
|
||||
BeforeAll {
|
||||
$format = "$PSScriptRoot\fixtures\format"
|
||||
$manifests = Get-ChildItem "$format\formated" -File -Filter '*.json'
|
||||
}
|
||||
|
||||
Context 'Beautify manifest' {
|
||||
$manifests | ForEach-Object {
|
||||
if ($PSVersionTable.PSVersion.Major -gt 5) { $_ = $_.Name } # Fix for pwsh
|
||||
|
||||
It "$_" {
|
||||
$pretty_json = (parse_json "$format\unformated\$_") | ConvertToPrettyJson
|
||||
$correct = (Get-Content "$format\formated\$_") -join "`r`n"
|
||||
$correct.CompareTo($pretty_json) | Should -Be 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
. "$PSScriptRoot\Scoop-TestLib.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
BeforeAll {
|
||||
. "$PSScriptRoot\Scoop-TestLib.ps1"
|
||||
. "$PSScriptRoot\..\lib\getopt.ps1"
|
||||
}
|
||||
|
||||
Describe 'getopt' -Tag 'Scoop' {
|
||||
It 'handle short option with required argument missing' {
|
||||
@@ -68,4 +70,18 @@ Describe 'getopt' -Tag 'Scoop' {
|
||||
$err | Should -BeNullOrEmpty
|
||||
$opt.'long-arg' | Should -Be 'test'
|
||||
}
|
||||
|
||||
It 'handles the option terminator' {
|
||||
$opt, $rem, $err = getopt '--long-arg', '--' '' 'long-arg'
|
||||
$err | Should -BeNullOrEmpty
|
||||
$opt.'long-arg' | Should -BeTrue
|
||||
$rem[0] | Should -BeNullOrEmpty
|
||||
$opt, $rem, $err = getopt '--long-arg', '--', '-x', '-y' 'xy' 'long-arg'
|
||||
$err | Should -BeNullOrEmpty
|
||||
$opt.'long-arg' | Should -BeTrue
|
||||
$opt.'x' | Should -BeNullOrEmpty
|
||||
$opt.'y' | Should -BeNullOrEmpty
|
||||
$rem[0] | Should -Be '-x'
|
||||
$rem[1] | Should -Be '-y'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,8 @@
|
||||
. "$PSScriptRoot\..\lib\core.ps1"
|
||||
. "$PSScriptRoot\..\lib\manifest.ps1"
|
||||
. "$PSScriptRoot\..\lib\install.ps1"
|
||||
. "$PSScriptRoot\..\lib\unix.ps1"
|
||||
. "$PSScriptRoot\Scoop-TestLib.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
|
||||
|
||||
BIN
test/fixtures/decompress/TestCases.zip
vendored
BIN
test/fixtures/decompress/TestCases.zip
vendored
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user